Fork of Semtech LoRaWAN stack
Fork of LoRaWAN-lib by
Revision 38:182ba91524e4, committed 2017-08-09
- Comitter:
- Shaun Nelson
- Date:
- Wed Aug 09 16:20:21 2017 -0400
- Branch:
- class_b
- Parent:
- 37:a592e8eeaa1e
- Child:
- 39:ca51084123b8
- Commit message:
- Synchronized with https://github.com/Senetco/LoRaMac-node/commit/6cb21de99eaa65caad9d911318df8875867a6e60
Changed in this revision
--- a/LoRaMac-definitions.h Tue Aug 08 19:27:17 2017 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,608 +0,0 @@
-/*
- / _____) _ | |
-( (____ _____ ____ _| |_ _____ ____| |__
- \____ \| ___ | (_ _) ___ |/ ___) _ \
- _____) ) ____| | | || |_| ____( (___| | | |
-(______/|_____)_|_|_| \__)_____)\____)_| |_|
- (C)2013 Semtech
-
-Description: LoRa MAC layer global definitions
-
-License: Revised BSD License, see LICENSE.TXT file include in the project
-
-Maintainer: Miguel Luis and Gregory Cristian
-*/
-#ifndef __LORAMAC_BOARD_H__
-#define __LORAMAC_BOARD_H__
-
-/*!
- * Returns individual channel mask
- *
- * \param[IN] channelIndex Channel index 1 based
- * \retval channelMask
- */
-#define LC( channelIndex ) ( uint16_t )( 1 << ( channelIndex - 1 ) )
-
-#if defined( USE_BAND_433 )
-
-/*!
- * LoRaMac maximum number of channels
- */
-#define LORA_MAX_NB_CHANNELS 16
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_TX_MIN_DATARATE DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_TX_MAX_DATARATE DR_7
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_RX_MIN_DATARATE DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_RX_MAX_DATARATE DR_7
-
-/*!
- * Default datarate used by the node
- */
-#define LORAMAC_DEFAULT_DATARATE DR_0
-
-/*!
- * Minimal Rx1 receive datarate offset
- */
-#define LORAMAC_MIN_RX1_DR_OFFSET 0
-
-/*!
- * Maximal Rx1 receive datarate offset
- */
-#define LORAMAC_MAX_RX1_DR_OFFSET 5
-
-/*!
- * Minimal Tx output power that can be used by the node
- */
-#define LORAMAC_MIN_TX_POWER TX_POWER_M5_DBM
-
-/*!
- * Maximal Tx output power that can be used by the node
- */
-#define LORAMAC_MAX_TX_POWER TX_POWER_10_DBM
-
-/*!
- * Default Tx output power used by the node
- */
-#define LORAMAC_DEFAULT_TX_POWER TX_POWER_10_DBM
-
-/*!
- * LoRaMac TxPower definition
- */
-#define TX_POWER_10_DBM 0
-#define TX_POWER_07_DBM 1
-#define TX_POWER_04_DBM 2
-#define TX_POWER_01_DBM 3
-#define TX_POWER_M2_DBM 4
-#define TX_POWER_M5_DBM 5
-
-/*!
- * LoRaMac datarates definition
- */
-#define DR_0 0 // SF12 - BW125
-#define DR_1 1 // SF11 - BW125
-#define DR_2 2 // SF10 - BW125
-#define DR_3 3 // SF9 - BW125
-#define DR_4 4 // SF8 - BW125
-#define DR_5 5 // SF7 - BW125
-#define DR_6 6 // SF7 - BW250
-#define DR_7 7 // FSK
-
-/*!
- * Verification of default datarate
- */
-#if ( LORAMAC_DEFAULT_DATARATE > DR_5 )
-#error "A default DR higher than DR_5 may lead to connectivity loss."
-#endif
-
-/*!
- * Second reception window channel definition.
- */
-// Channel = { Frequency [Hz], Datarate }
-#define RX_WND_2_CHANNEL { 434665000, DR_0 }
-
-/*!
- * LoRaMac maximum number of bands
- */
-#define LORA_MAX_NB_BANDS 1
-
-// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
-#define BAND0 { 100, TX_POWER_10_DBM, 0, 0 } // 1.0 %
-
-/*!
- * LoRaMac default channels
- */
-// Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
-#define LC1 { 433175000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-#define LC2 { 433375000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-#define LC3 { 433575000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-
-/*!
- * LoRaMac channels which are allowed for the join procedure
- */
-#define JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
-
-#elif defined( USE_BAND_470 )
-
-/*!
- * LoRaMac maximum number of channels
- */
-#define LORA_MAX_NB_CHANNELS 96
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_TX_MIN_DATARATE DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_TX_MAX_DATARATE DR_5
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_RX_MIN_DATARATE DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_RX_MAX_DATARATE DR_5
-
-/*!
- * Default datarate used by the node
- */
-#define LORAMAC_DEFAULT_DATARATE DR_0
-
-/*!
- * Minimal Rx1 receive datarate offset
- */
-#define LORAMAC_MIN_RX1_DR_OFFSET 0
-
-/*!
- * Maximal Rx1 receive datarate offset
- */
-#define LORAMAC_MAX_RX1_DR_OFFSET 3
-
-/*!
- * Minimal Tx output power that can be used by the node
- */
-#define LORAMAC_MIN_TX_POWER TX_POWER_2_DBM
-
-/*!
- * Maximal Tx output power that can be used by the node
- */
-#define LORAMAC_MAX_TX_POWER TX_POWER_17_DBM
-
-/*!
- * Default Tx output power used by the node
- */
-#define LORAMAC_DEFAULT_TX_POWER TX_POWER_14_DBM
-
-/*!
- * LoRaMac TxPower definition
- */
-#define TX_POWER_17_DBM 0
-#define TX_POWER_16_DBM 1
-#define TX_POWER_14_DBM 2
-#define TX_POWER_12_DBM 3
-#define TX_POWER_10_DBM 4
-#define TX_POWER_7_DBM 5
-#define TX_POWER_5_DBM 6
-#define TX_POWER_2_DBM 7
-
-
-/*!
- * LoRaMac datarates definition
- */
-#define DR_0 0 // SF12 - BW125 |
-#define DR_1 1 // SF11 - BW125 |
-#define DR_2 2 // SF10 - BW125 |
-#define DR_3 3 // SF9 - BW125 |
-#define DR_4 4 // SF8 - BW125 |
-#define DR_5 5 // SF7 - BW125 |
-
-/*!
- * Second reception window channel definition.
- */
-// Channel = { Frequency [Hz], Datarate }
-#define RX_WND_2_CHANNEL { 505300000, DR_0 }
-
-/*!
- * LoRaMac maximum number of bands
- */
-#define LORA_MAX_NB_BANDS 1
-
-// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
-#define BAND0 { 1, TX_POWER_17_DBM, 0, 0 } // 100.0 %
-
-#elif defined( USE_BAND_780 )
-
-/*!
- * LoRaMac maximum number of channels
- */
-#define LORA_MAX_NB_CHANNELS 16
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_TX_MIN_DATARATE DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_TX_MAX_DATARATE DR_7
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_RX_MIN_DATARATE DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_RX_MAX_DATARATE DR_7
-
-/*!
- * Default datarate used by the node
- */
-#define LORAMAC_DEFAULT_DATARATE DR_0
-
-/*!
- * Minimal Rx1 receive datarate offset
- */
-#define LORAMAC_MIN_RX1_DR_OFFSET 0
-
-/*!
- * Maximal Rx1 receive datarate offset
- */
-#define LORAMAC_MAX_RX1_DR_OFFSET 5
-
-/*!
- * Minimal Tx output power that can be used by the node
- */
-#define LORAMAC_MIN_TX_POWER TX_POWER_M5_DBM
-
-/*!
- * Maximal Tx output power that can be used by the node
- */
-#define LORAMAC_MAX_TX_POWER TX_POWER_10_DBM
-
-/*!
- * Default Tx output power used by the node
- */
-#define LORAMAC_DEFAULT_TX_POWER TX_POWER_10_DBM
-
-/*!
- * LoRaMac TxPower definition
- */
-#define TX_POWER_10_DBM 0
-#define TX_POWER_07_DBM 1
-#define TX_POWER_04_DBM 2
-#define TX_POWER_01_DBM 3
-#define TX_POWER_M2_DBM 4
-#define TX_POWER_M5_DBM 5
-
-/*!
- * LoRaMac datarates definition
- */
-#define DR_0 0 // SF12 - BW125
-#define DR_1 1 // SF11 - BW125
-#define DR_2 2 // SF10 - BW125
-#define DR_3 3 // SF9 - BW125
-#define DR_4 4 // SF8 - BW125
-#define DR_5 5 // SF7 - BW125
-#define DR_6 6 // SF7 - BW250
-#define DR_7 7 // FSK
-
-/*!
- * Verification of default datarate
- */
-#if ( LORAMAC_DEFAULT_DATARATE > DR_5 )
-#error "A default DR higher than DR_5 may lead to connectivity loss."
-#endif
-
-/*!
- * Second reception window channel definition.
- */
-// Channel = { Frequency [Hz], Datarate }
-#define RX_WND_2_CHANNEL { 786000000, DR_0 }
-
-/*!
- * LoRaMac maximum number of bands
- */
-#define LORA_MAX_NB_BANDS 1
-
-// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
-#define BAND0 { 100, TX_POWER_10_DBM, 0, 0 } // 1.0 %
-
-/*!
- * LoRaMac default channels
- */
-// Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
-#define LC1 { 779500000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-#define LC2 { 779700000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-#define LC3 { 779900000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-
-/*!
- * LoRaMac channels which are allowed for the join procedure
- */
-#define JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
-
-#elif defined( USE_BAND_868 )
-
-/*!
- * LoRaMac maximum number of channels
- */
-#define LORA_MAX_NB_CHANNELS 16
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_TX_MIN_DATARATE DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_TX_MAX_DATARATE DR_7
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_RX_MIN_DATARATE DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_RX_MAX_DATARATE DR_7
-
-/*!
- * Default datarate used by the node
- */
-#define LORAMAC_DEFAULT_DATARATE DR_0
-
-/*!
- * Minimal Rx1 receive datarate offset
- */
-#define LORAMAC_MIN_RX1_DR_OFFSET 0
-
-/*!
- * Maximal Rx1 receive datarate offset
- */
-#define LORAMAC_MAX_RX1_DR_OFFSET 5
-
-/*!
- * Minimal Tx output power that can be used by the node
- */
-#define LORAMAC_MIN_TX_POWER TX_POWER_02_DBM
-
-/*!
- * Maximal Tx output power that can be used by the node
- */
-#define LORAMAC_MAX_TX_POWER TX_POWER_20_DBM
-
-/*!
- * Default Tx output power used by the node
- */
-#define LORAMAC_DEFAULT_TX_POWER TX_POWER_14_DBM
-
-/*!
- * LoRaMac TxPower definition
- */
-#define TX_POWER_20_DBM 0
-#define TX_POWER_14_DBM 1
-#define TX_POWER_11_DBM 2
-#define TX_POWER_08_DBM 3
-#define TX_POWER_05_DBM 4
-#define TX_POWER_02_DBM 5
-
-/*!
- * LoRaMac datarates definition
- */
-#define DR_0 0 // SF12 - BW125
-#define DR_1 1 // SF11 - BW125
-#define DR_2 2 // SF10 - BW125
-#define DR_3 3 // SF9 - BW125
-#define DR_4 4 // SF8 - BW125
-#define DR_5 5 // SF7 - BW125
-#define DR_6 6 // SF7 - BW250
-#define DR_7 7 // FSK
-
-/*!
- * Verification of default datarate
- */
-#if ( LORAMAC_DEFAULT_DATARATE > DR_5 )
-#error "A default DR higher than DR_5 may lead to connectivity loss."
-#endif
-
-/*!
- * Second reception window channel definition.
- */
-// Channel = { Frequency [Hz], Datarate }
-#define RX_WND_2_CHANNEL { 869525000, DR_0 }
-
-/*!
- * LoRaMac maximum number of bands
- */
-#define LORA_MAX_NB_BANDS 5
-
-/*!
- * LoRaMac EU868 default bands
- */
-typedef enum
-{
- BAND_G1_0,
- BAND_G1_1,
- BAND_G1_2,
- BAND_G1_3,
- BAND_G1_4,
-}BandId_t;
-
-// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
-#define BAND0 { 100 , TX_POWER_14_DBM, 0, 0 } // 1.0 %
-#define BAND1 { 100 , TX_POWER_14_DBM, 0, 0 } // 1.0 %
-#define BAND2 { 1000, TX_POWER_14_DBM, 0, 0 } // 0.1 %
-#define BAND3 { 10 , TX_POWER_14_DBM, 0, 0 } // 10.0 %
-#define BAND4 { 100 , TX_POWER_14_DBM, 0, 0 } // 1.0 %
-
-/*!
- * LoRaMac default channels
- */
-// Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
-#define LC1 { 868100000, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
-#define LC2 { 868300000, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
-#define LC3 { 868500000, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
-
-/*!
- * LoRaMac channels which are allowed for the join procedure
- */
-#define JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
-
-#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-
-/*!
- * LoRaMac maximum number of channels
- */
-#define LORA_MAX_NB_CHANNELS 72
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_TX_MIN_DATARATE DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_TX_MAX_DATARATE DR_4
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_RX_MIN_DATARATE DR_8
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_RX_MAX_DATARATE DR_13
-
-/*!
- * Default datarate used by the node
- */
-#define LORAMAC_DEFAULT_DATARATE DR_0
-
-/*!
- * Minimal Rx1 receive datarate offset
- */
-#define LORAMAC_MIN_RX1_DR_OFFSET 0
-
-/*!
- * Maximal Rx1 receive datarate offset
- */
-#define LORAMAC_MAX_RX1_DR_OFFSET 3
-
-/*!
- * Minimal Tx output power that can be used by the node
- */
-#define LORAMAC_MIN_TX_POWER TX_POWER_10_DBM
-
-/*!
- * Maximal Tx output power that can be used by the node
- */
-#define LORAMAC_MAX_TX_POWER TX_POWER_30_DBM
-
-/*!
- * Default Tx output power used by the node
- */
-#define LORAMAC_DEFAULT_TX_POWER TX_POWER_20_DBM
-
-/*!
- * LoRaMac TxPower definition
- */
-#define TX_POWER_30_DBM 0
-#define TX_POWER_28_DBM 1
-#define TX_POWER_26_DBM 2
-#define TX_POWER_24_DBM 3
-#define TX_POWER_22_DBM 4
-#define TX_POWER_20_DBM 5
-#define TX_POWER_18_DBM 6
-#define TX_POWER_16_DBM 7
-#define TX_POWER_14_DBM 8
-#define TX_POWER_12_DBM 9
-#define TX_POWER_10_DBM 10
-
-/*!
- * LoRaMac datarates definition
- */
-#define DR_0 0 // SF10 - BW125 |
-#define DR_1 1 // SF9 - BW125 |
-#define DR_2 2 // SF8 - BW125 +-> Up link
-#define DR_3 3 // SF7 - BW125 |
-#define DR_4 4 // SF8 - BW500 |
-#define DR_5 5 // RFU
-#define DR_6 6 // RFU
-#define DR_7 7 // RFU
-#define DR_8 8 // SF12 - BW500 |
-#define DR_9 9 // SF11 - BW500 |
-#define DR_10 10 // SF10 - BW500 |
-#define DR_11 11 // SF9 - BW500 |
-#define DR_12 12 // SF8 - BW500 +-> Down link
-#define DR_13 13 // SF7 - BW500 |
-#define DR_14 14 // RFU |
-#define DR_15 15 // RFU |
-
-/*!
- * Second reception window channel definition.
- */
-// Channel = { Frequency [Hz], Datarate }
-#define RX_WND_2_CHANNEL { 923300000, DR_8 }
-
-/*!
- * LoRaMac maximum number of bands
- */
-#define LORA_MAX_NB_BANDS 1
-
-// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
-#define BAND0 { 1, TX_POWER_20_DBM, 0, 0 } // 100.0 %
-
-/*!
- * LoRaMac default channels
- */
-// Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
-/*
- * US band channels are initialized using a loop in LoRaMacInit function
- * \code
- * // 125 kHz channels
- * for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS - 8; i++ )
- * {
- * Channels[i].Frequency = 902.3e6 + i * 200e3;
- * Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0;
- * Channels[i].Band = 0;
- * }
- * // 500 kHz channels
- * for( uint8_t i = LORA_MAX_NB_CHANNELS - 8; i < LORA_MAX_NB_CHANNELS; i++ )
- * {
- * Channels[i].Frequency = 903.0e6 + ( i - ( LORA_MAX_NB_CHANNELS - 8 ) ) * 1.6e6;
- * Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4;
- * Channels[i].Band = 0;
- * }
- * \endcode
- */
-#else
- #error "Please define a frequency band in the compiler options."
-#endif
-
-#endif // __LORAMAC_BOARD_H__
--- a/LoRaMac.cpp Tue Aug 08 19:27:17 2017 -0400
+++ b/LoRaMac.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -15,15 +15,18 @@
License: Revised BSD License, see LICENSE.TXT file include in the project
-Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jäckle ( STACKFORCE )
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
*/
-#include <math.h>
#include "board.h"
+#include "LoRaMac.h"
+#include "LoRaMacClassB.h"
+#include "region/Region.h"
#include "LoRaMacCrypto.h"
-#include "LoRaMac.h"
#include "LoRaMacTest.h"
+
+
/*!
* Maximum PHY layer payload size
*/
@@ -32,14 +35,12 @@
/*!
* Maximum MAC commands buffer size
*/
-#define LORA_MAC_COMMAND_MAX_LENGTH 15
+#define LORA_MAC_COMMAND_MAX_LENGTH 128
/*!
- * FRMPayload overhead to be used when setting the Radio.SetMaxPayloadLength
- * in RxWindowSetup function.
- * Maximum PHYPayload = MaxPayloadOfDatarate/MaxPayloadOfDatarateRepeater + LORA_MAC_FRMPAYLOAD_OVERHEAD
+ * Maximum length of the fOpts field
*/
-#define LORA_MAC_FRMPAYLOAD_OVERHEAD 13 // MHDR(1) + FHDR(7) + Port(1) + MIC(4)
+#define LORA_MAC_COMMAND_MAX_FOPTS_LENGTH 15
/*!
* LoRaMac duty cycle for the back-off procedure during the first hour.
@@ -57,6 +58,11 @@
#define BACKOFF_DC_24_HOURS 10000
/*!
+ * LoRaMac region.
+ */
+static LoRaMacRegion_t LoRaMacRegion;
+
+/*!
* Device IEEE EUI
*/
static uint8_t *LoRaMacDevEui;
@@ -121,11 +127,6 @@
static bool PublicNetwork;
/*!
- * Indicates if the node supports repeaters
- */
-static bool RepeaterSupport;
-
-/*!
* Buffer containing the data to be sent or received.
*/
static uint8_t LoRaMacBuffer[LORAMAC_PHY_MAXPAYLOAD];
@@ -220,269 +221,6 @@
*/
static uint8_t MacCommandsBufferToRepeat[LORA_MAC_COMMAND_MAX_LENGTH];
-#if defined( USE_BAND_433 )
-/*!
- * Data rates table definition
- */
-const uint8_t Datarates[] = { 12, 11, 10, 9, 8, 7, 7, 50 };
-
-/*!
- * Bandwidths table definition in Hz
- */
-const uint32_t Bandwidths[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 250e3, 0 };
-
-/*!
- * Maximum payload with respect to the datarate index. Cannot operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarate[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
-
-/*!
- * Maximum payload with respect to the datarate index. Can operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarateRepeater[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
-
-/*!
- * Tx output powers table definition
- */
-const int8_t TxPowers[] = { 10, 7, 4, 1, -2, -5 };
-
-/*!
- * LoRaMac bands
- */
-static Band_t Bands[LORA_MAX_NB_BANDS] =
-{
- BAND0,
-};
-
-/*!
- * LoRaMAC channels
- */
-static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] =
-{
- LC1,
- LC2,
- LC3,
-};
-#elif defined( USE_BAND_470 )
-
-/*!
- * Data rates table definition
- */
-const uint8_t Datarates[] = { 12, 11, 10, 9, 8, 7 };
-
-/*!
- * Bandwidths table definition in Hz
- */
-const uint32_t Bandwidths[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3 };
-
-/*!
- * Maximum payload with respect to the datarate index. Cannot operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarate[] = { 51, 51, 51, 115, 222, 222 };
-
-/*!
- * Maximum payload with respect to the datarate index. Can operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarateRepeater[] = { 51, 51, 51, 115, 222, 222 };
-
-/*!
- * Tx output powers table definition
- */
-const int8_t TxPowers[] = { 17, 16, 14, 12, 10, 7, 5, 2 };
-
-/*!
- * LoRaMac bands
- */
-static Band_t Bands[LORA_MAX_NB_BANDS] =
-{
- BAND0,
-};
-
-/*!
- * LoRaMAC channels
- */
-static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS];
-
-/*!
- * Defines the first channel for RX window 1 for CN470 band
- */
-#define LORAMAC_FIRST_RX1_CHANNEL ( (uint32_t) 500.3e6 )
-
-/*!
- * Defines the last channel for RX window 1 for CN470 band
- */
-#define LORAMAC_LAST_RX1_CHANNEL ( (uint32_t) 509.7e6 )
-
-/*!
- * Defines the step width of the channels for RX window 1
- */
-#define LORAMAC_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 200e3 )
-
-#elif defined( USE_BAND_780 )
-/*!
- * Data rates table definition
- */
-const uint8_t Datarates[] = { 12, 11, 10, 9, 8, 7, 7, 50 };
-
-/*!
- * Bandwidths table definition in Hz
- */
-const uint32_t Bandwidths[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 250e3, 0 };
-
-/*!
- * Maximum payload with respect to the datarate index. Cannot operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarate[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
-
-/*!
- * Maximum payload with respect to the datarate index. Can operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarateRepeater[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
-
-/*!
- * Tx output powers table definition
- */
-const int8_t TxPowers[] = { 10, 7, 4, 1, -2, -5 };
-
-/*!
- * LoRaMac bands
- */
-static Band_t Bands[LORA_MAX_NB_BANDS] =
-{
- BAND0,
-};
-
-/*!
- * LoRaMAC channels
- */
-static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] =
-{
- LC1,
- LC2,
- LC3,
-};
-#elif defined( USE_BAND_868 )
-/*!
- * Data rates table definition
- */
-const uint8_t Datarates[] = { 12, 11, 10, 9, 8, 7, 7, 50 };
-
-/*!
- * Bandwidths table definition in Hz
- */
-const uint32_t Bandwidths[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 250e3, 0 };
-
-/*!
- * Maximum payload with respect to the datarate index. Cannot operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarate[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
-
-/*!
- * Maximum payload with respect to the datarate index. Can operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarateRepeater[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
-
-/*!
- * Tx output powers table definition
- */
-const int8_t TxPowers[] = { 20, 14, 11, 8, 5, 2 };
-
-/*!
- * LoRaMac bands
- */
-static Band_t Bands[LORA_MAX_NB_BANDS] =
-{
- BAND0,
- BAND1,
- BAND2,
- BAND3,
- BAND4,
-};
-
-/*!
- * LoRaMAC channels
- */
-static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] =
-{
- LC1,
- LC2,
- LC3,
-};
-#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-/*!
- * Data rates table definition
- */
-const uint8_t Datarates[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
-
-/*!
- * Bandwidths table definition in Hz
- */
-const uint32_t Bandwidths[] = { 125e3, 125e3, 125e3, 125e3, 500e3, 0, 0, 0, 500e3, 500e3, 500e3, 500e3, 500e3, 500e3, 0, 0 };
-
-/*!
- * Up/Down link data rates offset definition
- */
-const int8_t DatarateOffsets[5][4] =
-{
- { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0
- { DR_11, DR_10, DR_9 , DR_8 }, // DR_1
- { DR_12, DR_11, DR_10, DR_9 }, // DR_2
- { DR_13, DR_12, DR_11, DR_10 }, // DR_3
- { DR_13, DR_13, DR_12, DR_11 }, // DR_4
-};
-
-/*!
- * Maximum payload with respect to the datarate index. Cannot operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarate[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 };
-
-/*!
- * Maximum payload with respect to the datarate index. Can operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarateRepeater[] = { 11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 };
-
-/*!
- * Tx output powers table definition
- */
-const int8_t TxPowers[] = { 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10 };
-
-/*!
- * LoRaMac bands
- */
-static Band_t Bands[LORA_MAX_NB_BANDS] =
-{
- BAND0,
-};
-
-/*!
- * LoRaMAC channels
- */
-static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS];
-
-/*!
- * Contains the channels which remain to be applied.
- */
-static uint16_t ChannelsMaskRemaining[6];
-
-/*!
- * Defines the first channel for RX window 1 for US band
- */
-#define LORAMAC_FIRST_RX1_CHANNEL ( (uint32_t) 923.3e6 )
-
-/*!
- * Defines the last channel for RX window 1 for US band
- */
-#define LORAMAC_LAST_RX1_CHANNEL ( (uint32_t) 927.5e6 )
-
-/*!
- * Defines the step width of the channels for RX window 1
- */
-#define LORAMAC_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600e3 )
-
-#else
- #error "Please define a frequency band in the compiler options."
-#endif
-
/*!
* LoRaMac parameters
*/
@@ -522,6 +260,16 @@
static uint8_t Channel;
/*!
+ * Current channel index
+ */
+static uint8_t LastTxChannel;
+
+/*!
+ * Set to true, if the last uplink was a join request
+ */
+static bool LastTxIsJoinRequest;
+
+/*!
* Stores the time at LoRaMac initialization.
*
* \remark Used for the BACKOFF_DC computation.
@@ -588,20 +336,10 @@
static uint32_t RxWindow2Delay;
/*!
- * Rx window parameters
+ * LoRaMac Rx windows configuration
*/
-typedef struct
-{
- int8_t Datarate;
- uint8_t Bandwidth;
- uint32_t RxWindowTimeout;
- int32_t RxOffset;
-}RxConfigParams_t;
-
-/*!
- * Rx windows params
- */
-static RxConfigParams_t RxWindowsParams[2];
+static RxConfigParams_t RxWindow1Config;
+static RxConfigParams_t RxWindow2Config;
/*!
* Acknowledge timeout timer. Used for packet retransmissions.
@@ -649,11 +387,26 @@
static McpsConfirm_t McpsConfirm;
/*!
+ * Structure to hold MLME indication data.
+ */
+static MlmeIndication_t MlmeIndication;
+
+/*!
* Structure to hold MLME confirm data.
*/
static MlmeConfirm_t MlmeConfirm;
/*!
+ * MlmeConfirm queue data structure
+ */
+static MlmeConfirmQueue_t MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN];
+
+/*!
+ * Counts the number of MlmeConfirms to process
+ */
+static uint8_t MlmeConfirmQueueCnt;
+
+/*!
* Holds the current rx window slot
*/
static uint8_t RxSlot = 0;
@@ -720,36 +473,19 @@
static void OnAckTimeoutTimerEvent( void );
/*!
- * \brief Searches and set the next random available channel
- *
- * \param [OUT] Time to wait for the next transmission according to the duty
- * cycle.
- *
- * \retval status Function status [1: OK, 0: Unable to find a channel on the
- * current datarate]
- */
-static bool SetNextChannel( TimerTime_t* time );
-
-/*!
* \brief Initializes and opens the reception window
*
- * \param [IN] freq window channel frequency
- * \param [IN] datarate window channel datarate
- * \param [IN] bandwidth window channel bandwidth
- * \param [IN] timeout window channel timeout
- *
- * \retval status Operation status [true: Success, false: Fail]
+ * \param [IN] rxContinuous Set to true, if the RX is in continuous mode
+ * \param [IN] maxRxWindow Maximum RX window timeout
*/
-static bool RxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous );
+static void RxWindowSetup( bool rxContinuous, uint32_t maxRxWindow );
/*!
- * \brief Verifies if the RX window 2 frequency is in range
- *
- * \param [IN] freq window channel frequency
+ * \brief Switches the device class
*
- * \retval status Function status [1: OK, 0: Frequency not applicable]
+ * \param [IN] deviceClass Device class to switch to
*/
-static bool Rx2FreqInRange( uint32_t freq );
+static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass );
/*!
* \brief Adds a new MAC command to be sent.
@@ -803,105 +539,6 @@
static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen );
/*!
- * \brief Counts the number of bits in a mask.
- *
- * \param [IN] mask A mask from which the function counts the active bits.
- * \param [IN] nbBits The number of bits to check.
- *
- * \retval Number of enabled bits in the mask.
- */
-static uint8_t CountBits( uint16_t mask, uint8_t nbBits );
-
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-/*!
- * \brief Counts the number of enabled 125 kHz channels in the channel mask.
- * This function can only be applied to US915 band.
- *
- * \param [IN] channelsMask Pointer to the first element of the channel mask
- *
- * \retval Number of enabled channels in the channel mask
- */
-static uint8_t CountNbEnabled125kHzChannels( uint16_t *channelsMask );
-
-#if defined( USE_BAND_915_HYBRID )
-/*!
- * \brief Validates the correctness of the channel mask for US915, hybrid mode.
- *
- * \param [IN] mask Block definition to set.
- * \param [OUT] channelsMask Pointer to the first element of the channel mask
- */
-static void ReenableChannels( uint16_t mask, uint16_t* channelsMask );
-
-/*!
- * \brief Validates the correctness of the channel mask for US915, hybrid mode.
- *
- * \param [IN] channelsMask Pointer to the first element of the channel mask
- *
- * \retval [true: channel mask correct, false: channel mask not correct]
- */
-static bool ValidateChannelMask( uint16_t* channelsMask );
-#endif
-
-#endif
-
-/*!
- * \brief Validates the correctness of the datarate against the enable channels.
- *
- * \param [IN] datarate Datarate to be check
- * \param [IN] channelsMask Pointer to the first element of the channel mask
- *
- * \retval [true: datarate can be used, false: datarate can not be used]
- */
-static bool ValidateDatarate( int8_t datarate, uint16_t* channelsMask );
-
-/*!
- * \brief Limits the Tx power according to the number of enabled channels
- *
- * \param [IN] txPower txPower to limit
- * \param [IN] maxBandTxPower Maximum band allowed TxPower
- *
- * \retval Returns the maximum valid tx power
- */
-static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower );
-
-/*!
- * \brief Verifies, if a value is in a given range.
- *
- * \param value Value to verify, if it is in range
- *
- * \param min Minimum possible value
- *
- * \param max Maximum possible value
- *
- * \retval Returns the maximum valid tx power
- */
-static bool ValueInRange( int8_t value, int8_t min, int8_t max );
-
-/*!
- * \brief Calculates the next datarate to set, when ADR is on or off
- *
- * \param [IN] adrEnabled Specify whether ADR is on or off
- *
- * \param [IN] updateChannelMask Set to true, if the channel masks shall be updated
- *
- * \param [OUT] datarateOut Reports the datarate which will be used next
- *
- * \retval Returns the state of ADR ack request
- */
-static bool AdrNextDr( bool adrEnabled, bool updateChannelMask, int8_t* datarateOut );
-
-/*!
- * \brief Disables channel in a specified channel mask
- *
- * \param [IN] id - Id of the channel
- *
- * \param [IN] mask - Pointer to the channel mask to edit
- *
- * \retval [true, if disable was successful, false if not]
- */
-static bool DisableChannelInMask( uint8_t id, uint16_t* mask );
-
-/*!
* \brief Decodes MAC commands in the fOpts field and in the payload
*/
static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr );
@@ -938,13 +575,6 @@
static LoRaMacStatus_t ScheduleTx( void );
/*
- * \brief Sets the duty cycle for the join procedure.
- *
- * \retval Duty cycle
- */
-static uint16_t JoinDutyCycle( void );
-
-/*
* \brief Calculates the back-off time for the band of a channel.
*
* \param [IN] channel The last Tx channel index
@@ -952,12 +582,21 @@
static void CalculateBackOff( uint8_t channel );
/*
- * \brief Alternates the datarate of the channel for the join request.
+ * \brief Gets the index of the confirm queue of a specific MLME-request
*
- * \param [IN] nbTrials Number of performed join requests.
- * \retval Datarate to apply
+ * \param [IN] queue MLME-Confirm queue pointer
+ * \param [IN] req MLME-Request to validate
+ * \retval Index of the MLME-Confirm. 0xFF is no entry found.
*/
-static int8_t AlternateDatarate( uint16_t nbTrials );
+static uint8_t GetMlmeConfirmIndex( MlmeConfirmQueue_t* queue, Mlme_t req );
+
+/*
+ * \brief Sets the status of all MLME requests in the queue to the provided status
+ *
+ * \param [IN] queue MLME-Confirm queue pointer
+ * \param [IN] status MLME-Status to set
+ */
+static void SetEveryMlmeConfirmStatus( MlmeConfirmQueue_t* queue, LoRaMacEventInfoStatus_t status );
/*!
* \brief LoRaMAC layer prepared frame buffer transmission with channel specification
@@ -965,10 +604,10 @@
* \remark PrepareFrame must be called at least once before calling this
* function.
*
- * \param [IN] channel Channel parameters
- * \retval status Status of the operation.
+ * \param [IN] channel Channel to transmit on
+ * \retval Time delay Returns a time which shall delay the next TX.
*/
-LoRaMacStatus_t SendFrameOnChannel( ChannelParams_t channel );
+TimerTime_t SendFrameOnChannel( uint8_t channel );
/*!
* \brief Sets the radio in continuous transmission mode
@@ -987,7 +626,7 @@
*
* \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode
* \param [IN] frequency RF frequency to be set.
- * \param [IN] power RF ouptput power to be set.
+ * \param [IN] power RF output power to be set.
* \retval status Status of the operation.
*/
LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power );
@@ -997,58 +636,11 @@
*/
static void ResetMacParameters( void );
-/*
- * Rx window precise timing
- *
- * For more details please consult the following document, chapter 3.1.2.
- * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf
- * or
- * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf
- *
- * Downlink start: T = Tx + 1s (+/- 20 us)
- * |
- * TRxEarly | TRxLate
- * | | |
- * | | +---+---+---+---+---+---+---+---+
- * | | | Latest Rx window |
- * | | +---+---+---+---+---+---+---+---+
- * | | |
- * +---+---+---+---+---+---+---+---+
- * | Earliest Rx window |
- * +---+---+---+---+---+---+---+---+
- * |
- * +---+---+---+---+---+---+---+---+
- *Downlink preamble 8 symbols | | | | | | | | |
- * +---+---+---+---+---+---+---+---+
- *
- * Worst case Rx window timings
- *
- * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME
- * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME
- *
- * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
- *
- * RxOffset = ( TRxLate + TRxEarly ) / 2
- *
- * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
- * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME
- *
- * Minimal value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol
- */
-/*!
- * Computes the Rx window parameters.
- *
- * \param [IN] datarate Rx window datarate to be used
- * \param [IN] rxError Maximum timing error of the receiver. in milliseconds
- * The receiver will turn on in a [-rxError : +rxError] ms
- * interval around RxOffset
- *
- * \retval rxConfigParams Returns a RxConfigParams_t structure.
- */
-static RxConfigParams_t ComputeRxWindowParameters( int8_t datarate, uint32_t rxError );
-
static void OnRadioTxDone( void )
{
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ SetBandTxDoneParams_t txDone;
TimerTime_t curTime = TimerGetCurrentTime( );
if( LoRaMacDeviceClass != CLASS_C )
@@ -1072,15 +664,16 @@
}
if( ( LoRaMacDeviceClass == CLASS_C ) || ( NodeAckRequested == true ) )
{
- TimerSetValue( &AckTimeoutTimer, RxWindow2Delay + ACK_TIMEOUT +
- randr( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND ) );
+ getPhy.Attribute = PHY_ACK_TIMEOUT;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ TimerSetValue( &AckTimeoutTimer, RxWindow2Delay + phyParam.Value );
TimerStart( &AckTimeoutTimer );
}
}
else
{
McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
- MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT;
+ SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT );
if( LoRaMacFlags.Value == 0 )
{
@@ -1089,12 +682,25 @@
LoRaMacFlags.Bits.MacDone = 1;
}
+ // Verify if the last uplink was a join request
+ if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) )
+ {
+ LastTxIsJoinRequest = true;
+ }
+ else
+ {
+ LastTxIsJoinRequest = false;
+ }
+
+ // Store last Tx channel
+ LastTxChannel = Channel;
// Update last tx done time for the current channel
- Bands[Channels[Channel].Band].LastTxDoneTime = curTime;
+ txDone.Channel = Channel;
+ txDone.Joined = IsLoRaMacNetworkJoined;
+ txDone.LastTxDoneTime = curTime;
+ RegionSetBandTxDone( LoRaMacRegion, &txDone );
// Update Aggregated last tx done time
AggregatedLastTxDoneTime = curTime;
- // Update Backoff
- CalculateBackOff( Channel );
if( NodeAckRequested == false )
{
@@ -1124,8 +730,13 @@
{
LoRaMacHeader_t macHdr;
LoRaMacFrameCtrl_t fCtrl;
+ ApplyCFListParams_t applyCFList;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
bool skipIndication = false;
+ uint8_t index = 0;
+
uint8_t pktHeaderLen = 0;
uint32_t address = 0;
uint8_t appPayloadStartIndex = 0;
@@ -1164,6 +775,21 @@
Radio.Sleep( );
TimerStop( &RxWindowTimer2 );
+ // This function must be called even if we are not in class b mode yet.
+ if( LoRaMacClassBRxBeacon( payload, size ) == true )
+ {
+ return;
+ }
+ // Check if we expect a ping slot.
+ if( LoRaMacDeviceClass == CLASS_B )
+ {
+ if( LoRaMacClassBIsPingExpected( ) == true )
+ {
+ LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_SET_TIMER );
+ LoRaMacClassBPingSlotTimerEvent( );
+ }
+ }
+
macHdr.Value = payload[pktHeaderLen++];
switch( macHdr.Bits.MType )
@@ -1186,72 +812,80 @@
micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 2] << 16 );
micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 3] << 24 );
- if( micRx == mic )
+ index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_JOIN );
+ if( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
{
- LoRaMacJoinComputeSKeys( LoRaMacAppKey, LoRaMacRxPayload + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey );
-
- LoRaMacNetID = ( uint32_t )LoRaMacRxPayload[4];
- LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[5] << 8 );
- LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[6] << 16 );
-
- LoRaMacDevAddr = ( uint32_t )LoRaMacRxPayload[7];
- LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[8] << 8 );
- LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[9] << 16 );
- LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[10] << 24 );
-
- // DLSettings
- LoRaMacParams.Rx1DrOffset = ( LoRaMacRxPayload[11] >> 4 ) & 0x07;
- LoRaMacParams.Rx2Channel.Datarate = LoRaMacRxPayload[11] & 0x0F;
-
- // RxDelay
- LoRaMacParams.ReceiveDelay1 = ( LoRaMacRxPayload[12] & 0x0F );
- if( LoRaMacParams.ReceiveDelay1 == 0 )
+ if( micRx == mic )
{
- LoRaMacParams.ReceiveDelay1 = 1;
+ LoRaMacJoinComputeSKeys( LoRaMacAppKey, LoRaMacRxPayload + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey );
+
+ LoRaMacNetID = ( uint32_t )LoRaMacRxPayload[4];
+ LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[5] << 8 );
+ LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[6] << 16 );
+
+ LoRaMacDevAddr = ( uint32_t )LoRaMacRxPayload[7];
+ LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[8] << 8 );
+ LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[9] << 16 );
+ LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[10] << 24 );
+
+ // DLSettings
+ LoRaMacParams.Rx1DrOffset = ( LoRaMacRxPayload[11] >> 4 ) & 0x07;
+ LoRaMacParams.Rx2Channel.Datarate = LoRaMacRxPayload[11] & 0x0F;
+
+ // RxDelay
+ LoRaMacParams.ReceiveDelay1 = ( LoRaMacRxPayload[12] & 0x0F );
+ if( LoRaMacParams.ReceiveDelay1 == 0 )
+ {
+ LoRaMacParams.ReceiveDelay1 = 1;
+ }
+ LoRaMacParams.ReceiveDelay1 *= 1000;
+ LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1000;
+
+ // Apply CF list
+ applyCFList.Payload = &LoRaMacRxPayload[13];
+ // Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC
+ applyCFList.Size = size - 17;
+
+ RegionApplyCFList( LoRaMacRegion, &applyCFList );
+
+ MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_OK;
+ IsLoRaMacNetworkJoined = true;
+ LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate;
}
- LoRaMacParams.ReceiveDelay1 *= 1e3;
- LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1e3;
-
-#if !( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
- //CFList
- if( ( size - 1 ) > 16 )
+ else
{
- ChannelParams_t param;
- param.DrRange.Value = ( DR_5 << 4 ) | DR_0;
-
- LoRaMacState |= LORAMAC_TX_CONFIG;
- for( uint8_t i = 3, j = 0; i < ( 5 + 3 ); i++, j += 3 )
- {
- param.Frequency = ( ( uint32_t )LoRaMacRxPayload[13 + j] | ( ( uint32_t )LoRaMacRxPayload[14 + j] << 8 ) | ( ( uint32_t )LoRaMacRxPayload[15 + j] << 16 ) ) * 100;
- if( param.Frequency != 0 )
- {
- LoRaMacChannelAdd( i, param );
- }
- else
- {
- LoRaMacChannelRemove( i );
- }
- }
- LoRaMacState &= ~LORAMAC_TX_CONFIG;
+ MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
}
-#endif
- MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
- IsLoRaMacNetworkJoined = true;
- LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate;
- }
- else
- {
- MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
}
break;
case FRAME_TYPE_DATA_CONFIRMED_DOWN:
case FRAME_TYPE_DATA_UNCONFIRMED_DOWN:
{
+ // Check if the received payload size is valid
+ getPhy.UplinkDwellTime = LoRaMacParams.DownlinkDwellTime;
+ getPhy.Datarate = McpsIndication.RxDatarate;
+ getPhy.Attribute = PHY_MAX_PAYLOAD;
+
+ // Get the maximum payload length
+ if( LoRaMacParams.RepeaterSupport == true )
+ {
+ getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER;
+ }
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ if( (uint32_t)MAX( (int16_t)0, ( int16_t )( ( int16_t )size - ( int16_t )LORA_MAC_FRMPAYLOAD_OVERHEAD ) ) > phyParam.Value )
+ {
+ McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+ PrepareRxDoneAbort( );
+ return;
+ }
+
address = payload[pktHeaderLen++];
address |= ( (uint32_t)payload[pktHeaderLen++] << 8 );
address |= ( (uint32_t)payload[pktHeaderLen++] << 16 );
address |= ( (uint32_t)payload[pktHeaderLen++] << 24 );
+ fCtrl.Value = payload[pktHeaderLen++];
+
if( address != LoRaMacDevAddr )
{
curMulticastParams = MulticastChannels;
@@ -1274,6 +908,15 @@
PrepareRxDoneAbort( );
return;
}
+ if( ( macHdr.Bits.MType != FRAME_TYPE_DATA_UNCONFIRMED_DOWN ) ||
+ ( fCtrl.Bits.Ack == 1 ) ||
+ ( fCtrl.Bits.AdrAckReq == 1 ) )
+ {
+ // Wrong multicast message format. Refer to chapter 11.2.2 of the specification
+ McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL;
+ PrepareRxDoneAbort( );
+ return;
+ }
}
else
{
@@ -1283,8 +926,6 @@
downLinkCounter = DownLinkCounter;
}
- fCtrl.Value = payload[pktHeaderLen++];
-
sequenceCounter = ( uint16_t )payload[pktHeaderLen++];
sequenceCounter |= ( uint16_t )payload[pktHeaderLen++] << 8;
@@ -1320,7 +961,9 @@
}
// Check for a the maximum allowed counter difference
- if( sequenceCounterDiff >= MAX_FCNT_GAP )
+ getPhy.Attribute = PHY_MAX_FCNT_GAP;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ if( sequenceCounterDiff >= phyParam.Value )
{
McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS;
McpsIndication.DownLinkCounter = downLinkCounter;
@@ -1394,7 +1037,7 @@
// This must be done before parsing the payload and the MAC commands.
// We need to reset the MacCommandsBufferIndex here, since we need
- // to take retransmissions and repititions into account. Error cases
+ // to take retransmissions and repetitions into account. Error cases
// will be handled in function OnMacStateCheckTimerEvent.
if( McpsConfirm.McpsRequest == MCPS_CONFIRMED )
{
@@ -1418,8 +1061,7 @@
if( port == 0 )
{
- // Only allow frames which do not have fOpts
- if( fCtrl.Bits.FOptsLen == 0 )
+ if( ( fCtrl.Bits.FOptsLen == 0 ) && ( multicast == 0 ) )
{
LoRaMacPayloadDecrypt( payload + appPayloadStartIndex,
frameLen,
@@ -1439,7 +1081,7 @@
}
else
{
- if( fCtrl.Bits.FOptsLen > 0 )
+ if( ( fCtrl.Bits.FOptsLen > 0 ) && ( multicast == 0 ) )
{
// Decode Options field MAC commands. Omit the fPort.
ProcessMacCommands( payload, 8, appPayloadStartIndex - 1, snr );
@@ -1544,12 +1186,14 @@
}
McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
- MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
+ SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT );
LoRaMacFlags.Bits.MacDone = 1;
}
static void OnRadioRxError( void )
{
+ bool classBRx = false;
+
if( LoRaMacDeviceClass != CLASS_C )
{
Radio.Sleep( );
@@ -1559,32 +1203,52 @@
OnRxWindow2TimerEvent( );
}
- if( RxSlot == 0 )
+ if( LoRaMacDeviceClass == CLASS_B )
{
- if( NodeAckRequested == true )
+ if( LoRaMacClassBIsBeaconExpected( ) == true )
+ {
+ LoRaMacClassBSetBeaconState( BEACON_STATE_TIMEOUT );
+ LoRaMacClassBBeaconTimerEvent( );
+ classBRx = true;
+ }
+ if( LoRaMacClassBIsPingExpected( ) == true )
{
- McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR;
+ LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_SET_TIMER );
+ LoRaMacClassBPingSlotTimerEvent( );
+ classBRx = true;
}
- MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR;
-
- if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay )
+ }
+
+ if( classBRx == false )
+ {
+ if( RxSlot == 0 )
{
+ if( NodeAckRequested == true )
+ {
+ McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR;
+ }
+ SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_RX1_ERROR );
+ if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay )
+ {
+ LoRaMacFlags.Bits.MacDone = 1;
+ }
+ }
+ else
+ {
+ if( NodeAckRequested == true )
+ {
+ McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR;
+ }
+ SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_RX2_ERROR );
LoRaMacFlags.Bits.MacDone = 1;
}
}
- else
- {
- if( NodeAckRequested == true )
- {
- McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR;
- }
- MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR;
- LoRaMacFlags.Bits.MacDone = 1;
- }
}
static void OnRadioRxTimeout( void )
{
+ bool classBRx = false;
+
if( LoRaMacDeviceClass != CLASS_C )
{
Radio.Sleep( );
@@ -1594,21 +1258,64 @@
OnRxWindow2TimerEvent( );
}
- if( RxSlot == 1 )
+ if( LoRaMacDeviceClass == CLASS_B )
{
- if( NodeAckRequested == true )
+ if( LoRaMacClassBIsBeaconExpected( ) == true )
+ {
+ LoRaMacClassBSetBeaconState( BEACON_STATE_TIMEOUT );
+ LoRaMacClassBBeaconTimerEvent( );
+ classBRx = true;
+ }
+
+ if( LoRaMacClassBIsPingExpected( ) == true )
+ {
+ LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_SET_TIMER );
+ LoRaMacClassBPingSlotTimerEvent( );
+ classBRx = true;
+ }
+ }
+
+ if( classBRx == false )
+ {
+ if( RxSlot == 0 )
{
- McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT;
+ if( NodeAckRequested == true )
+ {
+ McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT;
+ }
+ SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT );
+
+ if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay )
+ {
+ LoRaMacFlags.Bits.MacDone = 1;
+ }
}
- MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT;
- LoRaMacFlags.Bits.MacDone = 1;
+ else
+ {
+ if( NodeAckRequested == true )
+ {
+ McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT;
+ }
+ SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT );
+
+ if( LoRaMacDeviceClass != CLASS_C )
+ {
+ LoRaMacFlags.Bits.MacDone = 1;
+ }
+ }
}
}
static void OnMacStateCheckTimerEvent( void )
{
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ uint8_t index = 0;
+ bool noTx = false;
+ uint8_t i, j = 0;
+
+
TimerStop( &MacStateCheckTimer );
- bool txTimeout = false;
if( LoRaMacFlags.Bits.MacDone == 1 )
{
@@ -1620,6 +1327,9 @@
if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) || ( ( LoRaMacFlags.Bits.McpsReq == 1 ) ) )
{
+ // Get a status of any request and check if we have a TX timeout
+ MlmeConfirm.Status = MlmeConfirmQueue[0].Status;
+
if( ( McpsConfirm.Status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) ||
( MlmeConfirm.Status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) )
{
@@ -1629,19 +1339,30 @@
McpsConfirm.NbRetries = AckTimeoutRetriesCounter;
McpsConfirm.AckReceived = false;
McpsConfirm.TxTimeOnAir = 0;
- txTimeout = true;
+ noTx = true;
+ }
+
+ index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_BEACON_ACQUISITION );
+ if( ( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN ) && ( LoRaMacFlags.Bits.McpsReq == 0 ) )
+ {
+ if( LoRaMacFlags.Bits.MlmeReq == 1 )
+ {
+ noTx = true;
+ LoRaMacState &= ~LORAMAC_TX_RUNNING;
+ }
}
}
- if( ( NodeAckRequested == false ) && ( txTimeout == false ) )
+ if( ( NodeAckRequested == false ) && ( noTx == false ) )
{
if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) || ( ( LoRaMacFlags.Bits.McpsReq == 1 ) ) )
{
- if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) )
+ index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_JOIN );
+ if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN ) )
{// Procedure for the join request
MlmeConfirm.NbRetries = JoinRequestTrials;
- if( MlmeConfirm.Status == LORAMAC_EVENT_INFO_STATUS_OK )
+ if( MlmeConfirmQueue[index].Status == LORAMAC_EVENT_INFO_STATUS_OK )
{// Node joined successfully
UpLinkCounter = 0;
ChannelsNbRepCounter = 0;
@@ -1666,7 +1387,7 @@
if( ( ChannelsNbRepCounter >= LoRaMacParams.ChannelsNbRep ) || ( LoRaMacFlags.Bits.McpsInd == 1 ) )
{
if( LoRaMacFlags.Bits.McpsInd == 0 )
- { // Maximum repititions without downlink. Reset MacCommandsBufferIndex. Increase ADR Ack counter.
+ { // Maximum repetitions without downlink. Reset MacCommandsBufferIndex. Increase ADR Ack counter.
// Only process the case when the MAC did not receive a downlink.
MacCommandsBufferIndex = 0;
AdrAckCounter++;
@@ -1716,7 +1437,11 @@
if( ( AckTimeoutRetriesCounter % 2 ) == 1 )
{
- LoRaMacParams.ChannelsDatarate = MAX( LoRaMacParams.ChannelsDatarate - 1, LORAMAC_TX_MIN_DATARATE );
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+ getPhy.Datarate = LoRaMacParams.ChannelsDatarate;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParams.ChannelsDatarate = phyParam.Value;
}
// Try to send the frame again
if( ScheduleTx( ) == LORAMAC_STATUS_OK )
@@ -1742,21 +1467,8 @@
}
else
{
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
- // Re-enable default channels LC1, LC2, LC3
- LoRaMacParams.ChannelsMask[0] = LoRaMacParams.ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
-#elif defined( USE_BAND_470 )
- // Re-enable default channels
- memcpy1( ( uint8_t* )LoRaMacParams.ChannelsMask, ( uint8_t* )LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-#elif defined( USE_BAND_915 )
- // Re-enable default channels
- memcpy1( ( uint8_t* )LoRaMacParams.ChannelsMask, ( uint8_t* )LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-#elif defined( USE_BAND_915_HYBRID )
- // Re-enable default channels
- ReenableChannels( LoRaMacParamsDefaults.ChannelsMask[4], LoRaMacParams.ChannelsMask );
-#else
- #error "Please define a frequency band in the compiler options."
-#endif
+ RegionInitDefaults( LoRaMacRegion, INIT_TYPE_RESTORE );
+
LoRaMacState &= ~LORAMAC_TX_RUNNING;
MacCommandsBufferIndex = 0;
@@ -1785,12 +1497,44 @@
if( LoRaMacFlags.Bits.MlmeReq == 1 )
{
- LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm );
- LoRaMacFlags.Bits.MlmeReq = 0;
+ j = MlmeConfirmQueueCnt;
+ for( i = 0; i < MlmeConfirmQueueCnt; i++ )
+ {
+ if( MlmeConfirmQueue[i].MlmeRequest == MLME_BEACON_ACQUISITION )
+ {
+ if( LoRaMacClassBIsAcquisitionPending( ) == true )
+ {
+ MlmeConfirmQueue[0].MlmeRequest = MLME_BEACON_ACQUISITION;
+ MlmeConfirmQueue[0].Status = MlmeConfirmQueue[i].Status;
+ continue;
+ }
+ }
+ j--;
+ MlmeConfirm.Status = MlmeConfirmQueue[i].Status;
+ MlmeConfirm.MlmeRequest = MlmeConfirmQueue[i].MlmeRequest;
+ LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm );
+ }
+ MlmeConfirmQueueCnt = j;
+
+ if( MlmeConfirmQueueCnt == 0 )
+ {
+ LoRaMacFlags.Bits.MlmeReq = 0;
+ }
+ }
+
+ if( LoRaMacFlags.Bits.MlmeInd == 1 )
+ {
+ LoRaMacPrimitives->MacMlmeIndication( &MlmeIndication );
+ LoRaMacFlags.Bits.MlmeInd = 0;
}
// Procedure done. Reset variables.
LoRaMacFlags.Bits.MacDone = 0;
+
+ if( LoRaMacDeviceClass == CLASS_B )
+ {
+ LoRaMacClassBResumeBeaconing( );
+ }
}
else
{
@@ -1818,15 +1562,20 @@
{
LoRaMacHeader_t macHdr;
LoRaMacFrameCtrl_t fCtrl;
+ AlternateDrParams_t altDr;
+ uint8_t index = 0;
TimerStop( &TxDelayedTimer );
LoRaMacState &= ~LORAMAC_TX_DELAYED;
- if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) )
+ index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_JOIN );
+
+ if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN ) )
{
ResetMacParameters( );
- // Add a +1, since we start to count from 0
- LoRaMacParams.ChannelsDatarate = AlternateDatarate( JoinRequestTrials + 1 );
+
+ altDr.NbTrials = JoinRequestTrials + 1;
+ LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, &altDr );
macHdr.Value = 0;
macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ;
@@ -1848,35 +1597,45 @@
TimerStop( &RxWindowTimer1 );
RxSlot = 0;
+ RxWindow1Config.Channel = Channel;
+ RxWindow1Config.DrOffset = LoRaMacParams.Rx1DrOffset;
+ RxWindow1Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime;
+ RxWindow1Config.RepeaterSupport = LoRaMacParams.RepeaterSupport;
+ RxWindow1Config.RxContinuous = false;
+ RxWindow1Config.Window = RxSlot;
+
if( LoRaMacDeviceClass == CLASS_C )
{
Radio.Standby( );
}
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
- RxWindowSetup( Channels[Channel].Frequency, RxWindowsParams[0].Datarate, RxWindowsParams[0].Bandwidth, RxWindowsParams[0].RxWindowTimeout, false );
-#elif defined( USE_BAND_470 )
- RxWindowSetup( LORAMAC_FIRST_RX1_CHANNEL + ( Channel % 48 ) * LORAMAC_STEPWIDTH_RX1_CHANNEL, RxWindowsParams[0].Datarate, RxWindowsParams[0].Bandwidth, RxWindowsParams[0].RxWindowTimeout, false );
-#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
- RxWindowSetup( LORAMAC_FIRST_RX1_CHANNEL + ( Channel % 8 ) * LORAMAC_STEPWIDTH_RX1_CHANNEL, RxWindowsParams[0].Datarate, RxWindowsParams[0].Bandwidth, RxWindowsParams[0].RxWindowTimeout, false );
-#else
- #error "Please define a frequency band in the compiler options."
-#endif
+ RegionRxConfig( LoRaMacRegion, &RxWindow1Config, ( int8_t* )&McpsIndication.RxDatarate );
+ RxWindowSetup( RxWindow1Config.RxContinuous, LoRaMacParams.MaxRxWindow );
}
static void OnRxWindow2TimerEvent( void )
{
- bool rxContinuousMode = false;
-
TimerStop( &RxWindowTimer2 );
- if( LoRaMacDeviceClass == CLASS_C )
+ RxWindow2Config.Channel = Channel;
+ RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency;
+ RxWindow2Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime;
+ RxWindow2Config.RepeaterSupport = LoRaMacParams.RepeaterSupport;
+ RxWindow2Config.Window = 1;
+
+ if( LoRaMacDeviceClass != CLASS_C )
{
- rxContinuousMode = true;
+ RxWindow2Config.RxContinuous = false;
+ }
+ else
+ {
+ RxWindow2Config.RxContinuous = true;
}
- if( RxWindowSetup( LoRaMacParams.Rx2Channel.Frequency, RxWindowsParams[1].Datarate, RxWindowsParams[1].Bandwidth, RxWindowsParams[1].RxWindowTimeout, rxContinuousMode ) == true )
+
+ if( RegionRxConfig( LoRaMacRegion, &RxWindow2Config, ( int8_t* )&McpsIndication.RxDatarate ) == true )
{
- RxSlot = 1;
+ RxWindowSetup( RxWindow2Config.RxContinuous, LoRaMacParams.MaxRxWindow );
+ RxSlot = RxWindow2Config.Window;
}
}
@@ -1895,221 +1654,93 @@
}
}
-static bool SetNextChannel( TimerTime_t* time )
+static void RxWindowSetup( bool rxContinuous, uint32_t maxRxWindow )
{
- uint8_t nbEnabledChannels = 0;
- uint8_t delayTx = 0;
- uint8_t enabledChannels[LORA_MAX_NB_CHANNELS];
- TimerTime_t nextTxDelay = ( TimerTime_t )( -1 );
-
- memset1( enabledChannels, 0, LORA_MAX_NB_CHANNELS );
-
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- if( CountNbEnabled125kHzChannels( ChannelsMaskRemaining ) == 0 )
- { // Restore default channels
- memcpy1( ( uint8_t* ) ChannelsMaskRemaining, ( uint8_t* ) LoRaMacParams.ChannelsMask, 8 );
- }
- if( ( LoRaMacParams.ChannelsDatarate >= DR_4 ) && ( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 ) )
- { // Make sure, that the channels are activated
- ChannelsMaskRemaining[4] = LoRaMacParams.ChannelsMask[4];
- }
-#elif defined( USE_BAND_470 )
- if( ( CountBits( LoRaMacParams.ChannelsMask[0], 16 ) == 0 ) &&
- ( CountBits( LoRaMacParams.ChannelsMask[1], 16 ) == 0 ) &&
- ( CountBits( LoRaMacParams.ChannelsMask[2], 16 ) == 0 ) &&
- ( CountBits( LoRaMacParams.ChannelsMask[3], 16 ) == 0 ) &&
- ( CountBits( LoRaMacParams.ChannelsMask[4], 16 ) == 0 ) &&
- ( CountBits( LoRaMacParams.ChannelsMask[5], 16 ) == 0 ) )
- {
- memcpy1( ( uint8_t* )LoRaMacParams.ChannelsMask, ( uint8_t* )LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
- }
-#else
- if( CountBits( LoRaMacParams.ChannelsMask[0], 16 ) == 0 )
- {
- // Re-enable default channels, if no channel is enabled
- LoRaMacParams.ChannelsMask[0] = LoRaMacParams.ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
- }
-#endif
-
- // Update Aggregated duty cycle
- if( AggregatedTimeOff <= TimerGetElapsedTime( AggregatedLastTxDoneTime ) )
+ if( rxContinuous == false )
{
- AggregatedTimeOff = 0;
-
- // Update bands Time OFF
- for( uint8_t i = 0; i < LORA_MAX_NB_BANDS; i++ )
- {
- if( ( IsLoRaMacNetworkJoined == false ) || ( DutyCycleOn == true ) )
- {
- if( Bands[i].TimeOff <= TimerGetElapsedTime( Bands[i].LastTxDoneTime ) )
- {
- Bands[i].TimeOff = 0;
- }
- if( Bands[i].TimeOff != 0 )
- {
- nextTxDelay = MIN( Bands[i].TimeOff - TimerGetElapsedTime( Bands[i].LastTxDoneTime ), nextTxDelay );
- }
- }
- else
- {
- if( DutyCycleOn == false )
- {
- Bands[i].TimeOff = 0;
- }
- }
- }
-
- // Search how many channels are enabled
- for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS; i += 16, k++ )
- {
- for( uint8_t j = 0; j < 16; j++ )
- {
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- if( ( ChannelsMaskRemaining[k] & ( 1 << j ) ) != 0 )
-#else
- if( ( LoRaMacParams.ChannelsMask[k] & ( 1 << j ) ) != 0 )
-#endif
- {
- if( Channels[i + j].Frequency == 0 )
- { // Check if the channel is enabled
- continue;
- }
-#if defined( USE_BAND_868 ) || defined( USE_BAND_433 ) || defined( USE_BAND_780 )
- if( IsLoRaMacNetworkJoined == false )
- {
- if( ( JOIN_CHANNELS & ( 1 << j ) ) == 0 )
- {
- continue;
- }
- }
-#endif
- if( ( ( Channels[i + j].DrRange.Fields.Min <= LoRaMacParams.ChannelsDatarate ) &&
- ( LoRaMacParams.ChannelsDatarate <= Channels[i + j].DrRange.Fields.Max ) ) == false )
- { // Check if the current channel selection supports the given datarate
- continue;
- }
- if( Bands[Channels[i + j].Band].TimeOff > 0 )
- { // Check if the band is available for transmission
- delayTx++;
- continue;
- }
- enabledChannels[nbEnabledChannels++] = i + j;
- }
- }
- }
+ Radio.Rx( maxRxWindow );
}
else
{
- delayTx++;
- nextTxDelay = AggregatedTimeOff - TimerGetElapsedTime( AggregatedLastTxDoneTime );
- }
-
- if( nbEnabledChannels > 0 )
- {
- Channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- if( Channel < ( LORA_MAX_NB_CHANNELS - 8 ) )
- {
- DisableChannelInMask( Channel, ChannelsMaskRemaining );
- }
-#endif
- *time = 0;
- return true;
- }
- else
- {
- if( delayTx > 0 )
- {
- // Delay transmission due to AggregatedTimeOff or to a band time off
- *time = nextTxDelay;
- return true;
- }
- // Datarate not supported by any channel
- *time = 0;
- return false;
+ Radio.Rx( 0 ); // Continuous mode
}
}
-static bool RxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous )
+static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass )
{
- uint8_t downlinkDatarate = Datarates[datarate];
- RadioModems_t modem;
-
- if( Radio.GetStatus( ) == RF_IDLE )
+ LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
+
+ switch( LoRaMacDeviceClass )
{
- Radio.SetChannel( freq );
-
- // Store downlink datarate
- McpsIndication.RxDatarate = ( uint8_t ) datarate;
-
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
- if( datarate == DR_7 )
+ case CLASS_A:
{
- modem = MODEM_FSK;
- Radio.SetRxConfig( modem, 50e3, downlinkDatarate * 1e3, 0, 83.333e3, 5, timeout, false, 0, true, 0, 0, false, rxContinuous );
- }
- else
- {
- modem = MODEM_LORA;
- Radio.SetRxConfig( modem, bandwidth, downlinkDatarate, 1, 0, 8, timeout, false, 0, false, 0, 0, true, rxContinuous );
- }
-#elif defined( USE_BAND_470 ) || defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- modem = MODEM_LORA;
- Radio.SetRxConfig( modem, bandwidth, downlinkDatarate, 1, 0, 8, timeout, false, 0, false, 0, 0, true, rxContinuous );
-#endif
-
- if( RepeaterSupport == true )
- {
- Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateRepeater[datarate] + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+ if( deviceClass == CLASS_B )
+ {
+ status = LoRaMacClassBSwitchClass( deviceClass );
+ if( status == LORAMAC_STATUS_OK )
+ {
+ LoRaMacDeviceClass = deviceClass;
+ }
+ }
+
+ if( deviceClass == CLASS_C )
+ {
+ LoRaMacDeviceClass = deviceClass;
+
+ // Set the NodeAckRequested indicator to default
+ NodeAckRequested = false;
+ OnRxWindow2TimerEvent( );
+
+ status = LORAMAC_STATUS_OK;
+ }
+ break;
}
- else
+ case CLASS_B:
{
- Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarate[datarate] + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+ status = LoRaMacClassBSwitchClass( deviceClass );
+ if( status == LORAMAC_STATUS_OK )
+ {
+ LoRaMacDeviceClass = deviceClass;
+ }
+ break;
}
-
- if( rxContinuous == false )
+ case CLASS_C:
{
- Radio.Rx( LoRaMacParams.MaxRxWindow );
+ if( deviceClass == CLASS_A )
+ {
+ LoRaMacDeviceClass = deviceClass;
+
+ // Set the radio into sleep to setup a defined state
+ Radio.Sleep( );
+
+ status = LORAMAC_STATUS_OK;
+ }
+ break;
}
- else
- {
- Radio.Rx( 0 ); // Continuous mode
- }
- return true;
}
- return false;
-}
-
-static bool Rx2FreqInRange( uint32_t freq )
-{
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
- if( Radio.CheckRfFrequency( freq ) == true )
-#elif defined( USE_BAND_470 ) || defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- if( ( Radio.CheckRfFrequency( freq ) == true ) &&
- ( freq >= LORAMAC_FIRST_RX1_CHANNEL ) &&
- ( freq <= LORAMAC_LAST_RX1_CHANNEL ) &&
- ( ( ( freq - ( uint32_t ) LORAMAC_FIRST_RX1_CHANNEL ) % ( uint32_t ) LORAMAC_STEPWIDTH_RX1_CHANNEL ) == 0 ) )
-#endif
- {
- return true;
- }
- return false;
+
+ return status;
}
static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen )
{
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
uint16_t maxN = 0;
uint16_t payloadSize = 0;
+ // Setup PHY request
+ getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+ getPhy.Datarate = datarate;
+ getPhy.Attribute = PHY_MAX_PAYLOAD;
+
// Get the maximum payload length
- if( RepeaterSupport == true )
+ if( LoRaMacParams.RepeaterSupport == true )
{
- maxN = MaxPayloadOfDatarateRepeater[datarate];
+ getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER;
}
- else
- {
- maxN = MaxPayloadOfDatarate[datarate];
- }
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ maxN = phyParam.Value;
// Calculate the resulting payload size
payloadSize = ( lenN + fOptsLen );
@@ -2122,258 +1753,6 @@
return false;
}
-static uint8_t CountBits( uint16_t mask, uint8_t nbBits )
-{
- uint8_t nbActiveBits = 0;
-
- for( uint8_t j = 0; j < nbBits; j++ )
- {
- if( ( mask & ( 1 << j ) ) == ( 1 << j ) )
- {
- nbActiveBits++;
- }
- }
- return nbActiveBits;
-}
-
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-static uint8_t CountNbEnabled125kHzChannels( uint16_t *channelsMask )
-{
- uint8_t nb125kHzChannels = 0;
-
- for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS - 8; i += 16, k++ )
- {
- nb125kHzChannels += CountBits( channelsMask[k], 16 );
- }
-
- return nb125kHzChannels;
-}
-
-#if defined( USE_BAND_915_HYBRID )
-static void ReenableChannels( uint16_t mask, uint16_t* channelsMask )
-{
- uint16_t blockMask = mask;
-
- for( uint8_t i = 0, j = 0; i < 4; i++, j += 2 )
- {
- channelsMask[i] = 0;
- if( ( blockMask & ( 1 << j ) ) != 0 )
- {
- channelsMask[i] |= 0x00FF;
- }
- if( ( blockMask & ( 1 << ( j + 1 ) ) ) != 0 )
- {
- channelsMask[i] |= 0xFF00;
- }
- }
- channelsMask[4] = blockMask;
- channelsMask[5] = 0x0000;
-}
-
-static bool ValidateChannelMask( uint16_t* channelsMask )
-{
- bool chanMaskState = false;
- uint16_t block1 = 0;
- uint16_t block2 = 0;
- uint8_t index = 0;
-
- for( uint8_t i = 0; i < 4; i++ )
- {
- block1 = channelsMask[i] & 0x00FF;
- block2 = channelsMask[i] & 0xFF00;
-
- if( ( CountBits( block1, 16 ) > 5 ) && ( chanMaskState == false ) )
- {
- channelsMask[i] &= block1;
- channelsMask[4] = 1 << ( i * 2 );
- chanMaskState = true;
- index = i;
- }
- else if( ( CountBits( block2, 16 ) > 5 ) && ( chanMaskState == false ) )
- {
- channelsMask[i] &= block2;
- channelsMask[4] = 1 << ( i * 2 + 1 );
- chanMaskState = true;
- index = i;
- }
- }
-
- // Do only change the channel mask, if we have found a valid block.
- if( chanMaskState == true )
- {
- for( uint8_t i = 0; i < 4; i++ )
- {
- if( i != index )
- {
- channelsMask[i] = 0;
- }
- }
- }
- return chanMaskState;
-}
-#endif
-#endif
-
-static bool ValidateDatarate( int8_t datarate, uint16_t* channelsMask )
-{
- if( ValueInRange( datarate, LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) == false )
- {
- return false;
- }
- for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS; i += 16, k++ )
- {
- for( uint8_t j = 0; j < 16; j++ )
- {
- if( ( ( channelsMask[k] & ( 1 << j ) ) != 0 ) )
- {// Check datarate validity for enabled channels
- if( ValueInRange( datarate, Channels[i + j].DrRange.Fields.Min, Channels[i + j].DrRange.Fields.Max ) == true )
- {
- // At least 1 channel has been found we can return OK.
- return true;
- }
- }
- }
- }
- return false;
-}
-
-static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower )
-{
- int8_t resultTxPower = txPower;
-
- // Limit tx power to the band max
- resultTxPower = MAX( txPower, maxBandTxPower );
-
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- if( ( LoRaMacParams.ChannelsDatarate == DR_4 ) ||
- ( ( LoRaMacParams.ChannelsDatarate >= DR_8 ) && ( LoRaMacParams.ChannelsDatarate <= DR_13 ) ) )
- {// Limit tx power to max 26dBm
- resultTxPower = MAX( txPower, TX_POWER_26_DBM );
- }
- else
- {
- if( CountNbEnabled125kHzChannels( LoRaMacParams.ChannelsMask ) < 50 )
- {// Limit tx power to max 21dBm
- resultTxPower = MAX( txPower, TX_POWER_20_DBM );
- }
- }
-#endif
- return resultTxPower;
-}
-
-static bool ValueInRange( int8_t value, int8_t min, int8_t max )
-{
- if( ( value >= min ) && ( value <= max ) )
- {
- return true;
- }
- return false;
-}
-
-static bool DisableChannelInMask( uint8_t id, uint16_t* mask )
-{
- uint8_t index = 0;
- index = id / 16;
-
- if( ( index > 4 ) || ( id >= LORA_MAX_NB_CHANNELS ) )
- {
- return false;
- }
-
- // Deactivate channel
- mask[index] &= ~( 1 << ( id % 16 ) );
-
- return true;
-}
-
-static bool AdrNextDr( bool adrEnabled, bool updateChannelMask, int8_t* datarateOut )
-{
- bool adrAckReq = false;
- int8_t datarate = LoRaMacParams.ChannelsDatarate;
-
- if( adrEnabled == true )
- {
- if( datarate == LORAMAC_TX_MIN_DATARATE )
- {
- AdrAckCounter = 0;
- adrAckReq = false;
- }
- else
- {
- if( AdrAckCounter >= ADR_ACK_LIMIT )
- {
- adrAckReq = true;
- LoRaMacParams.ChannelsTxPower = LORAMAC_MAX_TX_POWER;
- }
- else
- {
- adrAckReq = false;
- }
- if( AdrAckCounter >= ( ADR_ACK_LIMIT + ADR_ACK_DELAY ) )
- {
- if( ( AdrAckCounter % ADR_ACK_DELAY ) == 1 )
- {
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
- if( datarate > LORAMAC_TX_MIN_DATARATE )
- {
- datarate--;
- }
- if( datarate == LORAMAC_TX_MIN_DATARATE )
- {
- if( updateChannelMask == true )
- {
- // Re-enable default channels LC1, LC2, LC3
- LoRaMacParams.ChannelsMask[0] = LoRaMacParams.ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
- }
- }
-#elif defined( USE_BAND_470 )
- if( datarate > LORAMAC_TX_MIN_DATARATE )
- {
- datarate--;
- }
- if( datarate == LORAMAC_TX_MIN_DATARATE )
- {
- if( updateChannelMask == true )
- {
- // Re-enable default channels
- memcpy1( ( uint8_t* )LoRaMacParams.ChannelsMask, ( uint8_t* )LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
- }
- }
-#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- if( ( datarate > LORAMAC_TX_MIN_DATARATE ) && ( datarate == DR_8 ) )
- {
- datarate = DR_4;
- }
- else if( datarate > LORAMAC_TX_MIN_DATARATE )
- {
- datarate--;
- }
- if( datarate == LORAMAC_TX_MIN_DATARATE )
- {
- if( updateChannelMask == true )
- {
-#if defined( USE_BAND_915 )
- // Re-enable default channels
- memcpy1( ( uint8_t* )LoRaMacParams.ChannelsMask, ( uint8_t* )LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-#else // defined( USE_BAND_915_HYBRID )
- // Re-enable default channels
- ReenableChannels( LoRaMacParamsDefaults.ChannelsMask[4], LoRaMacParams.ChannelsMask );
-#endif
- }
- }
-#else
-#error "Please define a frequency band in the compiler options."
-#endif
- }
- }
- }
- }
-
- *datarateOut = datarate;
-
- return adrAckReq;
-}
-
static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 )
{
LoRaMacStatus_t status = LORAMAC_STATUS_BUSY;
@@ -2444,6 +1823,57 @@
status = LORAMAC_STATUS_OK;
}
break;
+ case MOTE_MAC_TX_PARAM_SETUP_ANS:
+ if( MacCommandsBufferIndex < bufLen )
+ {
+ MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+ // No payload for this answer
+ status = LORAMAC_STATUS_OK;
+ }
+ break;
+ case MOTE_MAC_DL_CHANNEL_ANS:
+ if( MacCommandsBufferIndex < bufLen )
+ {
+ MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+ // Status: Uplink frequency exists, Channel frequency OK
+ MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
+ status = LORAMAC_STATUS_OK;
+ }
+ break;
+ case MOTE_MAC_PING_SLOT_INFO_REQ:
+ if( MacCommandsBufferIndex < ( LORA_MAC_COMMAND_MAX_LENGTH - 1 ) )
+ {
+ MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+ // Status: Periodicity and Datarate
+ MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
+ status = LORAMAC_STATUS_OK;
+ }
+ break;
+ case MOTE_MAC_PING_SLOT_FREQ_ANS:
+ if( MacCommandsBufferIndex < LORA_MAC_COMMAND_MAX_LENGTH )
+ {
+ MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+ // Status: Datarate range OK, Channel frequency OK
+ MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
+ status = LORAMAC_STATUS_OK;
+ }
+ break;
+ case MOTE_MAC_BEACON_TIMING_REQ:
+ if( MacCommandsBufferIndex < LORA_MAC_COMMAND_MAX_LENGTH )
+ {
+ MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+ // No payload for this answer
+ status = LORAMAC_STATUS_OK;
+ }
+ break;
+ case MOTE_MAC_BEACON_FREQ_ANS:
+ if( MacCommandsBufferIndex < LORA_MAC_COMMAND_MAX_LENGTH )
+ {
+ MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+ // No payload for this answer
+ status = LORAMAC_STATUS_OK;
+ }
+ break;
default:
return LORAMAC_STATUS_SERVICE_UNKNOWN;
}
@@ -2469,14 +1899,15 @@
switch( cmdBufIn[i] )
{
// STICKY
+ case MOTE_MAC_DL_CHANNEL_ANS:
case MOTE_MAC_RX_PARAM_SETUP_ANS:
- {
+ { // 1 byte payload
cmdBufOut[cmdCount++] = cmdBufIn[i++];
cmdBufOut[cmdCount++] = cmdBufIn[i];
break;
}
case MOTE_MAC_RX_TIMING_SETUP_ANS:
- {
+ { // 0 byte payload
cmdBufOut[cmdCount++] = cmdBufIn[i];
break;
}
@@ -2492,6 +1923,7 @@
i++;
break;
}
+ case MOTE_MAC_TX_PARAM_SETUP_ANS:
case MOTE_MAC_DUTY_CYCLE_ANS:
case MOTE_MAC_LINK_CHECK_REQ:
{ // 0 byte payload
@@ -2507,198 +1939,58 @@
static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr )
{
+ uint8_t status = 0;
+ uint8_t index = 0;
+
while( macIndex < commandsSize )
{
// Decode Frame MAC commands
switch( payload[macIndex++] )
{
case SRV_MAC_LINK_CHECK_ANS:
- MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
- MlmeConfirm.DemodMargin = payload[macIndex++];
- MlmeConfirm.NbGateways = payload[macIndex++];
+ index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_LINK_CHECK );
+ if( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
+ {
+ MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_OK;
+ MlmeConfirm.DemodMargin = payload[macIndex++];
+ MlmeConfirm.NbGateways = payload[macIndex++];
+ }
break;
case SRV_MAC_LINK_ADR_REQ:
{
- uint8_t i;
- uint8_t status = 0x07;
- uint16_t chMask;
- int8_t txPower = 0;
- int8_t datarate = 0;
- uint8_t nbRep = 0;
- uint8_t chMaskCntl = 0;
- uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 };
-
- // Initialize local copy of the channels mask array
- for( i = 0; i < 6; i++ )
- {
- channelsMask[i] = LoRaMacParams.ChannelsMask[i];
- }
- datarate = payload[macIndex++];
- txPower = datarate & 0x0F;
- datarate = ( datarate >> 4 ) & 0x0F;
-
- if( ( AdrCtrlOn == false ) &&
- ( ( LoRaMacParams.ChannelsDatarate != datarate ) || ( LoRaMacParams.ChannelsTxPower != txPower ) ) )
- { // ADR disabled don't handle ADR requests if server tries to change datarate or txpower
- // Answer the server with fail status
- // Power ACK = 0
- // Data rate ACK = 0
- // Channel mask = 0
- AddMacCommand( MOTE_MAC_LINK_ADR_ANS, 0, 0 );
- macIndex += 3; // Skip over the remaining bytes of the request
- break;
- }
- chMask = ( uint16_t )payload[macIndex++];
- chMask |= ( uint16_t )payload[macIndex++] << 8;
-
- nbRep = payload[macIndex++];
- chMaskCntl = ( nbRep >> 4 ) & 0x07;
- nbRep &= 0x0F;
- if( nbRep == 0 )
- {
- nbRep = 1;
- }
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
- if( ( chMaskCntl == 0 ) && ( chMask == 0 ) )
- {
- status &= 0xFE; // Channel mask KO
- }
- else if( ( ( chMaskCntl >= 1 ) && ( chMaskCntl <= 5 )) ||
- ( chMaskCntl >= 7 ) )
- {
- // RFU
- status &= 0xFE; // Channel mask KO
- }
- else
- {
- for( i = 0; i < LORA_MAX_NB_CHANNELS; i++ )
- {
- if( chMaskCntl == 6 )
- {
- if( Channels[i].Frequency != 0 )
- {
- chMask |= 1 << i;
- }
- }
- else
- {
- if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
- ( Channels[i].Frequency == 0 ) )
- {// Trying to enable an undefined channel
- status &= 0xFE; // Channel mask KO
- }
- }
- }
- channelsMask[0] = chMask;
- }
-#elif defined( USE_BAND_470 )
- if( chMaskCntl == 6 )
- {
- // Enable all 125 kHz channels
- for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS; i += 16, k++ )
- {
- for( uint8_t j = 0; j < 16; j++ )
- {
- if( Channels[i + j].Frequency != 0 )
- {
- channelsMask[k] |= 1 << j;
- }
- }
- }
- }
- else if( chMaskCntl == 7 )
- {
- status &= 0xFE; // Channel mask KO
- }
- else
- {
- for( uint8_t i = 0; i < 16; i++ )
- {
- if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
- ( Channels[chMaskCntl * 16 + i].Frequency == 0 ) )
- {// Trying to enable an undefined channel
- status &= 0xFE; // Channel mask KO
- }
- }
- channelsMask[chMaskCntl] = chMask;
- }
-#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- if( chMaskCntl == 6 )
- {
- // Enable all 125 kHz channels
- channelsMask[0] = 0xFFFF;
- channelsMask[1] = 0xFFFF;
- channelsMask[2] = 0xFFFF;
- channelsMask[3] = 0xFFFF;
- // Apply chMask to channels 64 to 71
- channelsMask[4] = chMask;
- }
- else if( chMaskCntl == 7 )
- {
- // Disable all 125 kHz channels
- channelsMask[0] = 0x0000;
- channelsMask[1] = 0x0000;
- channelsMask[2] = 0x0000;
- channelsMask[3] = 0x0000;
- // Apply chMask to channels 64 to 71
- channelsMask[4] = chMask;
- }
- else if( chMaskCntl == 5 )
- {
- // RFU
- status &= 0xFE; // Channel mask KO
- }
- else
- {
- channelsMask[chMaskCntl] = chMask;
-
- // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels
- if( ( datarate < DR_4 ) && ( CountNbEnabled125kHzChannels( channelsMask ) < 2 ) )
- {
- status &= 0xFE; // Channel mask KO
- }
-
-#if defined( USE_BAND_915_HYBRID )
- if( ValidateChannelMask( channelsMask ) == false )
- {
- status &= 0xFE; // Channel mask KO
- }
-#endif
- }
-#else
- #error "Please define a frequency band in the compiler options."
-#endif
- if( ValidateDatarate( datarate, channelsMask ) == false )
- {
- status &= 0xFD; // Datarate KO
- }
-
- //
- // Remark MaxTxPower = 0 and MinTxPower = 5
- //
- if( ValueInRange( txPower, LORAMAC_MAX_TX_POWER, LORAMAC_MIN_TX_POWER ) == false )
- {
- status &= 0xFB; // TxPower KO
- }
+ LinkAdrReqParams_t linkAdrReq;
+ int8_t linkAdrDatarate = DR_0;
+ int8_t linkAdrTxPower = TX_POWER_0;
+ uint8_t linkAdrNbRep = 0;
+ uint8_t linkAdrNbBytesParsed = 0;
+
+ // Fill parameter structure
+ linkAdrReq.Payload = &payload[macIndex - 1];
+ linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 );
+ linkAdrReq.AdrEnabled = AdrCtrlOn;
+ linkAdrReq.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+ linkAdrReq.CurrentDatarate = LoRaMacParams.ChannelsDatarate;
+ linkAdrReq.CurrentTxPower = LoRaMacParams.ChannelsTxPower;
+ linkAdrReq.CurrentNbRep = LoRaMacParams.ChannelsNbRep;
+
+ // Process the ADR requests
+ status = RegionLinkAdrReq( LoRaMacRegion, &linkAdrReq, &linkAdrDatarate,
+ &linkAdrTxPower, &linkAdrNbRep, &linkAdrNbBytesParsed );
+
if( ( status & 0x07 ) == 0x07 )
{
- LoRaMacParams.ChannelsDatarate = datarate;
- LoRaMacParams.ChannelsTxPower = txPower;
-
- memcpy1( ( uint8_t* )LoRaMacParams.ChannelsMask, ( uint8_t* )channelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-
- LoRaMacParams.ChannelsNbRep = nbRep;
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- // Reset ChannelsMaskRemaining to the new ChannelsMask
- ChannelsMaskRemaining[0] &= channelsMask[0];
- ChannelsMaskRemaining[1] &= channelsMask[1];
- ChannelsMaskRemaining[2] &= channelsMask[2];
- ChannelsMaskRemaining[3] &= channelsMask[3];
- ChannelsMaskRemaining[4] = channelsMask[4];
- ChannelsMaskRemaining[5] = channelsMask[5];
-#endif
+ LoRaMacParams.ChannelsDatarate = linkAdrDatarate;
+ LoRaMacParams.ChannelsTxPower = linkAdrTxPower;
+ LoRaMacParams.ChannelsNbRep = linkAdrNbRep;
}
- AddMacCommand( MOTE_MAC_LINK_ADR_ANS, status, 0 );
+
+ // Add the answers to the buffer
+ for( uint8_t i = 0; i < ( linkAdrNbBytesParsed / 5 ); i++ )
+ {
+ AddMacCommand( MOTE_MAC_LINK_ADR_ANS, status, 0 );
+ }
+ // Update MAC index
+ macIndex += linkAdrNbBytesParsed - 1;
}
break;
case SRV_MAC_DUTY_CYCLE_REQ:
@@ -2708,46 +2000,26 @@
break;
case SRV_MAC_RX_PARAM_SETUP_REQ:
{
- uint8_t status = 0x07;
- int8_t datarate = 0;
- int8_t drOffset = 0;
- uint32_t freq = 0;
-
- drOffset = ( payload[macIndex] >> 4 ) & 0x07;
- datarate = payload[macIndex] & 0x0F;
+ RxParamSetupReqParams_t rxParamSetupReq;
+ status = 0x07;
+
+ rxParamSetupReq.DrOffset = ( payload[macIndex] >> 4 ) & 0x07;
+ rxParamSetupReq.Datarate = payload[macIndex] & 0x0F;
macIndex++;
- freq = ( uint32_t )payload[macIndex++];
- freq |= ( uint32_t )payload[macIndex++] << 8;
- freq |= ( uint32_t )payload[macIndex++] << 16;
- freq *= 100;
-
- if( Rx2FreqInRange( freq ) == false )
- {
- status &= 0xFE; // Channel frequency KO
- }
-
- if( ValueInRange( datarate, LORAMAC_RX_MIN_DATARATE, LORAMAC_RX_MAX_DATARATE ) == false )
- {
- status &= 0xFD; // Datarate KO
- }
-#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
- if( ( ValueInRange( datarate, DR_5, DR_7 ) == true ) ||
- ( datarate > DR_13 ) )
- {
- status &= 0xFD; // Datarate KO
- }
-#endif
- if( ValueInRange( drOffset, LORAMAC_MIN_RX1_DR_OFFSET, LORAMAC_MAX_RX1_DR_OFFSET ) == false )
- {
- status &= 0xFB; // Rx1DrOffset range KO
- }
+ rxParamSetupReq.Frequency = ( uint32_t )payload[macIndex++];
+ rxParamSetupReq.Frequency |= ( uint32_t )payload[macIndex++] << 8;
+ rxParamSetupReq.Frequency |= ( uint32_t )payload[macIndex++] << 16;
+ rxParamSetupReq.Frequency *= 100;
+
+ // Perform request on region
+ status = RegionRxParamSetupReq( LoRaMacRegion, &rxParamSetupReq );
if( ( status & 0x07 ) == 0x07 )
{
- LoRaMacParams.Rx2Channel.Datarate = datarate;
- LoRaMacParams.Rx2Channel.Frequency = freq;
- LoRaMacParams.Rx1DrOffset = drOffset;
+ LoRaMacParams.Rx2Channel.Datarate = rxParamSetupReq.Datarate;
+ LoRaMacParams.Rx2Channel.Frequency = rxParamSetupReq.Frequency;
+ LoRaMacParams.Rx1DrOffset = rxParamSetupReq.DrOffset;
}
AddMacCommand( MOTE_MAC_RX_PARAM_SETUP_ANS, status, 0 );
}
@@ -2764,69 +2036,22 @@
}
case SRV_MAC_NEW_CHANNEL_REQ:
{
- uint8_t status = 0x03;
-
-#if defined( USE_BAND_470 ) || defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- status &= 0xFC; // Channel frequency and datarate KO
- macIndex += 5;
-#else
- int8_t channelIndex = 0;
+ NewChannelReqParams_t newChannelReq;
ChannelParams_t chParam;
-
- channelIndex = payload[macIndex++];
+ status = 0x03;
+
+ newChannelReq.ChannelId = payload[macIndex++];
+ newChannelReq.NewChannel = &chParam;
+
chParam.Frequency = ( uint32_t )payload[macIndex++];
chParam.Frequency |= ( uint32_t )payload[macIndex++] << 8;
chParam.Frequency |= ( uint32_t )payload[macIndex++] << 16;
chParam.Frequency *= 100;
+ chParam.Rx1Frequency = 0;
chParam.DrRange.Value = payload[macIndex++];
- LoRaMacState |= LORAMAC_TX_CONFIG;
- if( chParam.Frequency == 0 )
- {
- if( channelIndex < 3 )
- {
- status &= 0xFC;
- }
- else
- {
- if( LoRaMacChannelRemove( channelIndex ) != LORAMAC_STATUS_OK )
- {
- status &= 0xFC;
- }
- }
- }
- else
- {
- switch( LoRaMacChannelAdd( channelIndex, chParam ) )
- {
- case LORAMAC_STATUS_OK:
- {
- break;
- }
- case LORAMAC_STATUS_FREQUENCY_INVALID:
- {
- status &= 0xFE;
- break;
- }
- case LORAMAC_STATUS_DATARATE_INVALID:
- {
- status &= 0xFD;
- break;
- }
- case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
- {
- status &= 0xFC;
- break;
- }
- default:
- {
- status &= 0xFC;
- break;
- }
- }
- }
- LoRaMacState &= ~LORAMAC_TX_CONFIG;
-#endif
+ status = RegionNewChannelReq( LoRaMacRegion, &newChannelReq );
+
AddMacCommand( MOTE_MAC_NEW_CHANNEL_ANS, status, 0 );
}
break;
@@ -2838,11 +2063,109 @@
{
delay++;
}
- LoRaMacParams.ReceiveDelay1 = delay * 1e3;
- LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1e3;
+ LoRaMacParams.ReceiveDelay1 = delay * 1000;
+ LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1000;
AddMacCommand( MOTE_MAC_RX_TIMING_SETUP_ANS, 0, 0 );
}
break;
+ case SRV_MAC_TX_PARAM_SETUP_REQ:
+ {
+ TxParamSetupReqParams_t txParamSetupReq;
+ uint8_t eirpDwellTime = payload[macIndex++];
+
+ txParamSetupReq.UplinkDwellTime = 0;
+ txParamSetupReq.DownlinkDwellTime = 0;
+
+ if( ( eirpDwellTime & 0x20 ) == 0x20 )
+ {
+ txParamSetupReq.DownlinkDwellTime = 1;
+ }
+ if( ( eirpDwellTime & 0x10 ) == 0x10 )
+ {
+ txParamSetupReq.UplinkDwellTime = 1;
+ }
+ txParamSetupReq.MaxEirp = eirpDwellTime & 0x0F;
+
+ // Check the status for correctness
+ if( RegionTxParamSetupReq( LoRaMacRegion, &txParamSetupReq ) != -1 )
+ {
+ // Accept command
+ LoRaMacParams.UplinkDwellTime = txParamSetupReq.UplinkDwellTime;
+ LoRaMacParams.DownlinkDwellTime = txParamSetupReq.DownlinkDwellTime;
+ LoRaMacParams.MaxEirp = LoRaMacMaxEirpTable[txParamSetupReq.MaxEirp];
+ // Add command response
+ AddMacCommand( MOTE_MAC_TX_PARAM_SETUP_ANS, 0, 0 );
+ }
+ }
+ break;
+ case SRV_MAC_DL_CHANNEL_REQ:
+ {
+ DlChannelReqParams_t dlChannelReq;
+ status = 0x03;
+
+ dlChannelReq.ChannelId = payload[macIndex++];
+ dlChannelReq.Rx1Frequency = ( uint32_t )payload[macIndex++];
+ dlChannelReq.Rx1Frequency |= ( uint32_t )payload[macIndex++] << 8;
+ dlChannelReq.Rx1Frequency |= ( uint32_t )payload[macIndex++] << 16;
+ dlChannelReq.Rx1Frequency *= 100;
+
+ status = RegionDlChannelReq( LoRaMacRegion, &dlChannelReq );
+
+ AddMacCommand( MOTE_MAC_DL_CHANNEL_ANS, status, 0 );
+ }
+ break;
+ case SRV_MAC_PING_SLOT_INFO_ANS:
+ {
+ LoRaMacClassBPingSlotInfoAns( );
+ }
+ break;
+ case SRV_MAC_PING_SLOT_CHANNEL_REQ:
+ {
+ uint8_t status = 0x03;
+ uint32_t frequency = 0;
+ uint8_t datarate;
+
+ frequency = ( uint32_t )payload[macIndex++];
+ frequency |= ( uint32_t )payload[macIndex++] << 8;
+ frequency |= ( uint32_t )payload[macIndex++] << 16;
+ frequency *= 100;
+ datarate = payload[macIndex++] & 0x0F;
+
+ status = LoRaMacClassBPingSlotChannelReq( datarate, frequency );
+ AddMacCommand( MOTE_MAC_PING_SLOT_FREQ_ANS, status, 0 );
+ }
+ break;
+ case SRV_MAC_BEACON_TIMING_ANS:
+ {
+ uint16_t beaconTimingDelay = 0;
+ uint8_t beaconTimingChannel = 0;
+
+ beaconTimingDelay = ( uint16_t )payload[macIndex++];
+ beaconTimingDelay |= ( uint16_t )payload[macIndex++] << 8;
+ beaconTimingChannel = payload[macIndex++];
+
+ LoRaMacClassBBeaconTimingAns( beaconTimingDelay, beaconTimingChannel );
+ }
+ break;
+ case SRV_MAC_BEACON_FREQ_REQ:
+ {
+ uint32_t frequency = 0;
+
+ frequency = ( uint32_t )payload[macIndex++];
+ frequency |= ( uint32_t )payload[macIndex++] << 8;
+ frequency |= ( uint32_t )payload[macIndex++] << 16;
+ frequency *= 100;
+
+ if( LoRaMacClassBBeaconFreqReq( frequency ) == true )
+ {
+ AddMacCommand( MOTE_MAC_BEACON_FREQ_ANS, 1, 0 );
+ }
+ else
+ {
+ AddMacCommand( MOTE_MAC_BEACON_FREQ_ANS, 0, 0 );
+ }
+ }
+ break;
default:
// Unknown command. ABORT MAC commands processing
return;
@@ -2857,7 +2180,14 @@
fCtrl.Value = 0;
fCtrl.Bits.FOptsLen = 0;
- fCtrl.Bits.FPending = 0;
+ if( LoRaMacDeviceClass == CLASS_B )
+ {
+ fCtrl.Bits.FPending = 1;
+ }
+ else
+ {
+ fCtrl.Bits.FPending = 0;
+ }
fCtrl.Bits.Ack = false;
fCtrl.Bits.AdrAckReq = false;
fCtrl.Bits.Adr = AdrCtrlOn;
@@ -2884,6 +2214,9 @@
static LoRaMacStatus_t ScheduleTx( void )
{
TimerTime_t dutyCycleTimeOff = 0;
+ TimerTime_t mutexTimeLock = 0;
+ TimerTime_t timeOff = 0;
+ NextChanParams_t nextChan;
// Check if the device is off
if( MaxDCycle == 255 )
@@ -2895,31 +2228,41 @@
AggregatedTimeOff = 0;
}
+ // Update Backoff
+ CalculateBackOff( LastTxChannel );
+
+ nextChan.AggrTimeOff = AggregatedTimeOff;
+ nextChan.Datarate = LoRaMacParams.ChannelsDatarate;
+ nextChan.DutyCycleEnabled = DutyCycleOn;
+ nextChan.Joined = IsLoRaMacNetworkJoined;
+ nextChan.LastAggrTx = AggregatedLastTxDoneTime;
+
// Select channel
- while( SetNextChannel( &dutyCycleTimeOff ) == false )
+ while( RegionNextChannel( LoRaMacRegion, &nextChan, &Channel, &dutyCycleTimeOff, &AggregatedTimeOff ) == false )
{
// Set the default datarate
LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate;
-
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
- // Re-enable default channels LC1, LC2, LC3
- LoRaMacParams.ChannelsMask[0] = LoRaMacParams.ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
-#endif
+ // Update datarate in the function parameters
+ nextChan.Datarate = LoRaMacParams.ChannelsDatarate;
}
// Compute Rx1 windows parameters
-#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
- RxWindowsParams[0] = ComputeRxWindowParameters( DatarateOffsets[LoRaMacParams.ChannelsDatarate][LoRaMacParams.Rx1DrOffset], LoRaMacParams.SystemMaxRxError );
-#else
- RxWindowsParams[0] = ComputeRxWindowParameters( MAX( DR_0, LoRaMacParams.ChannelsDatarate - LoRaMacParams.Rx1DrOffset ), LoRaMacParams.SystemMaxRxError );
-#endif
+ RegionComputeRxWindowParameters( LoRaMacRegion,
+ RegionApplyDrOffset( LoRaMacRegion, LoRaMacParams.DownlinkDwellTime, LoRaMacParams.ChannelsDatarate, LoRaMacParams.Rx1DrOffset ),
+ LoRaMacParams.MinRxSymbols,
+ LoRaMacParams.SystemMaxRxError,
+ &RxWindow1Config );
// Compute Rx2 windows parameters
- RxWindowsParams[1] = ComputeRxWindowParameters( LoRaMacParams.Rx2Channel.Datarate, LoRaMacParams.SystemMaxRxError );
+ RegionComputeRxWindowParameters( LoRaMacRegion,
+ LoRaMacParams.Rx2Channel.Datarate,
+ LoRaMacParams.MinRxSymbols,
+ LoRaMacParams.SystemMaxRxError,
+ &RxWindow2Config );
if( IsLoRaMacNetworkJoined == false )
{
- RxWindow1Delay = LoRaMacParams.JoinAcceptDelay1 + RxWindowsParams[0].RxOffset;
- RxWindow2Delay = LoRaMacParams.JoinAcceptDelay2 + RxWindowsParams[1].RxOffset;
+ RxWindow1Delay = LoRaMacParams.JoinAcceptDelay1 + RxWindow1Config.WindowOffset;
+ RxWindow2Delay = LoRaMacParams.JoinAcceptDelay2 + RxWindow2Config.WindowOffset;
}
else
{
@@ -2927,123 +2270,68 @@
{
return LORAMAC_STATUS_LENGTH_ERROR;
}
- RxWindow1Delay = LoRaMacParams.ReceiveDelay1 + RxWindowsParams[0].RxOffset;
- RxWindow2Delay = LoRaMacParams.ReceiveDelay2 + RxWindowsParams[1].RxOffset;
+ RxWindow1Delay = LoRaMacParams.ReceiveDelay1 + RxWindow1Config.WindowOffset;
+ RxWindow2Delay = LoRaMacParams.ReceiveDelay2 + RxWindow2Config.WindowOffset;
}
// Schedule transmission of frame
if( dutyCycleTimeOff == 0 )
{
// Try to send now
- return SendFrameOnChannel( Channels[Channel] );
+ mutexTimeLock = SendFrameOnChannel( Channel );
}
- else
+
+ timeOff = MAX( dutyCycleTimeOff, mutexTimeLock );
+
+ if( timeOff > 0 )
{
// Send later - prepare timer
LoRaMacState |= LORAMAC_TX_DELAYED;
- TimerSetValue( &TxDelayedTimer, dutyCycleTimeOff );
+ TimerSetValue( &TxDelayedTimer, timeOff );
TimerStart( &TxDelayedTimer );
-
- return LORAMAC_STATUS_OK;
}
-}
-
-static uint16_t JoinDutyCycle( void )
-{
- uint16_t dutyCycle = 0;
- TimerTime_t timeElapsed = TimerGetElapsedTime( LoRaMacInitializationTime );
-
- if( timeElapsed < 3600e3 )
- {
- dutyCycle = BACKOFF_DC_1_HOUR;
- }
- else if( timeElapsed < ( 3600e3 + 36000e3 ) )
- {
- dutyCycle = BACKOFF_DC_10_HOURS;
- }
- else
- {
- dutyCycle = BACKOFF_DC_24_HOURS;
- }
- return dutyCycle;
+ return LORAMAC_STATUS_OK;
}
static void CalculateBackOff( uint8_t channel )
{
- uint16_t dutyCycle = Bands[Channels[channel].Band].DCycle;
- uint16_t joinDutyCycle = 0;
-
- // Reset time-off to initial value.
- Bands[Channels[channel].Band].TimeOff = 0;
-
- if( IsLoRaMacNetworkJoined == false )
- {
- // The node has not joined yet. Apply join duty cycle to all regions.
- joinDutyCycle = JoinDutyCycle( );
- dutyCycle = MAX( dutyCycle, joinDutyCycle );
-
- // Update Band time-off.
- Bands[Channels[channel].Band].TimeOff = TxTimeOnAir * dutyCycle - TxTimeOnAir;
- }
- else
- {
- if( DutyCycleOn == true )
- {
- Bands[Channels[channel].Band].TimeOff = TxTimeOnAir * dutyCycle - TxTimeOnAir;
- }
- }
-
- // Update Aggregated Time OFF
+ CalcBackOffParams_t calcBackOff;
+
+ calcBackOff.Joined = IsLoRaMacNetworkJoined;
+ calcBackOff.DutyCycleEnabled = DutyCycleOn;
+ calcBackOff.Channel = channel;
+ calcBackOff.ElapsedTime = TimerGetElapsedTime( LoRaMacInitializationTime );
+ calcBackOff.TxTimeOnAir = TxTimeOnAir;
+ calcBackOff.LastTxIsJoinRequest = LastTxIsJoinRequest;
+
+ // Update regional back-off
+ RegionCalcBackOff( LoRaMacRegion, &calcBackOff );
+
+ // Update aggregated time-off
AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir );
}
-static int8_t AlternateDatarate( uint16_t nbTrials )
+static uint8_t GetMlmeConfirmIndex( MlmeConfirmQueue_t* queue, Mlme_t req )
{
- int8_t datarate = LORAMAC_TX_MIN_DATARATE;
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-#if defined( USE_BAND_915 )
- // Re-enable 500 kHz default channels
- LoRaMacParams.ChannelsMask[4] = 0x00FF;
-#else // defined( USE_BAND_915_HYBRID )
- // Re-enable 500 kHz default channels
- ReenableChannels( LoRaMacParamsDefaults.ChannelsMask[4], LoRaMacParams.ChannelsMask );
-#endif
-
- if( ( nbTrials & 0x01 ) == 0x01 )
- {
- datarate = DR_4;
- }
- else
- {
- datarate = DR_0;
- }
-#else
- if( ( nbTrials % 48 ) == 0 )
+ for( uint8_t i = 0; i < MlmeConfirmQueueCnt; i++ )
{
- datarate = DR_0;
- }
- else if( ( nbTrials % 32 ) == 0 )
- {
- datarate = DR_1;
+ if( queue->MlmeRequest == req )
+ {
+ return i;
+ }
+ queue++;
}
- else if( ( nbTrials % 24 ) == 0 )
- {
- datarate = DR_2;
- }
- else if( ( nbTrials % 16 ) == 0 )
+
+ // Out of band
+ return LORA_MAC_MLME_CONFIRM_QUEUE_LEN;
+}
+
+static void SetEveryMlmeConfirmStatus( MlmeConfirmQueue_t* queue, LoRaMacEventInfoStatus_t status )
+{
+ for( uint8_t i = 0; i < MlmeConfirmQueueCnt; i++ )
{
- datarate = DR_3;
- }
- else if( ( nbTrials % 8 ) == 0 )
- {
- datarate = DR_4;
+ queue[i].Status = status;
}
- else
- {
- datarate = DR_5;
- }
-#endif
- return datarate;
}
static void ResetMacParameters( void )
@@ -3071,16 +2359,12 @@
LoRaMacParams.ChannelsTxPower = LoRaMacParamsDefaults.ChannelsTxPower;
LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate;
-
LoRaMacParams.Rx1DrOffset = LoRaMacParamsDefaults.Rx1DrOffset;
LoRaMacParams.Rx2Channel = LoRaMacParamsDefaults.Rx2Channel;
-
- memcpy1( ( uint8_t* ) LoRaMacParams.ChannelsMask, ( uint8_t* ) LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- memcpy1( ( uint8_t* ) ChannelsMaskRemaining, ( uint8_t* ) LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-#endif
-
+ LoRaMacParams.UplinkDwellTime = LoRaMacParamsDefaults.UplinkDwellTime;
+ LoRaMacParams.DownlinkDwellTime = LoRaMacParamsDefaults.DownlinkDwellTime;
+ LoRaMacParams.MaxEirp = LoRaMacParamsDefaults.MaxEirp;
+ LoRaMacParams.AntennaGain = LoRaMacParamsDefaults.AntennaGain;
NodeAckRequested = false;
SrvAckRequested = false;
@@ -3095,11 +2379,13 @@
}
// Initialize channel index.
- Channel = LORA_MAX_NB_CHANNELS;
+ Channel = 0;
+ LastTxChannel = Channel;
}
LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize )
{
+ AdrNextParams_t adrNext;
uint16_t i;
uint8_t pktHeaderLen = 0;
uint32_t mic = 0;
@@ -3151,8 +2437,17 @@
return LORAMAC_STATUS_NO_NETWORK_JOINED; // No network has been joined yet
}
- fCtrl->Bits.AdrAckReq = AdrNextDr( fCtrl->Bits.Adr, true, &LoRaMacParams.ChannelsDatarate );
-
+ // Adr next request
+ adrNext.UpdateChanMask = true;
+ adrNext.AdrEnabled = fCtrl->Bits.Adr;
+ adrNext.AdrAckCounter = AdrAckCounter;
+ adrNext.Datarate = LoRaMacParams.ChannelsDatarate;
+ adrNext.TxPower = LoRaMacParams.ChannelsTxPower;
+ adrNext.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+
+ fCtrl->Bits.AdrAckReq = RegionAdrNext( LoRaMacRegion, &adrNext,
+ &LoRaMacParams.ChannelsDatarate, &LoRaMacParams.ChannelsTxPower, &AdrAckCounter );
+
if( SrvAckRequested == true )
{
SrvAckRequested = false;
@@ -3175,21 +2470,30 @@
if( ( payload != NULL ) && ( LoRaMacTxPayloadLen > 0 ) )
{
- if( ( MacCommandsBufferIndex <= LORA_MAC_COMMAND_MAX_LENGTH ) && ( MacCommandsInNextTx == true ) )
+ if( MacCommandsInNextTx == true )
{
- fCtrl->Bits.FOptsLen += MacCommandsBufferIndex;
-
- // Update FCtrl field with new value of OptionsLength
- LoRaMacBuffer[0x05] = fCtrl->Value;
- for( i = 0; i < MacCommandsBufferIndex; i++ )
+ if( MacCommandsBufferIndex <= LORA_MAC_COMMAND_MAX_FOPTS_LENGTH )
{
- LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i];
+ fCtrl->Bits.FOptsLen += MacCommandsBufferIndex;
+
+ // Update FCtrl field with new value of OptionsLength
+ LoRaMacBuffer[0x05] = fCtrl->Value;
+ for( i = 0; i < MacCommandsBufferIndex; i++ )
+ {
+ LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i];
+ }
+ }
+ else
+ {
+ LoRaMacTxPayloadLen = MacCommandsBufferIndex;
+ payload = MacCommandsBuffer;
+ framePort = 0;
}
}
}
else
{
- if( ( MacCommandsBufferIndex > 0 ) && ( MacCommandsInNextTx ) )
+ if( ( MacCommandsBufferIndex > 0 ) && ( MacCommandsInNextTx == true ) )
{
LoRaMacTxPayloadLen = MacCommandsBufferIndex;
payload = MacCommandsBuffer;
@@ -3210,6 +2514,8 @@
if( framePort == 0 )
{
+ // Reset buffer index as the mac commands are being sent on port 0
+ MacCommandsBufferIndex = 0;
LoRaMacPayloadEncrypt( (uint8_t* ) payload, LoRaMacTxPayloadLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &LoRaMacBuffer[pktHeaderLen] );
}
else
@@ -3243,67 +2549,55 @@
return LORAMAC_STATUS_OK;
}
-LoRaMacStatus_t SendFrameOnChannel( ChannelParams_t channel )
+TimerTime_t SendFrameOnChannel( uint8_t channel )
{
- int8_t datarate = Datarates[LoRaMacParams.ChannelsDatarate];
- int8_t txPowerIndex = 0;
+ TxConfigParams_t txConfig;
int8_t txPower = 0;
- txPowerIndex = LimitTxPower( LoRaMacParams.ChannelsTxPower, Bands[channel.Band].TxMaxPower );
- txPower = TxPowers[txPowerIndex];
-
- MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+ txConfig.Channel = channel;
+ txConfig.Datarate = LoRaMacParams.ChannelsDatarate;
+ txConfig.TxPower = LoRaMacParams.ChannelsTxPower;
+ txConfig.MaxEirp = LoRaMacParams.MaxEirp;
+ txConfig.AntennaGain = LoRaMacParams.AntennaGain;
+ txConfig.PktLen = LoRaMacBufferPktLen;
+
+ if( LoRaMacDeviceClass == CLASS_B )
+ {
+ if( LoRaMacClassBIsBeaconExpected( ) == true )
+ {
+ return LoRaMacClassBGetBeaconReservedTime( );
+ }
+ if( LoRaMacClassBIsPingExpected( ) == true )
+ {
+ return LoRaMacClassBGetPingSlotWinTime( );
+ }
+ }
+ RegionTxConfig( LoRaMacRegion, &txConfig, &txPower, &TxTimeOnAir );
+
+ SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_ERROR );
McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
McpsConfirm.Datarate = LoRaMacParams.ChannelsDatarate;
- McpsConfirm.TxPower = txPowerIndex;
- McpsConfirm.UpLinkFrequency = channel.Frequency;
-
- Radio.SetChannel( channel.Frequency );
-
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
- if( LoRaMacParams.ChannelsDatarate == DR_7 )
- { // High Speed FSK channel
- Radio.SetMaxPayloadLength( MODEM_FSK, LoRaMacBufferPktLen );
- Radio.SetTxConfig( MODEM_FSK, txPower, 25e3, 0, datarate * 1e3, 0, 5, false, true, 0, 0, false, 3e3 );
- TxTimeOnAir = Radio.TimeOnAir( MODEM_FSK, LoRaMacBufferPktLen );
-
- }
- else if( LoRaMacParams.ChannelsDatarate == DR_6 )
- { // High speed LoRa channel
- Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
- Radio.SetTxConfig( MODEM_LORA, txPower, 0, 1, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
- TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
- }
- else
- { // Normal LoRa channel
- Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
- Radio.SetTxConfig( MODEM_LORA, txPower, 0, 0, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
- TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
- }
-#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
- if( LoRaMacParams.ChannelsDatarate >= DR_4 )
- { // High speed LoRa channel BW500 kHz
- Radio.SetTxConfig( MODEM_LORA, txPower, 0, 2, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
- TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
- }
- else
- { // Normal LoRa channel
- Radio.SetTxConfig( MODEM_LORA, txPower, 0, 0, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
- TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
- }
-#elif defined( USE_BAND_470 )
- Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
- Radio.SetTxConfig( MODEM_LORA, txPower, 0, 0, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
- TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
-#else
- #error "Please define a frequency band in the compiler options."
-#endif
+ McpsConfirm.TxPower = txPower;
// Store the time on air
McpsConfirm.TxTimeOnAir = TxTimeOnAir;
MlmeConfirm.TxTimeOnAir = TxTimeOnAir;
+ if( ( LoRaMacDeviceClass == CLASS_B ) || ( LoRaMacClassBIsBeaconModeActive( ) == true ) )
+ {
+ TimerTime_t collisionTime = LoRaMacClassBIsUplinkCollision( TxTimeOnAir );
+
+ if( collisionTime > 0 )
+ {
+ return collisionTime;
+ }
+ }
+
+ if( LoRaMacDeviceClass == CLASS_B )
+ {
+ LoRaMacClassBHaltBeaconing( );
+ }
+
// Starts the MAC layer status check timer
TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT );
TimerStart( &MacStateCheckTimer );
@@ -3313,28 +2607,33 @@
JoinRequestTrials++;
}
+ LoRaMacState |= LORAMAC_TX_RUNNING;
+
// Send now
Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen );
LoRaMacState |= LORAMAC_TX_RUNNING;
- return LORAMAC_STATUS_OK;
+ return 0;
}
LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout )
{
- int8_t txPowerIndex = 0;
- int8_t txPower = 0;
-
- txPowerIndex = LimitTxPower( LoRaMacParams.ChannelsTxPower, Bands[Channels[Channel].Band].TxMaxPower );
- txPower = TxPowers[txPowerIndex];
+ ContinuousWaveParams_t continuousWave;
+
+ continuousWave.Channel = Channel;
+ continuousWave.Datarate = LoRaMacParams.ChannelsDatarate;
+ continuousWave.TxPower = LoRaMacParams.ChannelsTxPower;
+ continuousWave.MaxEirp = LoRaMacParams.MaxEirp;
+ continuousWave.AntennaGain = LoRaMacParams.AntennaGain;
+ continuousWave.Timeout = timeout;
+
+ RegionSetContinuousWave( LoRaMacRegion, &continuousWave );
// Starts the MAC layer status check timer
TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT );
TimerStart( &MacStateCheckTimer );
- Radio.SetTxContinuousWave( Channels[Channel].Frequency, txPower, timeout );
-
LoRaMacState |= LORAMAC_TX_RUNNING;
return LORAMAC_STATUS_OK;
@@ -3353,8 +2652,13 @@
return LORAMAC_STATUS_OK;
}
-LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks )
+LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks, LoRaMacRegion_t region )
{
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ LoRaMacClassBCallback_t classBCallbacks;
+ LoRaMacClassBParams_t classBParams;
+
if( primitives == NULL )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
@@ -3362,13 +2666,20 @@
if( ( primitives->MacMcpsConfirm == NULL ) ||
( primitives->MacMcpsIndication == NULL ) ||
- ( primitives->MacMlmeConfirm == NULL ) )
+ ( primitives->MacMlmeConfirm == NULL ) ||
+ ( primitives->MacMlmeIndication == NULL ) )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
+ // Verify if the region is supported
+ if( RegionIsActive( region ) == false )
+ {
+ return LORAMAC_STATUS_REGION_NOT_SUPPORTED;
+ }
LoRaMacPrimitives = primitives;
LoRaMacCallbacks = callbacks;
+ LoRaMacRegion = region;
LoRaMacFlags.Value = 0;
@@ -3377,102 +2688,80 @@
JoinRequestTrials = 0;
MaxJoinRequestTrials = 1;
- RepeaterSupport = false;
// Reset duty cycle times
AggregatedLastTxDoneTime = 0;
AggregatedTimeOff = 0;
- // Duty cycle
-#if defined( USE_BAND_433 )
- DutyCycleOn = true;
-#elif defined( USE_BAND_470 )
- DutyCycleOn = false;
-#elif defined( USE_BAND_780 )
- DutyCycleOn = true;
-#elif defined( USE_BAND_868 )
- DutyCycleOn = true;
-#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- DutyCycleOn = false;
-#else
- #error "Please define a frequency band in the compiler options."
-#endif
-
// Reset to defaults
- LoRaMacParamsDefaults.ChannelsTxPower = LORAMAC_DEFAULT_TX_POWER;
- LoRaMacParamsDefaults.ChannelsDatarate = LORAMAC_DEFAULT_DATARATE;
-
+ getPhy.Attribute = PHY_DUTY_CYCLE;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ DutyCycleOn = ( bool ) phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_TX_POWER;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.ChannelsTxPower = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_TX_DR;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.ChannelsDatarate = phyParam.Value;
+
+ getPhy.Attribute = PHY_MAX_RX_WINDOW;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.MaxRxWindow = phyParam.Value;
+
+ getPhy.Attribute = PHY_RECEIVE_DELAY1;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.ReceiveDelay1 = phyParam.Value;
+
+ getPhy.Attribute = PHY_RECEIVE_DELAY2;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.ReceiveDelay2 = phyParam.Value;
+
+ getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY1;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.JoinAcceptDelay1 = phyParam.Value;
+
+ getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY2;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.JoinAcceptDelay2 = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_DR1_OFFSET;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.Rx1DrOffset = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_RX2_FREQUENCY;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.Rx2Channel.Frequency = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_RX2_DR;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.Rx2Channel.Datarate = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_UPLINK_DWELL_TIME;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.UplinkDwellTime = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_DOWNLINK_DWELL_TIME;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.DownlinkDwellTime = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_MAX_EIRP;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.MaxEirp = phyParam.fValue;
+
+ getPhy.Attribute = PHY_DEF_ANTENNA_GAIN;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ LoRaMacParamsDefaults.AntennaGain = phyParam.fValue;
+
+ RegionInitDefaults( LoRaMacRegion, INIT_TYPE_INIT );
+
+ // Init parameters which are not set in function ResetMacParameters
+ LoRaMacParams.RepeaterSupport = false;
+ LoRaMacParamsDefaults.ChannelsNbRep = 1;
LoRaMacParamsDefaults.SystemMaxRxError = 10;
LoRaMacParamsDefaults.MinRxSymbols = 6;
- LoRaMacParamsDefaults.MaxRxWindow = MAX_RX_WINDOW;
- LoRaMacParamsDefaults.ReceiveDelay1 = RECEIVE_DELAY1;
- LoRaMacParamsDefaults.ReceiveDelay2 = RECEIVE_DELAY2;
- LoRaMacParamsDefaults.JoinAcceptDelay1 = JOIN_ACCEPT_DELAY1;
- LoRaMacParamsDefaults.JoinAcceptDelay2 = JOIN_ACCEPT_DELAY2;
-
- LoRaMacParamsDefaults.ChannelsNbRep = 1;
- LoRaMacParamsDefaults.Rx1DrOffset = 0;
-
- LoRaMacParamsDefaults.Rx2Channel = ( Rx2ChannelParams_t )RX_WND_2_CHANNEL;
-
- // Channel mask
-#if defined( USE_BAND_433 )
- LoRaMacParamsDefaults.ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
-#elif defined ( USE_BAND_470 )
- LoRaMacParamsDefaults.ChannelsMask[0] = 0xFFFF;
- LoRaMacParamsDefaults.ChannelsMask[1] = 0xFFFF;
- LoRaMacParamsDefaults.ChannelsMask[2] = 0xFFFF;
- LoRaMacParamsDefaults.ChannelsMask[3] = 0xFFFF;
- LoRaMacParamsDefaults.ChannelsMask[4] = 0xFFFF;
- LoRaMacParamsDefaults.ChannelsMask[5] = 0xFFFF;
-#elif defined( USE_BAND_780 )
- LoRaMacParamsDefaults.ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
-#elif defined( USE_BAND_868 )
- LoRaMacParamsDefaults.ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
-#elif defined( USE_BAND_915 )
- LoRaMacParamsDefaults.ChannelsMask[0] = 0xFFFF;
- LoRaMacParamsDefaults.ChannelsMask[1] = 0xFFFF;
- LoRaMacParamsDefaults.ChannelsMask[2] = 0xFFFF;
- LoRaMacParamsDefaults.ChannelsMask[3] = 0xFFFF;
- LoRaMacParamsDefaults.ChannelsMask[4] = 0x00FF;
- LoRaMacParamsDefaults.ChannelsMask[5] = 0x0000;
-#elif defined( USE_BAND_915_HYBRID )
- LoRaMacParamsDefaults.ChannelsMask[0] = 0x00FF;
- LoRaMacParamsDefaults.ChannelsMask[1] = 0x0000;
- LoRaMacParamsDefaults.ChannelsMask[2] = 0x0000;
- LoRaMacParamsDefaults.ChannelsMask[3] = 0x0000;
- LoRaMacParamsDefaults.ChannelsMask[4] = 0x0001;
- LoRaMacParamsDefaults.ChannelsMask[5] = 0x0000;
-#else
- #error "Please define a frequency band in the compiler options."
-#endif
-
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- // 125 kHz channels
- for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS - 8; i++ )
- {
- Channels[i].Frequency = 902.3e6 + i * 200e3;
- Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0;
- Channels[i].Band = 0;
- }
- // 500 kHz channels
- for( uint8_t i = LORA_MAX_NB_CHANNELS - 8; i < LORA_MAX_NB_CHANNELS; i++ )
- {
- Channels[i].Frequency = 903.0e6 + ( i - ( LORA_MAX_NB_CHANNELS - 8 ) ) * 1.6e6;
- Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4;
- Channels[i].Band = 0;
- }
-#elif defined( USE_BAND_470 )
- // 125 kHz channels
- for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS; i++ )
- {
- Channels[i].Frequency = 470.3e6 + i * 200e3;
- Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0;
- Channels[i].Band = 0;
- }
-#endif
-
- // Init parameters which are not set in function ResetMacParameters
+
LoRaMacParams.SystemMaxRxError = LoRaMacParamsDefaults.SystemMaxRxError;
LoRaMacParams.MinRxSymbols = LoRaMacParamsDefaults.MinRxSymbols;
LoRaMacParams.MaxRxWindow = LoRaMacParamsDefaults.MaxRxWindow;
@@ -3511,12 +2800,39 @@
Radio.SetPublicNetwork( PublicNetwork );
Radio.Sleep( );
+ // Initialize class b
+ // Apply callback
+ classBCallbacks.GetTemperatureLevel = NULL;
+ if( callbacks != NULL )
+ {
+ classBCallbacks.GetTemperatureLevel = callbacks->GetTemperatureLevel;
+ }
+ classBCallbacks.GetMlmeConfrimIndex = GetMlmeConfirmIndex;
+
+ // Must all be static. Don't use local references.
+ classBParams.MlmeIndication = &MlmeIndication;
+ classBParams.McpsIndication = &McpsIndication;
+ classBParams.MlmeConfirm = &MlmeConfirm;
+ classBParams.LoRaMacFlags = &LoRaMacFlags;
+ classBParams.LoRaMacDevAddr = &LoRaMacDevAddr;
+ classBParams.MlmeConfirmQueue = MlmeConfirmQueue;
+ classBParams.LoRaMacRegion = &LoRaMacRegion;
+ classBParams.MacStateCheckTimer = &MacStateCheckTimer;
+ classBParams.LoRaMacParams = &LoRaMacParams;
+ classBParams.MulticastChannels = MulticastChannels;
+
+ LoRaMacClassBInit( &classBParams, &classBCallbacks );
+
return LORAMAC_STATUS_OK;
}
LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo )
{
+ AdrNextParams_t adrNext;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
int8_t datarate = LoRaMacParamsDefaults.ChannelsDatarate;
+ int8_t txPower = LoRaMacParamsDefaults.ChannelsTxPower;
uint8_t fOptLen = MacCommandsBufferIndex + MacCommandsBufferToRepeatIndex;
if( txInfo == NULL )
@@ -3524,42 +2840,59 @@
return LORAMAC_STATUS_PARAMETER_INVALID;
}
- AdrNextDr( AdrCtrlOn, false, &datarate );
-
- if( RepeaterSupport == true )
+ // Setup ADR request
+ adrNext.UpdateChanMask = false;
+ adrNext.AdrEnabled = AdrCtrlOn;
+ adrNext.AdrAckCounter = AdrAckCounter;
+ adrNext.Datarate = LoRaMacParams.ChannelsDatarate;
+ adrNext.TxPower = LoRaMacParams.ChannelsTxPower;
+ adrNext.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+
+ // We call the function for information purposes only. We don't want to
+ // apply the datarate, the tx power and the ADR ack counter.
+ RegionAdrNext( LoRaMacRegion, &adrNext, &datarate, &txPower, &AdrAckCounter );
+
+ // Setup PHY request
+ getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+ getPhy.Datarate = datarate;
+ getPhy.Attribute = PHY_MAX_PAYLOAD;
+
+ // Change request in case repeater is supported
+ if( LoRaMacParams.RepeaterSupport == true )
{
- txInfo->CurrentPayloadSize = MaxPayloadOfDatarateRepeater[datarate];
+ getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER;
}
- else
- {
- txInfo->CurrentPayloadSize = MaxPayloadOfDatarate[datarate];
- }
-
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ txInfo->CurrentPayloadSize = phyParam.Value;
+
+ // Verify if the fOpts fit into the maximum payload
if( txInfo->CurrentPayloadSize >= fOptLen )
{
txInfo->MaxPossiblePayload = txInfo->CurrentPayloadSize - fOptLen;
}
else
{
- return LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR;
+ txInfo->MaxPossiblePayload = txInfo->CurrentPayloadSize;
+ // The fOpts don't fit into the maximum payload. Omit the MAC commands to
+ // ensure that another uplink is possible.
+ fOptLen = 0;
+ MacCommandsBufferIndex = 0;
+ MacCommandsBufferToRepeatIndex = 0;
}
- if( ValidatePayloadLength( size, datarate, 0 ) == false )
+ // Verify if the fOpts and the payload fit into the maximum payload
+ if( ValidatePayloadLength( size, datarate, fOptLen ) == false )
{
return LORAMAC_STATUS_LENGTH_ERROR;
}
-
- if( ValidatePayloadLength( size, datarate, fOptLen ) == false )
- {
- return LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR;
- }
-
return LORAMAC_STATUS_OK;
}
LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet )
{
LoRaMacStatus_t status = LORAMAC_STATUS_OK;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
if( mibGet == NULL )
{
@@ -3610,12 +2943,15 @@
}
case MIB_REPEATER_SUPPORT:
{
- mibGet->Param.EnableRepeaterSupport = RepeaterSupport;
+ mibGet->Param.EnableRepeaterSupport = LoRaMacParams.RepeaterSupport;
break;
}
case MIB_CHANNELS:
{
- mibGet->Param.ChannelList = Channels;
+ getPhy.Attribute = PHY_CHANNELS;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+
+ mibGet->Param.ChannelList = phyParam.Channels;
break;
}
case MIB_RX2_CHANNEL:
@@ -3630,12 +2966,18 @@
}
case MIB_CHANNELS_DEFAULT_MASK:
{
- mibGet->Param.ChannelsDefaultMask = LoRaMacParamsDefaults.ChannelsMask;
+ getPhy.Attribute = PHY_CHANNELS_DEFAULT_MASK;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+
+ mibGet->Param.ChannelsDefaultMask = phyParam.ChannelsMask;
break;
}
case MIB_CHANNELS_MASK:
{
- mibGet->Param.ChannelsMask = LoRaMacParams.ChannelsMask;
+ getPhy.Attribute = PHY_CHANNELS_MASK;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+
+ mibGet->Param.ChannelsMask = phyParam.ChannelsMask;
break;
}
case MIB_CHANNELS_NB_REP:
@@ -3713,17 +3055,32 @@
mibGet->Param.MinRxSymbols = LoRaMacParams.MinRxSymbols;
break;
}
- default:
- status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+ case MIB_ANTENNA_GAIN:
+ {
+ mibGet->Param.AntennaGain = LoRaMacParams.AntennaGain;
break;
+ }
+ default:
+ {
+ if( LoRaMacDeviceClass == CLASS_B )
+ {
+ status = LoRaMacClassBMibGetRequestConfirm( mibGet );
+ }
+ else
+ {
+ status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+ }
+ break;
+ }
}
-
return status;
}
LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet )
{
LoRaMacStatus_t status = LORAMAC_STATUS_OK;
+ ChanMaskSetParams_t chanMaskSet;
+ VerifyParams_t verify;
if( mibSet == NULL )
{
@@ -3738,27 +3095,7 @@
{
case MIB_DEVICE_CLASS:
{
- LoRaMacDeviceClass = mibSet->Param.Class;
- switch( LoRaMacDeviceClass )
- {
- case CLASS_A:
- {
- // Set the radio into sleep to setup a defined state
- Radio.Sleep( );
- break;
- }
- case CLASS_B:
- {
- break;
- }
- case CLASS_C:
- {
- // Set the NodeAckRequested indicator to default
- NodeAckRequested = false;
- OnRxWindow2TimerEvent( );
- break;
- }
- }
+ status = SwitchClass( mibSet->Param.Class );
break;
}
case MIB_NETWORK_JOINED:
@@ -3815,58 +3152,44 @@
}
case MIB_REPEATER_SUPPORT:
{
- RepeaterSupport = mibSet->Param.EnableRepeaterSupport;
+ LoRaMacParams.RepeaterSupport = mibSet->Param.EnableRepeaterSupport;
break;
}
case MIB_RX2_CHANNEL:
{
- LoRaMacParams.Rx2Channel = mibSet->Param.Rx2Channel;
- break;
- }
- case MIB_RX2_DEFAULT_CHANNEL:
- {
- LoRaMacParamsDefaults.Rx2Channel = mibSet->Param.Rx2DefaultChannel;
- break;
- }
- case MIB_CHANNELS_DEFAULT_MASK:
- {
- if( mibSet->Param.ChannelsDefaultMask )
+ verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate;
+ verify.DatarateParams.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime;
+
+ if( RegionVerify( LoRaMacRegion, &verify, PHY_RX_DR ) == true )
{
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- bool chanMaskState = true;
-
-#if defined( USE_BAND_915_HYBRID )
- chanMaskState = ValidateChannelMask( mibSet->Param.ChannelsDefaultMask );
-#endif
- if( chanMaskState == true )
+ LoRaMacParams.Rx2Channel = mibSet->Param.Rx2Channel;
+
+ if( ( LoRaMacDeviceClass == CLASS_C ) && ( IsLoRaMacNetworkJoined == true ) )
{
- if( ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) < 2 ) &&
- ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) > 0 ) )
+ // Compute Rx2 windows parameters
+ RegionComputeRxWindowParameters( LoRaMacRegion,
+ LoRaMacParams.Rx2Channel.Datarate,
+ LoRaMacParams.MinRxSymbols,
+ LoRaMacParams.SystemMaxRxError,
+ &RxWindow2Config );
+
+ RxWindow2Config.Channel = Channel;
+ RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency;
+ RxWindow2Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime;
+ RxWindow2Config.RepeaterSupport = LoRaMacParams.RepeaterSupport;
+ RxWindow2Config.Window = 1;
+ RxWindow2Config.RxContinuous = true;
+
+ if( RegionRxConfig( LoRaMacRegion, &RxWindow2Config, ( int8_t* )&McpsIndication.RxDatarate ) == true )
+ {
+ RxWindowSetup( RxWindow2Config.RxContinuous, LoRaMacParams.MaxRxWindow );
+ RxSlot = RxWindow2Config.Window;
+ }
+ else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
- else
- {
- memcpy1( ( uint8_t* ) LoRaMacParamsDefaults.ChannelsMask,
- ( uint8_t* ) mibSet->Param.ChannelsDefaultMask, sizeof( LoRaMacParamsDefaults.ChannelsMask ) );
- for ( uint8_t i = 0; i < sizeof( LoRaMacParamsDefaults.ChannelsMask ) / 2; i++ )
- {
- // Disable channels which are no longer available
- ChannelsMaskRemaining[i] &= LoRaMacParamsDefaults.ChannelsMask[i];
- }
- }
}
- else
- {
- status = LORAMAC_STATUS_PARAMETER_INVALID;
- }
-#elif defined( USE_BAND_470 )
- memcpy1( ( uint8_t* ) LoRaMacParamsDefaults.ChannelsMask,
- ( uint8_t* ) mibSet->Param.ChannelsDefaultMask, sizeof( LoRaMacParamsDefaults.ChannelsMask ) );
-#else
- memcpy1( ( uint8_t* ) LoRaMacParamsDefaults.ChannelsMask,
- ( uint8_t* ) mibSet->Param.ChannelsDefaultMask, 2 );
-#endif
}
else
{
@@ -3874,47 +3197,38 @@
}
break;
}
+ case MIB_RX2_DEFAULT_CHANNEL:
+ {
+ verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate;
+ verify.DatarateParams.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime;
+
+ if( RegionVerify( LoRaMacRegion, &verify, PHY_RX_DR ) == true )
+ {
+ LoRaMacParamsDefaults.Rx2Channel = mibSet->Param.Rx2DefaultChannel;
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_CHANNELS_DEFAULT_MASK:
+ {
+ chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask;
+ chanMaskSet.ChannelsMaskType = CHANNELS_DEFAULT_MASK;
+
+ if( RegionChanMaskSet( LoRaMacRegion, &chanMaskSet ) == false )
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
case MIB_CHANNELS_MASK:
{
- if( mibSet->Param.ChannelsMask )
- {
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- bool chanMaskState = true;
-
-#if defined( USE_BAND_915_HYBRID )
- chanMaskState = ValidateChannelMask( mibSet->Param.ChannelsMask );
-#endif
- if( chanMaskState == true )
- {
- if( ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) < 2 ) &&
- ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) > 0 ) )
- {
- status = LORAMAC_STATUS_PARAMETER_INVALID;
- }
- else
- {
- memcpy1( ( uint8_t* ) LoRaMacParams.ChannelsMask,
- ( uint8_t* ) mibSet->Param.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
- for ( uint8_t i = 0; i < sizeof( LoRaMacParams.ChannelsMask ) / 2; i++ )
- {
- // Disable channels which are no longer available
- ChannelsMaskRemaining[i] &= LoRaMacParams.ChannelsMask[i];
- }
- }
- }
- else
- {
- status = LORAMAC_STATUS_PARAMETER_INVALID;
- }
-#elif defined( USE_BAND_470 )
- memcpy1( ( uint8_t* ) LoRaMacParams.ChannelsMask,
- ( uint8_t* ) mibSet->Param.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-#else
- memcpy1( ( uint8_t* ) LoRaMacParams.ChannelsMask,
- ( uint8_t* ) mibSet->Param.ChannelsMask, 2 );
-#endif
- }
- else
+ chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask;
+ chanMaskSet.ChannelsMaskType = CHANNELS_MASK;
+
+ if( RegionChanMaskSet( LoRaMacRegion, &chanMaskSet ) == false )
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
@@ -3960,19 +3274,12 @@
}
case MIB_CHANNELS_DEFAULT_DATARATE:
{
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
- if( ValueInRange( mibSet->Param.ChannelsDefaultDatarate,
- DR_0, DR_5 ) )
+ verify.DatarateParams.Datarate = mibSet->Param.ChannelsDefaultDatarate;
+
+ if( RegionVerify( LoRaMacRegion, &verify, PHY_DEF_TX_DR ) == true )
{
- LoRaMacParamsDefaults.ChannelsDatarate = mibSet->Param.ChannelsDefaultDatarate;
+ LoRaMacParamsDefaults.ChannelsDatarate = verify.DatarateParams.Datarate;
}
-#else
- if( ValueInRange( mibSet->Param.ChannelsDefaultDatarate,
- LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) )
- {
- LoRaMacParamsDefaults.ChannelsDatarate = mibSet->Param.ChannelsDefaultDatarate;
- }
-#endif
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
@@ -3981,10 +3288,11 @@
}
case MIB_CHANNELS_DATARATE:
{
- if( ValueInRange( mibSet->Param.ChannelsDatarate,
- LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) )
+ verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate;
+
+ if( RegionVerify( LoRaMacRegion, &verify, PHY_TX_DR ) == true )
{
- LoRaMacParams.ChannelsDatarate = mibSet->Param.ChannelsDatarate;
+ LoRaMacParams.ChannelsDatarate = verify.DatarateParams.Datarate;
}
else
{
@@ -3994,10 +3302,11 @@
}
case MIB_CHANNELS_DEFAULT_TX_POWER:
{
- if( ValueInRange( mibSet->Param.ChannelsDefaultTxPower,
- LORAMAC_MAX_TX_POWER, LORAMAC_MIN_TX_POWER ) )
+ verify.TxPower = mibSet->Param.ChannelsDefaultTxPower;
+
+ if( RegionVerify( LoRaMacRegion, &verify, PHY_DEF_TX_POWER ) == true )
{
- LoRaMacParamsDefaults.ChannelsTxPower = mibSet->Param.ChannelsDefaultTxPower;
+ LoRaMacParamsDefaults.ChannelsTxPower = verify.TxPower;
}
else
{
@@ -4007,10 +3316,11 @@
}
case MIB_CHANNELS_TX_POWER:
{
- if( ValueInRange( mibSet->Param.ChannelsTxPower,
- LORAMAC_MAX_TX_POWER, LORAMAC_MIN_TX_POWER ) )
+ verify.TxPower = mibSet->Param.ChannelsTxPower;
+
+ if( RegionVerify( LoRaMacRegion, &verify, PHY_TX_POWER ) == true )
{
- LoRaMacParams.ChannelsTxPower = mibSet->Param.ChannelsTxPower;
+ LoRaMacParams.ChannelsTxPower = verify.TxPower;
}
else
{
@@ -4038,9 +3348,23 @@
LoRaMacParams.MinRxSymbols = LoRaMacParamsDefaults.MinRxSymbols = mibSet->Param.MinRxSymbols;
break;
}
+ case MIB_ANTENNA_GAIN:
+ {
+ LoRaMacParams.AntennaGain = mibSet->Param.AntennaGain;
+ break;
+ }
default:
- status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+ {
+ if( LoRaMacDeviceClass == CLASS_B )
+ {
+ status = LoRaMacMibClassBSetRequestConfirm( mibSet );
+ }
+ else
+ {
+ status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+ }
break;
+ }
}
return status;
@@ -4048,18 +3372,8 @@
LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params )
{
-#if defined( USE_BAND_470 ) || defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- return LORAMAC_STATUS_PARAMETER_INVALID;
-#else
- bool datarateInvalid = false;
- bool frequencyInvalid = false;
- uint8_t band = 0;
-
- // The id must not exceed LORA_MAX_NB_CHANNELS
- if( id >= LORA_MAX_NB_CHANNELS )
- {
- return LORAMAC_STATUS_PARAMETER_INVALID;
- }
+ ChannelAddParams_t channelAdd;
+
// Validate if the MAC is in a correct state
if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
{
@@ -4068,99 +3382,17 @@
return LORAMAC_STATUS_BUSY;
}
}
- // Validate the datarate
- if( ( params.DrRange.Fields.Min > params.DrRange.Fields.Max ) ||
- ( ValueInRange( params.DrRange.Fields.Min, LORAMAC_TX_MIN_DATARATE,
- LORAMAC_TX_MAX_DATARATE ) == false ) ||
- ( ValueInRange( params.DrRange.Fields.Max, LORAMAC_TX_MIN_DATARATE,
- LORAMAC_TX_MAX_DATARATE ) == false ) )
- {
- datarateInvalid = true;
- }
-
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
- if( id < 3 )
- {
- if( params.Frequency != Channels[id].Frequency )
- {
- frequencyInvalid = true;
- }
-
- if( params.DrRange.Fields.Min > DR_0 )
- {
- datarateInvalid = true;
- }
- if( ValueInRange( params.DrRange.Fields.Max, DR_5, LORAMAC_TX_MAX_DATARATE ) == false )
- {
- datarateInvalid = true;
- }
- }
-#endif
-
- // Validate the frequency
- if( ( Radio.CheckRfFrequency( params.Frequency ) == true ) && ( params.Frequency > 0 ) && ( frequencyInvalid == false ) )
- {
-#if defined( USE_BAND_868 )
- if( ( params.Frequency >= 863000000 ) && ( params.Frequency < 865000000 ) )
- {
- band = BAND_G1_2;
- }
- else if( ( params.Frequency >= 865000000 ) && ( params.Frequency <= 868000000 ) )
- {
- band = BAND_G1_0;
- }
- else if( ( params.Frequency > 868000000 ) && ( params.Frequency <= 868600000 ) )
- {
- band = BAND_G1_1;
- }
- else if( ( params.Frequency >= 868700000 ) && ( params.Frequency <= 869200000 ) )
- {
- band = BAND_G1_2;
- }
- else if( ( params.Frequency >= 869400000 ) && ( params.Frequency <= 869650000 ) )
- {
- band = BAND_G1_3;
- }
- else if( ( params.Frequency >= 869700000 ) && ( params.Frequency <= 870000000 ) )
- {
- band = BAND_G1_4;
- }
- else
- {
- frequencyInvalid = true;
- }
-#endif
- }
- else
- {
- frequencyInvalid = true;
- }
-
- if( ( datarateInvalid == true ) && ( frequencyInvalid == true ) )
- {
- return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
- }
- if( datarateInvalid == true )
- {
- return LORAMAC_STATUS_DATARATE_INVALID;
- }
- if( frequencyInvalid == true )
- {
- return LORAMAC_STATUS_FREQUENCY_INVALID;
- }
-
- // Every parameter is valid, activate the channel
- Channels[id] = params;
- Channels[id].Band = band;
- LoRaMacParams.ChannelsMask[0] |= ( 1 << id );
-
- return LORAMAC_STATUS_OK;
-#endif
+
+ channelAdd.NewChannel = ¶ms;
+ channelAdd.ChannelId = id;
+
+ return RegionChannelAdd( LoRaMacRegion, &channelAdd );
}
LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id )
{
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
+ ChannelRemoveParams_t channelRemove;
+
if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
{
if( ( LoRaMacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG )
@@ -4169,25 +3401,13 @@
}
}
- if( ( id < 3 ) || ( id >= LORA_MAX_NB_CHANNELS ) )
+ channelRemove.ChannelId = id;
+
+ if( RegionChannelsRemove( LoRaMacRegion, &channelRemove ) == false )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
- else
- {
- // Remove the channel from the list of channels
- Channels[id] = ( ChannelParams_t ){ 0, { 0 }, 0 };
-
- // Disable the channel as it doesn't exist anymore
- if( DisableChannelInMask( id, LoRaMacParams.ChannelsMask ) == false )
- {
- return LORAMAC_STATUS_PARAMETER_INVALID;
- }
- }
return LORAMAC_STATUS_OK;
-#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) || defined( USE_BAND_470 ) )
- return LORAMAC_STATUS_PARAMETER_INVALID;
-#endif
}
LoRaMacStatus_t LoRaMacMulticastChannelLink( MulticastParams_t *channelParam )
@@ -4203,6 +3423,7 @@
// Reset downlink counter
channelParam->DownLinkCounter = 0;
+ channelParam->Next = NULL;
if( MulticastChannels == NULL )
{
@@ -4268,6 +3489,10 @@
{
LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
LoRaMacHeader_t macHdr;
+ AlternateDrParams_t altDr;
+ VerifyParams_t verify;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
if( mlmeRequest == NULL )
{
@@ -4277,11 +3502,18 @@
{
return LORAMAC_STATUS_BUSY;
}
-
- memset1( ( uint8_t* ) &MlmeConfirm, 0, sizeof( MlmeConfirm ) );
-
- MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
-
+ if( LORA_MAC_MLME_CONFIRM_QUEUE_LEN <= MlmeConfirmQueueCnt )
+ {
+ return LORAMAC_STATUS_BUSY;
+ }
+
+ // Reset the confirm queue if it is empty
+ if( MlmeConfirmQueueCnt == 0 )
+ {
+ memset1( ( uint8_t* ) &MlmeConfirm, 0, sizeof( MlmeConfirm ) );
+ }
+
+ // Switch requests
switch( mlmeRequest->Type )
{
case MLME_JOIN:
@@ -4299,22 +3531,21 @@
return LORAMAC_STATUS_PARAMETER_INVALID;
}
-#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
- // Enables at least the usage of the 2 datarates.
- if( mlmeRequest->Req.Join.NbTrials < 2 )
+ // Apply the request
+ LoRaMacFlags.Bits.MlmeReq = 1;
+ MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+ MlmeConfirmQueueCnt++;
+
+ // Verify the parameter NbTrials for the join procedure
+ verify.NbJoinTrials = mlmeRequest->Req.Join.NbTrials;
+
+ if( RegionVerify( LoRaMacRegion, &verify, PHY_NB_JOIN_TRIALS ) == false )
{
- mlmeRequest->Req.Join.NbTrials = 2;
+ // Value not supported, get default
+ getPhy.Attribute = PHY_DEF_NB_JOIN_TRIALS;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ mlmeRequest->Req.Join.NbTrials = ( uint8_t ) phyParam.Value;
}
-#else
- // Enables at least the usage of all datarates.
- if( mlmeRequest->Req.Join.NbTrials < 48 )
- {
- mlmeRequest->Req.Join.NbTrials = 48;
- }
-#endif
-
- LoRaMacFlags.Bits.MlmeReq = 1;
- MlmeConfirm.MlmeRequest = mlmeRequest->Type;
LoRaMacDevEui = mlmeRequest->Req.Join.DevEui;
LoRaMacAppEui = mlmeRequest->Req.Join.AppEui;
@@ -4330,33 +3561,85 @@
ResetMacParameters( );
- // Add a +1, since we start to count from 0
- LoRaMacParams.ChannelsDatarate = AlternateDatarate( JoinRequestTrials + 1 );
+ altDr.NbTrials = JoinRequestTrials + 1;
+
+ LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, &altDr );
status = Send( &macHdr, 0, NULL, 0 );
break;
}
case MLME_LINK_CHECK:
{
+ // Apply the request
LoRaMacFlags.Bits.MlmeReq = 1;
+ MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+ MlmeConfirmQueueCnt++;
+
// LoRaMac will send this command piggy-pack
- MlmeConfirm.MlmeRequest = mlmeRequest->Type;
-
status = AddMacCommand( MOTE_MAC_LINK_CHECK_REQ, 0, 0 );
break;
}
case MLME_TXCW:
{
- MlmeConfirm.MlmeRequest = mlmeRequest->Type;
+ // Apply the request
LoRaMacFlags.Bits.MlmeReq = 1;
+ MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+ MlmeConfirmQueueCnt++;
status = SetTxContinuousWave( mlmeRequest->Req.TxCw.Timeout );
break;
}
case MLME_TXCW_1:
{
- MlmeConfirm.MlmeRequest = mlmeRequest->Type;
+ // Apply the request
+ LoRaMacFlags.Bits.MlmeReq = 1;
+ MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+ MlmeConfirmQueueCnt++;
+ status = SetTxContinuousWave1( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power );
+ break;
+ }
+ case MLME_PING_SLOT_INFO:
+ {
+ uint8_t value = mlmeRequest->Req.PingSlotInfo.PingSlot.Value;
+
+ // Apply the request
+ LoRaMacFlags.Bits.MlmeReq = 1;
+ MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+ MlmeConfirmQueueCnt++;
+ // LoRaMac will send this command piggy-pack
+ LoRaMacClassBSetPingSlotInfo( mlmeRequest->Req.PingSlotInfo.PingSlot.Fields.Periodicity );
+
+ status = AddMacCommand( MOTE_MAC_PING_SLOT_INFO_REQ, value, 0 );
+ break;
+ }
+ case MLME_BEACON_TIMING:
+ {
+ // Apply the request
LoRaMacFlags.Bits.MlmeReq = 1;
- status = SetTxContinuousWave1( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power );
+ MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+ MlmeConfirmQueueCnt++;
+ // LoRaMac will send this command piggy-pack
+ status = AddMacCommand( MOTE_MAC_BEACON_TIMING_REQ, 0, 0 );
+ break;
+ }
+ case MLME_BEACON_ACQUISITION:
+ {
+ // Apply the request
+ LoRaMacFlags.Bits.MlmeReq = 1;
+ MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+ MlmeConfirmQueueCnt++;
+
+ if( ( LoRaMacClassBIsAcquisitionPending( ) == false ) && ( LoRaMacClassBIsAcquisitionTimerSet( ) == false ) )
+ {
+ // Start class B algorithm
+ LoRaMacClassBSetBeaconState( BEACON_STATE_ACQUISITION );
+ LoRaMacClassBBeaconTimerEvent( );
+
+ status = LORAMAC_STATUS_OK;
+ }
+ else
+ {
+ status = LORAMAC_STATUS_BUSY;
+ }
break;
}
default:
@@ -4366,7 +3649,11 @@
if( status != LORAMAC_STATUS_OK )
{
NodeAckRequested = false;
- LoRaMacFlags.Bits.MlmeReq = 0;
+ MlmeConfirmQueueCnt--;
+ if( MlmeConfirmQueueCnt == 0 )
+ {
+ LoRaMacFlags.Bits.MlmeReq = 0;
+ }
}
return status;
@@ -4374,8 +3661,11 @@
LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest )
{
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
LoRaMacHeader_t macHdr;
+ VerifyParams_t verify;
uint8_t fPort = 0;
void *fBuffer;
uint16_t fBufferSize;
@@ -4396,6 +3686,9 @@
memset1 ( ( uint8_t* ) &McpsConfirm, 0, sizeof( McpsConfirm ) );
McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+ // AckTimeoutRetriesCounter must be reset every time a new request (unconfirmed or confirmed) is performed.
+ AckTimeoutRetriesCounter = 1;
+
switch( mcpsRequest->Type )
{
case MCPS_UNCONFIRMED:
@@ -4413,7 +3706,6 @@
case MCPS_CONFIRMED:
{
readyToSend = true;
- AckTimeoutRetriesCounter = 1;
AckTimeoutRetries = mcpsRequest->Req.Confirmed.NbTrials;
macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP;
@@ -4438,13 +3730,24 @@
break;
}
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+ phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+ // Apply the minimum possible datarate.
+ // Some regions have limitations for the minimum datarate.
+ datarate = MAX( datarate, phyParam.Value );
+
if( readyToSend == true )
{
if( AdrCtrlOn == false )
{
- if( ValueInRange( datarate, LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) == true )
+ verify.DatarateParams.Datarate = datarate;
+ verify.DatarateParams.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+
+ if( RegionVerify( LoRaMacRegion, &verify, PHY_TX_DR ) == true )
{
- LoRaMacParams.ChannelsDatarate = datarate;
+ LoRaMacParams.ChannelsDatarate = verify.DatarateParams.Datarate;
}
else
{
@@ -4480,52 +3783,17 @@
void LoRaMacTestSetDutyCycleOn( bool enable )
{
-#if ( defined( USE_BAND_868 ) || defined( USE_BAND_433 ) || defined( USE_BAND_780 ) )
- DutyCycleOn = enable;
-#else
- DutyCycleOn = false;
-#endif
+ VerifyParams_t verify;
+
+ verify.DutyCycle = enable;
+
+ if( RegionVerify( LoRaMacRegion, &verify, PHY_DUTY_CYCLE ) == true )
+ {
+ DutyCycleOn = enable;
+ }
}
void LoRaMacTestSetChannel( uint8_t channel )
{
Channel = channel;
}
-
-static RxConfigParams_t ComputeRxWindowParameters( int8_t datarate, uint32_t rxError )
-{
- RxConfigParams_t rxConfigParams = { 0, 0, 0, 0 };
- double tSymbol = 0.0;
-
- rxConfigParams.Datarate = datarate;
- switch( Bandwidths[datarate] )
- {
- default:
- case 125000:
- rxConfigParams.Bandwidth = 0;
- break;
- case 250000:
- rxConfigParams.Bandwidth = 1;
- break;
- case 500000:
- rxConfigParams.Bandwidth = 2;
- break;
- }
-
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
- if( datarate == DR_7 )
- { // FSK
- tSymbol = ( 1.0 / ( double )Datarates[datarate] ) * 8.0; // 1 symbol equals 1 byte
- }
- else
-#endif
- { // LoRa
- tSymbol = ( ( double )( 1 << Datarates[datarate] ) / ( double )Bandwidths[datarate] ) * 1e3;
- }
-
- rxConfigParams.RxWindowTimeout = MAX( ( uint32_t )ceil( ( ( 2 * LoRaMacParams.MinRxSymbols - 8 ) * tSymbol + 2 * rxError ) / tSymbol ), LoRaMacParams.MinRxSymbols ); // Computed number of symbols
-
- rxConfigParams.RxOffset = ( int32_t )ceil( ( 4.0 * tSymbol ) - ( ( rxConfigParams.RxWindowTimeout * tSymbol ) / 2.0 ) - RADIO_WAKEUP_TIME );
-
- return rxConfigParams;
-}
--- a/LoRaMac.h Tue Aug 08 19:27:17 2017 -0400
+++ b/LoRaMac.h Wed Aug 09 16:20:21 2017 -0400
@@ -26,7 +26,7 @@
*
* \author Gregory Cristian ( Semtech )
*
- * \author Daniel Jäckle ( STACKFORCE )
+ * \author Daniel Jaeckle ( STACKFORCE )
*
* \defgroup LORAMAC LoRa MAC layer implementation
* This module specifies the API implementation of the LoRaMAC layer.
@@ -42,79 +42,51 @@
*
* \example classC/LoRaMote/main.c
* LoRaWAN class C application example for the LoRaMote.
+ *
+ * \example classA/MoteII/main.c
+ * LoRaWAN class A application example for the MoteII.
+ *
+ * \example classB/MoteII/main.c
+ * LoRaWAN class B application example for the MoteII.
+ *
+ * \example classC/MoteII/main.c
+ * LoRaWAN class C application example for the MoteII.
+ *
+ * \example classA/NAMote72/main.c
+ * LoRaWAN class A application example for the NAMote72.
+ *
+ * \example classB/NAMote72/main.c
+ * LoRaWAN class B application example for the NAMote72.
+ *
+ * \example classC/NAMote72/main.c
+ * LoRaWAN class C application example for the NAMote72.
+ *
+ * \example classA/SensorNode/main.c
+ * LoRaWAN class A application example for the SensorNode.
+ *
+ * \example classB/SensorNode/main.c
+ * LoRaWAN class B application example for the SensorNode.
+ *
+ * \example classC/SensorNode/main.c
+ * LoRaWAN class C application example for the SensorNode.
+ *
+ * \example classA/SK-iM880A/main.c
+ * LoRaWAN class A application example for the SK-iM880A.
+ *
+ * \example classB/SK-iM880A/main.c
+ * LoRaWAN class B application example for the SK-iM880A.
+ *
+ * \example classC/SK-iM880A/main.c
+ * LoRaWAN class C application example for the SK-iM880A.
*/
#ifndef __LORAMAC_H__
#define __LORAMAC_H__
-
-// Includes board dependent definitions such as channels frequencies
-#include "LoRaMac-definitions.h"
-
/*!
* Stack Version Information
*/
#define LORAMAC_MAJOR_VERSION 4
-#define LORAMAC_MINOR_VERSION 3
-#define LORAMAC_POINT_VERSION 2
-
-/*!
- * Beacon interval in ms
- */
-#define BEACON_INTERVAL 128000
-
-/*!
- * Class A&B receive delay 1 in ms
- */
-#define RECEIVE_DELAY1 1000
-
-/*!
- * Class A&B receive delay 2 in ms
- */
-#define RECEIVE_DELAY2 2000
-
-/*!
- * Join accept receive delay 1 in ms
- */
-#define JOIN_ACCEPT_DELAY1 5000
-
-/*!
- * Join accept receive delay 2 in ms
- */
-#define JOIN_ACCEPT_DELAY2 6000
-
-/*!
- * Class A&B maximum receive window delay in ms
- */
-#define MAX_RX_WINDOW 3000
-
-/*!
- * Maximum allowed gap for the FCNT field
- */
-#define MAX_FCNT_GAP 16384
-
-/*!
- * ADR acknowledgement counter limit
- */
-#define ADR_ACK_LIMIT 64
-
-/*!
- * Number of ADR acknowledgement requests before returning to default datarate
- */
-#define ADR_ACK_DELAY 32
-
-/*!
- * Number of seconds after the start of the second reception window without
- * receiving an acknowledge.
- * AckTimeout = \ref ACK_TIMEOUT + Random( -\ref ACK_TIMEOUT_RND, \ref ACK_TIMEOUT_RND )
- */
-#define ACK_TIMEOUT 2000
-
-/*!
- * Random number of seconds after the start of the second reception window without
- * receiving an acknowledge
- * AckTimeout = \ref ACK_TIMEOUT + Random( -\ref ACK_TIMEOUT_RND, \ref ACK_TIMEOUT_RND )
- */
-#define ACK_TIMEOUT_RND 1000
-
+#define LORAMAC_MINOR_VERSION 4
+#define LORAMAC_POINT_VERSION 0
/*!
* Check the Mac layer state every MAC_STATE_CHECK_TIMEOUT in ms
*/
@@ -126,11 +98,6 @@
#define MAX_ACK_RETRIES 8
/*!
- * RSSI free threshold [dBm]
- */
-#define RSSI_FREE_TH ( int8_t )( -90 )
-
-/*!
* Frame direction definition for up-link communications
*/
#define UP_LINK 0
@@ -147,26 +114,40 @@
#define LORAMAC_MFR_LEN 4
/*!
+ * LoRaMac MLME-Confirm queue length
+ */
+#define LORA_MAC_MLME_CONFIRM_QUEUE_LEN 5
+
+/*!
+ * FRMPayload overhead to be used when setting the Radio.SetMaxPayloadLength
+ * in RxWindowSetup function.
+ * Maximum PHYPayload = MaxPayloadOfDatarate/MaxPayloadOfDatarateRepeater + LORA_MAC_FRMPAYLOAD_OVERHEAD
+ */
+#define LORA_MAC_FRMPAYLOAD_OVERHEAD 13 // MHDR(1) + FHDR(7) + Port(1) + MIC(4)
+
+/*!
* LoRaWAN devices classes definition
+ *
+ * LoRaWAN Specification V1.0.2, chapter 2.1
*/
typedef enum eDeviceClass
{
/*!
* LoRaWAN device class A
*
- * LoRaWAN Specification V1.0.1, chapter 3ff
+ * LoRaWAN Specification V1.0.2, chapter 3
*/
CLASS_A,
/*!
* LoRaWAN device class B
*
- * LoRaWAN Specification V1.0.1, chapter 8ff
+ * LoRaWAN Specification V1.0.2, chapter 8
*/
CLASS_B,
/*!
* LoRaWAN device class C
*
- * LoRaWAN Specification V1.0.1, chapter 17ff
+ * LoRaWAN Specification V1.0.2, chapter 17
*/
CLASS_C,
}DeviceClass_t;
@@ -188,17 +169,17 @@
/*!
* Minimum data rate
*
- * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7]
+ * LoRaWAN Regional Parameters V1.0.2rB
*
- * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4]
+ * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
*/
int8_t Min : 4;
/*!
* Maximum data rate
*
- * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7]
+ * LoRaWAN Regional Parameters V1.0.2rB
*
- * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4]
+ * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
*/
int8_t Max : 4;
}Fields;
@@ -218,6 +199,10 @@
*/
int8_t TxMaxPower;
/*!
+ * Time stamp of the last JoinReq Tx frame.
+ */
+ TimerTime_t LastJoinTxDoneTime;
+ /*!
* Time stamp of the last Tx frame
*/
TimerTime_t LastTxDoneTime;
@@ -237,6 +222,10 @@
*/
uint32_t Frequency;
/*!
+ * Alternative frequency for RX window 1
+ */
+ uint32_t Rx1Frequency;
+ /*!
* Data rate definition
*/
DrRange_t DrRange;
@@ -258,9 +247,9 @@
/*!
* Data rate
*
- * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7]
+ * LoRaWAN Regional Parameters V1.0.2rB
*
- * US915 - [DR_8, DR_9, DR_10, DR_11, DR_12, DR_13]
+ * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
*/
uint8_t Datarate;
}Rx2ChannelParams_t;
@@ -322,9 +311,25 @@
*/
Rx2ChannelParams_t Rx2Channel;
/*!
- * Mask indicating which channels are enabled
+ * Uplink dwell time configuration. 0: No limit, 1: 400ms
+ */
+ uint8_t UplinkDwellTime;
+ /*!
+ * Downlink dwell time configuration. 0: No limit, 1: 400ms
+ */
+ uint8_t DownlinkDwellTime;
+ /*!
+ * Maximum possible EIRP
*/
- uint16_t ChannelsMask[6];
+ float MaxEirp;
+ /*!
+ * Antenna gain of the node
+ */
+ float AntennaGain;
+ /*!
+ * Indicates if the node supports repeaters
+ */
+ bool RepeaterSupport;
}LoRaMacParams_t;
/*!
@@ -349,6 +354,10 @@
*/
uint32_t DownLinkCounter;
/*!
+ * Ping offset of the muilticast channel for Class B
+ */
+ uint16_t PingOffset;
+ /*!
* Reference pointer to the next multicast channel parameters in the list
*/
struct sMulticastParams *Next;
@@ -357,7 +366,7 @@
/*!
* LoRaMAC frame types
*
- * LoRaWAN Specification V1.0.1, chapter 4.2.1, table 1
+ * LoRaWAN Specification V1.0.2, chapter 4.2.1, table 1
*/
typedef enum eLoRaMacFrameType
{
@@ -398,7 +407,7 @@
/*!
* LoRaMAC mote MAC commands
*
- * LoRaWAN Specification V1.0.1, chapter 5, table 4
+ * LoRaWAN Specification V1.0.2, chapter 5, table 4
*/
typedef enum eLoRaMacMoteCmd
{
@@ -430,12 +439,36 @@
* RXTimingSetupAns
*/
MOTE_MAC_RX_TIMING_SETUP_ANS = 0x08,
+ /*!
+ * TXParamSetupAns
+ */
+ MOTE_MAC_TX_PARAM_SETUP_ANS = 0x09,
+ /*!
+ * DlChannelAns
+ */
+ MOTE_MAC_DL_CHANNEL_ANS = 0x0A,
+ /*!
+ * PingSlotInfoReq
+ */
+ MOTE_MAC_PING_SLOT_INFO_REQ = 0x10,
+ /*!
+ * PingSlotFreqAns
+ */
+ MOTE_MAC_PING_SLOT_FREQ_ANS = 0x11,
+ /*!
+ * BeaconTimingReq
+ */
+ MOTE_MAC_BEACON_TIMING_REQ = 0x12,
+ /*!
+ * BeaconFreqAns
+ */
+ MOTE_MAC_BEACON_FREQ_ANS = 0x13,
}LoRaMacMoteCmd_t;
/*!
* LoRaMAC server MAC commands
*
- * LoRaWAN Specification V1.0.1 chapter 5, table 4
+ * LoRaWAN Specification V1.0.2 chapter 5, table 4
*/
typedef enum eLoRaMacSrvCmd
{
@@ -467,6 +500,30 @@
* RXTimingSetupReq
*/
SRV_MAC_RX_TIMING_SETUP_REQ = 0x08,
+ /*!
+ * NewChannelReq
+ */
+ SRV_MAC_TX_PARAM_SETUP_REQ = 0x09,
+ /*!
+ * DlChannelReq
+ */
+ SRV_MAC_DL_CHANNEL_REQ = 0x0A,
+ /*!
+ * PingSlotInfoAns
+ */
+ SRV_MAC_PING_SLOT_INFO_ANS = 0x10,
+ /*!
+ * PingSlotChannelReq
+ */
+ SRV_MAC_PING_SLOT_CHANNEL_REQ = 0x11,
+ /*!
+ * BeaconTimingAns
+ */
+ SRV_MAC_BEACON_TIMING_ANS = 0x12,
+ /*!
+ * BeaconFreqReq
+ */
+ SRV_MAC_BEACON_FREQ_REQ = 0x13,
}LoRaMacSrvCmd_t;
/*!
@@ -495,7 +552,7 @@
/*!
* LoRaMAC header field definition (MHDR field)
*
- * LoRaWAN Specification V1.0.1, chapter 4.2
+ * LoRaWAN Specification V1.0.2, chapter 4.2
*/
typedef union uLoRaMacHeader
{
@@ -526,7 +583,7 @@
/*!
* LoRaMAC frame control field definition (FCtrl)
*
- * LoRaWAN Specification V1.0.1, chapter 4.3.1
+ * LoRaWAN Specification V1.0.2, chapter 4.3.1
*/
typedef union uLoRaMacFrameCtrl
{
@@ -562,6 +619,26 @@
}Bits;
}LoRaMacFrameCtrl_t;
+typedef union uPingSlotInfo
+{
+ uint8_t Value;
+ struct sInfoFields
+ {
+ uint8_t Periodicity : 3;
+ uint8_t RFU : 5;
+ }Fields;
+}PingSlotInfo_t;
+
+typedef struct sBeaconInfo
+{
+ uint32_t Time;
+ struct sGwSpecific
+ {
+ uint8_t InfoDesc;
+ uint8_t Info[6];
+ }GwSpecific;
+}BeaconInfo_t;
+
/*!
* Enumeration containing the status of the operation of a MAC service
*/
@@ -580,6 +657,10 @@
*/
LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT,
/*!
+ * An Rx timeout occurred on receive window 1
+ */
+ LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT,
+ /*!
* An Rx timeout occurred on receive window 2
*/
LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT,
@@ -596,9 +677,8 @@
*/
LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL,
/*!
- * A frame with an invalid downlink counter was received. The
- * downlink counter of the frame was equal to the local copy
- * of the downlink counter of the node.
+ * The downlink counter of the frame was equal to the local copy of the
+ * downlink counter of the node
*/
LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED,
/*!
@@ -615,9 +695,25 @@
*/
LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL,
/*!
- * message integrity check failure
+ * Message integrity check failure
*/
LORAMAC_EVENT_INFO_STATUS_MIC_FAIL,
+ /*!
+ * ToDo
+ */
+ LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL,
+ /*!
+ * ToDo
+ */
+ LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED,
+ /*!
+ * ToDo
+ */
+ LORAMAC_EVENT_INFO_STATUS_BEACON_LOST,
+ /*!
+ * ToDo
+ */
+ LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND,
}LoRaMacEventInfoStatus_t;
/*!
@@ -651,6 +747,10 @@
*/
uint8_t MlmeReq : 1;
/*!
+ * MLME-Ind pending
+ */
+ uint8_t MlmeInd : 1;
+ /*!
* MAC cycle done
*/
uint8_t MacDone : 1;
@@ -709,7 +809,7 @@
* Frame port field. Must be set if the payload is not empty. Use the
* application specific frame port values: [1...223]
*
- * LoRaWAN Specification V1.0.1, chapter 4.3.2
+ * LoRaWAN Specification V1.0.2, chapter 4.3.2
*/
uint8_t fPort;
/*!
@@ -735,7 +835,7 @@
* Frame port field. Must be set if the payload is not empty. Use the
* application specific frame port values: [1...223]
*
- * LoRaWAN Specification V1.0.1, chapter 4.3.2
+ * LoRaWAN Specification V1.0.2, chapter 4.3.2
*/
uint8_t fPort;
/*!
@@ -753,7 +853,7 @@
/*!
* Number of trials to transmit the frame, if the LoRaMAC layer did not
* receive an acknowledgment. The MAC performs a datarate adaptation,
- * according to the LoRaWAN Specification V1.0.1, chapter 19.4, according
+ * according to the LoRaWAN Specification V1.0.2, chapter 18.4, according
* to the following table:
*
* Transmission nb | Data Rate
@@ -955,15 +1055,20 @@
/*!
* Initiates the Over-the-Air activation
*
- * LoRaWAN Specification V1.0.1, chapter 6.2
+ * LoRaWAN Specification V1.0.2, chapter 6.2
*/
MLME_JOIN,
/*!
* LinkCheckReq - Connectivity validation
*
- * LoRaWAN Specification V1.0.1, chapter 5, table 4
+ * LoRaWAN Specification V1.0.2, chapter 5, table 4
*/
MLME_LINK_CHECK,
+ MLME_PING_SLOT_INFO,
+ MLME_BEACON_TIMING,
+ MLME_SWITCH_CLASS,
+ MLME_BEACON_ACQUISITION,
+ MLME_BEACON,
/*!
* Sets Tx continuous wave mode
*
@@ -986,19 +1091,19 @@
/*!
* Globally unique end-device identifier
*
- * LoRaWAN Specification V1.0.1, chapter 6.2.1
+ * LoRaWAN Specification V1.0.2, chapter 6.2.1
*/
uint8_t *DevEui;
/*!
* Application identifier
*
- * LoRaWAN Specification V1.0.1, chapter 6.1.2
+ * LoRaWAN Specification V1.0.2, chapter 6.1.2
*/
uint8_t *AppEui;
/*!
* AES-128 application key
*
- * LoRaWAN Specification V1.0.1, chapter 6.2.2
+ * LoRaWAN Specification V1.0.2, chapter 6.2.2
*/
uint8_t *AppKey;
/*!
@@ -1008,6 +1113,22 @@
}MlmeReqJoin_t;
/*!
+ * LoRaMAC MLME-Request for the ping slot info service
+ */
+typedef struct sMlmeReqPingSlotInfo
+{
+ PingSlotInfo_t PingSlot;
+}MlmeReqPingSlotInfo_t;
+
+/*!
+ * LoRaMAC MLME-Request for the switch class service
+ */
+typedef struct sMlmeReqSwitchClass
+{
+ DeviceClass_t Class;
+}MlmeReqSwitchClass_t;
+
+/*!
* LoRaMAC MLME-Request for Tx continuous wave mode
*/
typedef struct sMlmeReqTxCw
@@ -1046,6 +1167,14 @@
*/
MlmeReqJoin_t Join;
/*!
+ * MLME-Request parameters for a ping slot info request
+ */
+ MlmeReqPingSlotInfo_t PingSlotInfo;
+ /*!
+ * MLME-Request parameters for a switch class request
+ */
+ MlmeReqSwitchClass_t SwitchClass;
+ /*!
* MLME-Request parameters for Tx continuous mode request
*/
MlmeReqTxCw_t TxCw;
@@ -1078,47 +1207,76 @@
* Number of gateways which received the last LinkCheckReq
*/
uint8_t NbGateways;
+ TimerTime_t BeaconTimingDelay;
+ uint8_t BeaconTimingChannel;
/*!
* Provides the number of retransmissions
*/
uint8_t NbRetries;
}MlmeConfirm_t;
+typedef struct sMlmeIndication
+{
+ /*!
+ * Holds the previously performed MLME-Request
+ */
+ Mlme_t MlmeIndication;
+ /*!
+ * Status of the operation
+ */
+ LoRaMacEventInfoStatus_t Status;
+
+ BeaconInfo_t BeaconInfo;
+}MlmeIndication_t;
+
/*!
* LoRa Mac Information Base (MIB)
*
* The following table lists the MIB parameters and the related attributes:
*
- * Attribute | Get | Set
- * --------------------------------- | :-: | :-:
- * \ref MIB_DEVICE_CLASS | YES | YES
- * \ref MIB_NETWORK_JOINED | YES | YES
- * \ref MIB_ADR | YES | YES
- * \ref MIB_NET_ID | YES | YES
- * \ref MIB_DEV_ADDR | YES | YES
- * \ref MIB_NWK_SKEY | YES | YES
- * \ref MIB_APP_SKEY | YES | YES
- * \ref MIB_PUBLIC_NETWORK | YES | YES
- * \ref MIB_REPEATER_SUPPORT | YES | YES
- * \ref MIB_CHANNELS | YES | NO
- * \ref MIB_RX2_CHANNEL | YES | YES
- * \ref MIB_CHANNELS_MASK | YES | YES
+ * Attribute | Get | Set
+ * ----------------------------------------------| :-: | :-:
+ * \ref MIB_DEVICE_CLASS | YES | YES
+ * \ref MIB_NETWORK_JOINED | YES | YES
+ * \ref MIB_ADR | YES | YES
+ * \ref MIB_NET_ID | YES | YES
+ * \ref MIB_DEV_ADDR | YES | YES
+ * \ref MIB_NWK_SKEY | YES | YES
+ * \ref MIB_APP_SKEY | YES | YES
+ * \ref MIB_PUBLIC_NETWORK | YES | YES
+ * \ref MIB_REPEATER_SUPPORT | YES | YES
+ * \ref MIB_CHANNELS | YES | NO
+ * \ref MIB_RX2_CHANNEL | YES | YES
+ * \ref MIB_CHANNELS_MASK | YES | YES
* \ref MIB_CHANNELS_DEFAULT_MASK | YES | YES
- * \ref MIB_CHANNELS_NB_REP | YES | YES
- * \ref MIB_MAX_RX_WINDOW_DURATION | YES | YES
- * \ref MIB_RECEIVE_DELAY_1 | YES | YES
- * \ref MIB_RECEIVE_DELAY_2 | YES | YES
- * \ref MIB_JOIN_ACCEPT_DELAY_1 | YES | YES
- * \ref MIB_JOIN_ACCEPT_DELAY_2 | YES | YES
- * \ref MIB_CHANNELS_DATARATE | YES | YES
- * \ref MIB_CHANNELS_DEFAULT_DATARATE| YES | YES
- * \ref MIB_CHANNELS_TX_POWER | YES | YES
- * \ref MIB_CHANNELS_DEFAULT_TX_POWER| YES | YES
- * \ref MIB_UPLINK_COUNTER | YES | YES
- * \ref MIB_DOWNLINK_COUNTER | YES | YES
- * \ref MIB_MULTICAST_CHANNEL | YES | NO
- * \ref MIB_SYSTEM_MAX_RX_ERROR | YES | YES
- * \ref MIB_MIN_RX_SYMBOLS | YES | YES
+ * \ref MIB_CHANNELS_NB_REP | YES | YES
+ * \ref MIB_MAX_RX_WINDOW_DURATION | YES | YES
+ * \ref MIB_RECEIVE_DELAY_1 | YES | YES
+ * \ref MIB_RECEIVE_DELAY_2 | YES | YES
+ * \ref MIB_JOIN_ACCEPT_DELAY_1 | YES | YES
+ * \ref MIB_JOIN_ACCEPT_DELAY_2 | YES | YES
+ * \ref MIB_CHANNELS_DATARATE | YES | YES
+ * \ref MIB_CHANNELS_DEFAULT_DATARATE | YES | YES
+ * \ref MIB_CHANNELS_TX_POWER | YES | YES
+ * \ref MIB_CHANNELS_DEFAULT_TX_POWER | YES | YES
+ * \ref MIB_UPLINK_COUNTER | YES | YES
+ * \ref MIB_DOWNLINK_COUNTER | YES | YES
+ * \ref MIB_MULTICAST_CHANNEL | YES | NO
+ * \ref MIB_SYSTEM_MAX_RX_ERROR | YES | YES
+ * \ref MIB_MIN_RX_SYMBOLS | YES | YES
+ * \ref MIB_BEACON_INTERVAL | YES | YES
+ * \ref MIB_BEACON_RESERVED | YES | YES
+ * \ref MIB_BEACON_GUARD | YES | YES
+ * \ref MIB_BEACON_WINDOW | YES | YES
+ * \ref MIB_BEACON_WINDOW_SLOTS | YES | YES
+ * \ref MIB_PING_SLOT_WINDOW | YES | YES
+ * \ref MIB_BEACON_SYMBOL_TO_DEFAULT | YES | YES
+ * \ref MIB_BEACON_SYMBOL_TO_EXPANSION_MAX | YES | YES
+ * \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX | YES | YES
+ * \ref MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR | YES | YES
+ * \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR | YES | YES
+ * \ref MIB_MAX_BEACON_LESS_PERIOD | YES | YES
+ * \ref MIB_ANTENNA_GAIN | YES | YES
*
* The following table provides links to the function implementations of the
* related MIB primitives:
@@ -1133,19 +1291,19 @@
/*!
* LoRaWAN device class
*
- * LoRaWAN Specification V1.0.1
+ * LoRaWAN Specification V1.0.2
*/
MIB_DEVICE_CLASS,
/*!
* LoRaWAN Network joined attribute
*
- * LoRaWAN Specification V1.0.1
+ * LoRaWAN Specification V1.0.2
*/
MIB_NETWORK_JOINED,
/*!
* Adaptive data rate
*
- * LoRaWAN Specification V1.0.1, chapter 4.3.1.1
+ * LoRaWAN Specification V1.0.2, chapter 4.3.1.1
*
* [true: ADR enabled, false: ADR disabled]
*/
@@ -1153,31 +1311,31 @@
/*!
* Network identifier
*
- * LoRaWAN Specification V1.0.1, chapter 6.1.1
+ * LoRaWAN Specification V1.0.2, chapter 6.1.1
*/
MIB_NET_ID,
/*!
* End-device address
*
- * LoRaWAN Specification V1.0.1, chapter 6.1.1
+ * LoRaWAN Specification V1.0.2, chapter 6.1.1
*/
MIB_DEV_ADDR,
/*!
* Network session key
*
- * LoRaWAN Specification V1.0.1, chapter 6.1.3
+ * LoRaWAN Specification V1.0.2, chapter 6.1.3
*/
MIB_NWK_SKEY,
/*!
* Application session key
*
- * LoRaWAN Specification V1.0.1, chapter 6.1.4
+ * LoRaWAN Specification V1.0.2, chapter 6.1.4
*/
MIB_APP_SKEY,
/*!
* Set the network type to public or private
*
- * LoRaWAN Specification V1.0.1, chapter 7
+ * LoRaWAN Regional Parameters V1.0.2rB
*
* [true: public network, false: private network]
*/
@@ -1185,7 +1343,7 @@
/*!
* Support the operation with repeaters
*
- * LoRaWAN Specification V1.0.1, chapter 7
+ * LoRaWAN Regional Parameters V1.0.2rB
*
* [true: repeater support enabled, false: repeater support disabled]
*/
@@ -1195,127 +1353,111 @@
* pointer which references the first entry of the channel list. The
* list is of size LORA_MAX_NB_CHANNELS
*
- * LoRaWAN Specification V1.0.1, chapter 7
+ * LoRaWAN Regional Parameters V1.0.2rB
*/
MIB_CHANNELS,
/*!
* Set receive window 2 channel
*
- * LoRaWAN Specification V1.0.1, chapter 3.3.2
+ * LoRaWAN Specification V1.0.2, chapter 3.3.1
*/
MIB_RX2_CHANNEL,
/*!
* Set receive window 2 channel
*
- * LoRaWAN Specification V1.0.1, chapter 3.3.2
+ * LoRaWAN Specification V1.0.2, chapter 3.3.2
*/
MIB_RX2_DEFAULT_CHANNEL,
/*!
* LoRaWAN channels mask
*
- * LoRaWAN Specification V1.0.1, chapter 7
+ * LoRaWAN Regional Parameters V1.0.2rB
*/
MIB_CHANNELS_MASK,
/*!
* LoRaWAN default channels mask
*
- * LoRaWAN Specification V1.0.1, chapter 7
+ * LoRaWAN Regional Parameters V1.0.2rB
*/
MIB_CHANNELS_DEFAULT_MASK,
/*!
* Set the number of repetitions on a channel
*
- * LoRaWAN Specification V1.0.1, chapter 5.2
+ * LoRaWAN Specification V1.0.2, chapter 5.2
*/
MIB_CHANNELS_NB_REP,
/*!
* Maximum receive window duration in [ms]
*
- * LoRaWAN Specification V1.0.1, chapter 3.3.3
+ * LoRaWAN Specification V1.0.2, chapter 3.3.3
*/
MIB_MAX_RX_WINDOW_DURATION,
/*!
* Receive delay 1 in [ms]
*
- * LoRaWAN Specification V1.0.1, chapter 7
+ * LoRaWAN Regional Parameters V1.0.2rB
*/
MIB_RECEIVE_DELAY_1,
/*!
* Receive delay 2 in [ms]
*
- * LoRaWAN Specification V1.0.1, chapter 7
+ * LoRaWAN Regional Parameters V1.0.2rB
*/
MIB_RECEIVE_DELAY_2,
/*!
* Join accept delay 1 in [ms]
*
- * LoRaWAN Specification V1.0.1, chapter 7
+ * LoRaWAN Regional Parameters V1.0.2rB
*/
MIB_JOIN_ACCEPT_DELAY_1,
/*!
* Join accept delay 2 in [ms]
*
- * LoRaWAN Specification V1.0.1, chapter 7
+ * LoRaWAN Regional Parameters V1.0.2rB
*/
MIB_JOIN_ACCEPT_DELAY_2,
/*!
* Default Data rate of a channel
*
- * LoRaWAN Specification V1.0.1, chapter 7
+ * LoRaWAN Regional Parameters V1.0.2rB
*
- * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7]
- *
- * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13]
+ * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
*/
MIB_CHANNELS_DEFAULT_DATARATE,
/*!
* Data rate of a channel
*
- * LoRaWAN Specification V1.0.1, chapter 7
+ * LoRaWAN Regional Parameters V1.0.2rB
*
- * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7]
- *
- * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13]
+ * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
*/
MIB_CHANNELS_DATARATE,
/*!
* Transmission power of a channel
*
- * LoRaWAN Specification V1.0.1, chapter 7
- *
- * EU868 - [TX_POWER_20_DBM, TX_POWER_14_DBM, TX_POWER_11_DBM,
- * TX_POWER_08_DBM, TX_POWER_05_DBM, TX_POWER_02_DBM]
+ * LoRaWAN Regional Parameters V1.0.2rB
*
- * US915 - [TX_POWER_30_DBM, TX_POWER_28_DBM, TX_POWER_26_DBM,
- * TX_POWER_24_DBM, TX_POWER_22_DBM, TX_POWER_20_DBM,
- * TX_POWER_18_DBM, TX_POWER_14_DBM, TX_POWER_12_DBM,
- * TX_POWER_10_DBM]
+ * The allowed ranges are region specific. Please refer to \ref TX_POWER_0 to \ref TX_POWER_15 for details.
*/
MIB_CHANNELS_TX_POWER,
/*!
* Transmission power of a channel
*
- * LoRaWAN Specification V1.0.1, chapter 7
- *
- * EU868 - [TX_POWER_20_DBM, TX_POWER_14_DBM, TX_POWER_11_DBM,
- * TX_POWER_08_DBM, TX_POWER_05_DBM, TX_POWER_02_DBM]
+ * LoRaWAN Regional Parameters V1.0.2rB
*
- * US915 - [TX_POWER_30_DBM, TX_POWER_28_DBM, TX_POWER_26_DBM,
- * TX_POWER_24_DBM, TX_POWER_22_DBM, TX_POWER_20_DBM,
- * TX_POWER_18_DBM, TX_POWER_14_DBM, TX_POWER_12_DBM,
- * TX_POWER_10_DBM]
+ * The allowed ranges are region specific. Please refer to \ref TX_POWER_0 to \ref TX_POWER_15 for details.
*/
MIB_CHANNELS_DEFAULT_TX_POWER,
/*!
* LoRaWAN Up-link counter
*
- * LoRaWAN Specification V1.0.1, chapter 4.3.1.5
+ * LoRaWAN Specification V1.0.2, chapter 4.3.1.5
*/
MIB_UPLINK_COUNTER,
/*!
* LoRaWAN Down-link counter
*
- * LoRaWAN Specification V1.0.1, chapter 4.3.1.5
+ * LoRaWAN Specification V1.0.2, chapter 4.3.1.5
*/
MIB_DOWNLINK_COUNTER,
/*!
@@ -1335,6 +1477,63 @@
* Default: 6 symbols
*/
MIB_MIN_RX_SYMBOLS,
+ /*!
+ * Antenna gain of the node. Default value is region specific.
+ * The antenna gain is used to calculate the TX power of the node.
+ * The formula is:
+ * radioTxPower = ( int8_t )floor( maxEirp - antennaGain )
+ */
+ MIB_ANTENNA_GAIN,
+ /*!
+ * Beacon interval in ms
+ */
+ MIB_BEACON_INTERVAL,
+ /*!
+ * Beacon reserved time in ms
+ */
+ MIB_BEACON_RESERVED,
+ /*!
+ * Beacon guard time in ms
+ */
+ MIB_BEACON_GUARD,
+ /*!
+ * Beacon window time in ms
+ */
+ MIB_BEACON_WINDOW,
+ /*!
+ * Beacon window time in number of slots
+ */
+ MIB_BEACON_WINDOW_SLOTS,
+ /*!
+ * Ping slot length time in ms
+ */
+ MIB_PING_SLOT_WINDOW,
+ /*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+ MIB_BEACON_SYMBOL_TO_DEFAULT,
+ /*!
+ * Maximum symbol timeout for beacons
+ */
+ MIB_BEACON_SYMBOL_TO_EXPANSION_MAX,
+ /*!
+ * Maximum symbol timeout for ping slots
+ */
+ MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX,
+ /*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+ MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR,
+ /*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+ MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR,
+ /*!
+ * Maximum allowed beacon less time in ms
+ */
+ MIB_MAX_BEACON_LESS_PERIOD
}Mib_t;
/*!
@@ -1516,6 +1715,86 @@
* Related MIB type: \ref MIB_MIN_RX_SYMBOLS
*/
uint8_t MinRxSymbols;
+ /*!
+ * Antenna gain
+ *
+ * Related MIB type: \ref MIB_ANTENNA_GAIN
+ */
+ float AntennaGain;
+ /*!
+ * Beacon interval in ms
+ *
+ * Related MIB type: \ref MIB_BEACON_INTERVAL
+ */
+ uint32_t BeaconInterval;
+ /*!
+ * Beacon reserved time in ms
+ *
+ * Related MIB type: \ref MIB_BEACON_RESERVED
+ */
+ uint32_t BeaconReserved;
+ /*!
+ * Beacon guard time in ms
+ *
+ * Related MIB type: \ref MIB_BEACON_GUARD
+ */
+ uint32_t BeaconGuard;
+ /*!
+ * Beacon window time in ms
+ *
+ * Related MIB type: \ref MIB_BEACON_WINDOW
+ */
+ uint32_t BeaconWindow;
+ /*!
+ * Beacon window time in number of slots
+ *
+ * Related MIB type: \ref MIB_BEACON_WINDOW_SLOTS
+ */
+ uint32_t BeaconWindowSlots;
+ /*!
+ * Ping slot length time in ms
+ *
+ * Related MIB type: \ref MIB_PING_SLOT_WINDOW
+ */
+ uint32_t PingSlotWindow;
+ /*!
+ * Default symbol timeout for beacons and ping slot windows
+ *
+ * Related MIB type: \ref MIB_BEACON_SYMBOL_TO_DEFAULT
+ */
+ uint32_t BeaconSymbolToDefault;
+ /*!
+ * Maximum symbol timeout for beacons
+ *
+ * Related MIB type: \ref MIB_BEACON_SYMBOL_TO_EXPANSION_MAX
+ */
+ uint32_t BeaconSymbolToExpansionMax;
+ /*!
+ * Maximum symbol timeout for ping slots
+ *
+ * Related MIB type: \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX
+ */
+ uint32_t PingSlotSymbolToExpansionMax;
+ /*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ *
+ * Related MIB type: \ref MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR
+ */
+ uint32_t BeaconSymbolToExpansionFactor;
+ /*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ *
+ * Related MIB type: \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR
+ */
+ uint32_t PingSlotSymbolToExpansionFactor;
+ /*!
+ * Maximum allowed beacon less time in ms
+ *
+ * Related MIB type: \ref MIB_MAX_BEACON_LESS_PERIOD
+ */
+ uint32_t MaxBeaconLessPeriod;
}MibParam_t;
/*!
@@ -1550,6 +1829,21 @@
}LoRaMacTxInfo_t;
/*!
+ * Structure to hold multiple MLME request confirm data
+ */
+typedef struct sMlmeConfirmQueue
+{
+ /*!
+ * Holds the previously performed MLME-Request
+ */
+ Mlme_t MlmeRequest;
+ /*!
+ * Status of the operation
+ */
+ LoRaMacEventInfoStatus_t Status;
+}MlmeConfirmQueue_t;
+
+/*!
* LoRaMAC Status
*/
typedef enum eLoRaMacStatus
@@ -1587,20 +1881,68 @@
*/
LORAMAC_STATUS_NO_NETWORK_JOINED,
/*!
- * Service not started - payload lenght error
+ * Service not started - payload length error
*/
LORAMAC_STATUS_LENGTH_ERROR,
/*!
- * Service not started - payload lenght error
- */
- LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR,
- /*!
* Service not started - the device is switched off
*/
LORAMAC_STATUS_DEVICE_OFF,
+ /*!
+ * Service not started - the specified region is not supported
+ * or not activated with preprocessor definitions.
+ */
+ LORAMAC_STATUS_REGION_NOT_SUPPORTED
}LoRaMacStatus_t;
/*!
+ * LoRaMAC region enumeration
+ */
+typedef enum eLoRaMacRegion_t
+{
+ /*!
+ * AS band on 923MHz
+ */
+ LORAMAC_REGION_AS923,
+ /*!
+ * Australian band on 915MHz
+ */
+ LORAMAC_REGION_AU915,
+ /*!
+ * Chinese band on 470MHz
+ */
+ LORAMAC_REGION_CN470,
+ /*!
+ * Chinese band on 779MHz
+ */
+ LORAMAC_REGION_CN779,
+ /*!
+ * European band on 433MHz
+ */
+ LORAMAC_REGION_EU433,
+ /*!
+ * European band on 868MHz
+ */
+ LORAMAC_REGION_EU868,
+ /*!
+ * South korean band on 920MHz
+ */
+ LORAMAC_REGION_KR920,
+ /*!
+ * India band on 865MHz
+ */
+ LORAMAC_REGION_IN865,
+ /*!
+ * North american band on 915MHz
+ */
+ LORAMAC_REGION_US915,
+ /*!
+ * North american band on 915MHz with a maximum of 16 channels
+ */
+ LORAMAC_REGION_US915_HYBRID,
+}LoRaMacRegion_t;
+
+/*!
* LoRaMAC events structure
* Used to notify upper layers of MAC events
*/
@@ -1624,8 +1966,17 @@
* \param [OUT] MLME-Confirm parameters
*/
void ( *MacMlmeConfirm )( MlmeConfirm_t *MlmeConfirm );
+ /*!
+ * \brief MLME-Indication primitive
+ *
+ * \param [OUT] MLME-Indication parameters
+ */
+ void ( *MacMlmeIndication )( MlmeIndication_t *MlmeIndication );
}LoRaMacPrimitives_t;
+/*!
+ * LoRaMAC callback structure
+ */
typedef struct sLoRaMacCallback
{
/*!
@@ -1637,9 +1988,22 @@
* to measure the battery level]
*/
uint8_t ( *GetBatteryLevel )( void );
+ /*!
+ * \brief Measures the temperature level
+ *
+ * \retval Temperature level
+ */
+ float ( *GetTemperatureLevel )( void );
}LoRaMacCallback_t;
/*!
+ * LoRaMAC Max EIRP (dBm) table
+ */
+static const uint8_t LoRaMacMaxEirpTable[] = { 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 };
+
+
+
+/*!
* \brief LoRaMAC layer initialization
*
* \details In addition to the initialization of the LoRaMAC layer, this
@@ -1647,18 +2011,21 @@
* MLME services. Every data field of \ref LoRaMacPrimitives_t must be
* set to a valid callback function.
*
- * \param [IN] events - Pointer to a structure defining the LoRaMAC
- * event functions. Refer to \ref LoRaMacPrimitives_t.
+ * \param [IN] primitives - Pointer to a structure defining the LoRaMAC
+ * event functions. Refer to \ref LoRaMacPrimitives_t.
*
* \param [IN] events - Pointer to a structure defining the LoRaMAC
* callback functions. Refer to \ref LoRaMacCallback_t.
*
+ * \param [IN] region - The region to start.
+ *
* \retval LoRaMacStatus_t Status of the operation. Possible returns are:
* returns are:
* \ref LORAMAC_STATUS_OK,
- * \ref LORAMAC_STATUS_PARAMETER_INVALID.
+ * \ref LORAMAC_STATUS_PARAMETER_INVALID,
+ * \ref LORAMAC_STATUS_REGION_NOT_SUPPORTED.
*/
-LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks );
+LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks, LoRaMacRegion_t region );
/*!
* \brief Queries the LoRaMAC if it is possible to send the next frame with
@@ -1675,11 +2042,13 @@
*
* \retval LoRaMacStatus_t Status of the operation. When the parameters are
* not valid, the function returns \ref LORAMAC_STATUS_PARAMETER_INVALID.
- * In case of a length error caused by the applicative payload size, the
- * function returns LORAMAC_STATUS_LENGTH_ERROR. In case of a length error
- * due to additional MAC commands in the queue, the function returns
- * LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR. In case the query is valid, and
- * the LoRaMAC is able to send the frame, the function returns LORAMAC_STATUS_OK. *
+ * In case of a length error caused by the applicative payload in combination
+ * with the MAC commands, the function returns \ref LORAMAC_STATUS_LENGTH_ERROR.
+ * Please note that if the size of the MAC commands which are in the queue do
+ * not fit into the payload size on the related datarate, the LoRaMAC will
+ * omit the MAC commands.
+ * In case the query is valid, and the LoRaMAC is able to send the frame,
+ * the function returns \ref LORAMAC_STATUS_OK.
*/
LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo );
@@ -1687,14 +2056,10 @@
* \brief LoRaMAC channel add service
*
* \details Adds a new channel to the channel list and activates the id in
- * the channel mask. For the US915 band, all channels are enabled
- * by default. It is not possible to activate less than 6 125 kHz
- * channels.
+ * the channel mask. Please note that this functionality is not available
+ * on all regions. Information about allowed ranges are available at the LoRaWAN Regional Parameters V1.0.2rB
*
- * \param [IN] id - Id of the channel. Possible values are:
- *
- * 0-15 for EU868
- * 0-72 for US915
+ * \param [IN] id - Id of the channel.
*
* \param [IN] params - Channel parameters to set.
*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LoRaMacClassB.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1345 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC Class B layer implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include "board.h"
+#include "LoRaMac.h"
+#include "region/Region.h"
+#include "LoRaMacClassB.h"
+#include "LoRaMacCrypto.h"
+
+#ifdef LORAMAC_CLASSB_ENABLED
+/*!
+ * State of the beaconing mechanism
+ */
+static BeaconState_t BeaconState;
+
+/*!
+ * State of the ping slot mechanism
+ */
+static PingSlotState_t PingSlotState;
+
+/*!
+ * State of the multicast slot mechanism
+ */
+static PingSlotState_t MulticastSlotState;
+
+/*!
+ * Class B ping slot context
+ */
+static PingSlotContext_t PingSlotCtx;
+
+/*!
+ * Class B beacon context
+ */
+static BeaconContext_t BeaconCtx;
+
+/*!
+ * Timer for CLASS B beacon acquisition and tracking.
+ */
+static TimerEvent_t BeaconTimer;
+
+/*!
+ * Timer for CLASS B ping slot timer.
+ */
+static TimerEvent_t PingSlotTimer;
+
+/*!
+ * Timer for CLASS B multicast ping slot timer.
+ */
+static TimerEvent_t MulticastSlotTimer;
+
+/*!
+ * Container for the callbacks related to class b.
+ */
+static LoRaMacClassBCallback_t LoRaMacClassBCallbacks;
+
+/*!
+ * Data structure which holds the parameters which needs to be set
+ * in class b operation.
+ */
+static LoRaMacClassBParams_t LoRaMacClassBParams;
+
+
+/*!
+ * \brief Calculates the next ping slot time.
+ *
+ * \param [IN] slotOffset The ping slot offset
+ * \param [IN] pingPeriod The ping period
+ * \param [OUT] timeOffset Time offset of the next slot, based on current time
+ *
+ * \retval [true: ping slot found, false: no ping slot found]
+ */
+static bool CalcNextSlotTime( uint16_t slotOffset, uint16_t pingPeriod, TimerTime_t* timeOffset )
+{
+ uint8_t currentPingSlot = 0;
+ TimerTime_t slotTime = 0;
+ TimerTime_t currentTime = TimerGetCurrentTime( );
+
+ // Calculate the point in time of the last beacon even if we missed it
+ slotTime = ( ( currentTime - BeaconCtx.LastBeaconRx ) % BeaconCtx.Cfg.Interval );
+ slotTime = currentTime - slotTime;
+
+ // Add the reserved time and the ping offset
+ slotTime += BeaconCtx.Cfg.Reserved;
+ slotTime += slotOffset * PingSlotCtx.Cfg.PingSlotWindow;
+
+ if( slotTime < currentTime )
+ {
+ currentPingSlot = ( ( currentTime - slotTime ) /
+ ( pingPeriod * PingSlotCtx.Cfg.PingSlotWindow ) ) + 1;
+ slotTime += ( ( TimerTime_t )( currentPingSlot * pingPeriod ) *
+ PingSlotCtx.Cfg.PingSlotWindow );
+ }
+
+ if( currentPingSlot < PingSlotCtx.PingNb )
+ {
+ if( slotTime <= ( BeaconCtx.NextBeaconRx - BeaconCtx.Cfg.Guard - PingSlotCtx.Cfg.PingSlotWindow ) )
+ {
+ // Calculate the relative ping slot time
+ slotTime -= currentTime;
+ slotTime -= RADIO_WAKEUP_TIME;
+ slotTime = TimerTempCompensation( slotTime, BeaconCtx.Temperature );
+ *timeOffset = slotTime;
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ * \brief Calculates CRC's of the beacon frame
+ *
+ * \param [IN] buffer Pointer to the data
+ * \param [IN] length Length of the data
+ *
+ * \retval CRC
+ */
+static uint16_t BeaconCrc( uint8_t *buffer, uint16_t length )
+{
+ // The CRC calculation follows CCITT
+ const uint16_t polynom = 0x1021;
+ // CRC initial value
+ uint16_t crc = 0x0000;
+
+ if( buffer == NULL )
+ {
+ return 0;
+ }
+
+ for( uint16_t i = 0; i < length; ++i )
+ {
+ crc ^= ( uint16_t ) buffer[i] << 8;
+ for( uint16_t j = 0; j < 8; ++j )
+ {
+ crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 );
+ }
+ }
+
+ return crc;
+}
+
+static void GetTemperatureLevel( LoRaMacClassBCallback_t *callbacks, BeaconContext_t *beaconCtx )
+{
+ // Measure temperature, if available
+ if( ( callbacks != NULL ) && ( callbacks->GetTemperatureLevel != NULL ) )
+ {
+ beaconCtx->Temperature = callbacks->GetTemperatureLevel( );
+ }
+}
+
+#endif // LORAMAC_CLASSB_ENABLED
+
+void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Init variables to default
+ memset1( ( uint8_t* ) &BeaconCtx, 0, sizeof( BeaconContext_t ) );
+ memset1( ( uint8_t* ) &PingSlotCtx, 0, sizeof( PingSlotCtx ) );
+
+ // Store callbacks
+ LoRaMacClassBCallbacks = *callbacks;
+
+ // Store parameter pointers
+ LoRaMacClassBParams = *classBParams;
+
+ // Setup default temperature
+ BeaconCtx.Temperature = 25.0;
+ GetTemperatureLevel( &LoRaMacClassBCallbacks, &BeaconCtx );
+
+ // Initialize timers
+ TimerInit( &BeaconTimer, LoRaMacClassBBeaconTimerEvent );
+ TimerInit( &PingSlotTimer, LoRaMacClassBPingSlotTimerEvent );
+ TimerInit( &MulticastSlotTimer, LoRaMacClassBMulticastSlotTimerEvent );
+
+ // Setup default states
+ BeaconState = BEACON_STATE_ACQUISITION;
+ PingSlotState = PINGSLOT_STATE_SET_TIMER;
+ MulticastSlotState = PINGSLOT_STATE_SET_TIMER;
+
+ // Get phy parameters
+ getPhy.Attribute = PHY_BEACON_INTERVAL;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ BeaconCtx.Cfg.Interval = phyParam.Value;
+
+ getPhy.Attribute = PHY_BEACON_RESERVED;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ BeaconCtx.Cfg.Reserved = phyParam.Value;
+
+ getPhy.Attribute = PHY_BEACON_GUARD;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ BeaconCtx.Cfg.Guard = phyParam.Value;
+
+ getPhy.Attribute = PHY_BEACON_WINDOW;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ BeaconCtx.Cfg.Window = phyParam.Value;
+
+ getPhy.Attribute = PHY_BEACON_WINDOW_SLOTS;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ BeaconCtx.Cfg.WindowSlots = phyParam.Value;
+
+ getPhy.Attribute = PHY_BEACON_SYMBOL_TO_DEFAULT;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ BeaconCtx.Cfg.SymbolToDefault = phyParam.Value;
+
+ getPhy.Attribute = PHY_BEACON_SYMBOL_TO_EXPANSION_MAX;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ BeaconCtx.Cfg.SymbolToExpansionMax = phyParam.Value;
+
+ getPhy.Attribute = PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ BeaconCtx.Cfg.SymbolToExpansionFactor = phyParam.Value;
+
+ getPhy.Attribute = PHY_MAX_BEACON_LESS_PERIOD;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ BeaconCtx.Cfg.MaxBeaconLessPeriod = phyParam.Value;
+
+ getPhy.Attribute = PHY_BEACON_DELAY_BEACON_TIMING_ANS;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ BeaconCtx.Cfg.DelayBeaconTimingAns = phyParam.Value;
+
+ getPhy.Attribute = PHY_PING_SLOT_WINDOW;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ PingSlotCtx.Cfg.PingSlotWindow = phyParam.Value;
+
+ getPhy.Attribute = PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ PingSlotCtx.Cfg.SymbolToExpansionMax = phyParam.Value;
+
+ getPhy.Attribute = PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ PingSlotCtx.Cfg.SymbolToExpansionFactor = phyParam.Value;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBSetBeaconState( BeaconState_t beaconState )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ BeaconState = beaconState;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBSetPingSlotState( PingSlotState_t pingSlotState )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ PingSlotState = pingSlotState;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBBeaconTimerEvent( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ RxBeaconSetup_t rxBeaconSetup;
+ bool beaconChannelSet = false;
+ uint8_t index = 0;
+ bool activateTimer = false;
+ TimerTime_t beaconEventTime = 1;
+ TimerTime_t currentTime = TimerGetCurrentTime( );
+
+ TimerStop( &BeaconTimer );
+
+ // Beacon state machine
+ switch( BeaconState )
+ {
+ case BEACON_STATE_ACQUISITION:
+ {
+ activateTimer = true;
+
+ if( BeaconCtx.Ctrl.AcquisitionPending == 1 )
+ {
+ Radio.Sleep();
+ BeaconState = BEACON_STATE_SWITCH_CLASS;
+ }
+ else
+ {
+ // Default symbol timeouts
+ BeaconCtx.SymbolTimeout = BeaconCtx.Cfg.SymbolToDefault;
+ PingSlotCtx.SymbolTimeout = BeaconCtx.Cfg.SymbolToDefault;
+
+ if( BeaconCtx.Ctrl.BeaconDelaySet == 1 )
+ {
+ if( BeaconCtx.BeaconTimingDelay > 0 )
+ {
+ if( BeaconCtx.NextBeaconRx > currentTime )
+ {
+ BeaconCtx.Ctrl.AcquisitionTimerSet = 1;
+ beaconEventTime = TimerTempCompensation( BeaconCtx.NextBeaconRx - currentTime, BeaconCtx.Temperature );
+ }
+ else
+ {
+ BeaconCtx.Ctrl.BeaconDelaySet = 0;
+ BeaconCtx.Ctrl.AcquisitionPending = 1;
+ BeaconCtx.Ctrl.AcquisitionTimerSet = 0;
+ beaconEventTime = BeaconCtx.Cfg.Interval;
+
+ rxBeaconSetup.SymbolTimeout = BeaconCtx.SymbolTimeout;
+ rxBeaconSetup.RxTime = 0;
+ rxBeaconSetup.DeviceAddress = *LoRaMacClassBParams.LoRaMacDevAddr;
+ rxBeaconSetup.BeaconTimingChannel = BeaconCtx.BeaconTimingChannel;
+ rxBeaconSetup.CustomFrequencyEnabled = BeaconCtx.Ctrl.CustomFreq;
+ rxBeaconSetup.BeaconChannelSet = BeaconCtx.Ctrl.BeaconChannelSet;
+ rxBeaconSetup.CustomFrequency = BeaconCtx.Frequency;
+ rxBeaconSetup.BeaconTime = BeaconCtx.BeaconTime;
+ rxBeaconSetup.BeaconInterval = BeaconCtx.Cfg.Interval;
+
+ RegionRxBeaconSetup( *LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &LoRaMacClassBParams.McpsIndication->RxDatarate, &beaconChannelSet );
+ BeaconCtx.Ctrl.BeaconChannelSet = beaconChannelSet;
+ }
+ BeaconCtx.NextBeaconRx = 0;
+ BeaconCtx.BeaconTimingDelay = 0;
+ }
+ else
+ {
+ BeaconCtx.Ctrl.BeaconDelaySet = 0;
+ BeaconCtx.Ctrl.AcquisitionPending = 0;
+ BeaconCtx.Ctrl.AcquisitionTimerSet = 1;
+ beaconEventTime = BeaconCtx.Cfg.DelayBeaconTimingAns;
+
+ rxBeaconSetup.SymbolTimeout = BeaconCtx.SymbolTimeout;
+ rxBeaconSetup.RxTime = 0;
+ rxBeaconSetup.DeviceAddress = *LoRaMacClassBParams.LoRaMacDevAddr;
+ rxBeaconSetup.BeaconTimingChannel = BeaconCtx.BeaconTimingChannel;
+ rxBeaconSetup.CustomFrequencyEnabled = BeaconCtx.Ctrl.CustomFreq;
+ rxBeaconSetup.BeaconChannelSet = BeaconCtx.Ctrl.BeaconChannelSet;
+ rxBeaconSetup.CustomFrequency = BeaconCtx.Frequency;
+ rxBeaconSetup.BeaconTime = BeaconCtx.BeaconTime;
+ rxBeaconSetup.BeaconInterval = BeaconCtx.Cfg.Interval;
+
+ RegionRxBeaconSetup( *LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &LoRaMacClassBParams.McpsIndication->RxDatarate, &beaconChannelSet );
+ BeaconCtx.Ctrl.BeaconChannelSet = beaconChannelSet;
+ }
+ }
+ else
+ {
+ BeaconCtx.Ctrl.AcquisitionPending = 1;
+ beaconEventTime = BeaconCtx.Cfg.Interval;
+ if( BeaconCtx.Ctrl.AcquisitionTimerSet == 0 )
+ {
+ rxBeaconSetup.SymbolTimeout = BeaconCtx.SymbolTimeout;
+ rxBeaconSetup.RxTime = 0;
+ rxBeaconSetup.DeviceAddress = *LoRaMacClassBParams.LoRaMacDevAddr;
+ rxBeaconSetup.BeaconTimingChannel = BeaconCtx.BeaconTimingChannel;
+ rxBeaconSetup.CustomFrequencyEnabled = BeaconCtx.Ctrl.CustomFreq;
+ rxBeaconSetup.BeaconChannelSet = BeaconCtx.Ctrl.BeaconChannelSet;
+ rxBeaconSetup.CustomFrequency = BeaconCtx.Frequency;
+ rxBeaconSetup.BeaconTime = BeaconCtx.BeaconTime;
+ rxBeaconSetup.BeaconInterval = BeaconCtx.Cfg.Interval;
+
+ RegionRxBeaconSetup( *LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &LoRaMacClassBParams.McpsIndication->RxDatarate, &beaconChannelSet );
+ BeaconCtx.Ctrl.BeaconChannelSet = beaconChannelSet;
+ }
+ BeaconCtx.Ctrl.AcquisitionTimerSet = 0;
+ }
+ }
+ break;
+ }
+ case BEACON_STATE_TIMEOUT:
+ {
+ // Store listen time
+ BeaconCtx.ListenTime = currentTime - BeaconCtx.NextBeaconRx;
+ // Setup next state
+ BeaconState = BEACON_STATE_BEACON_MISSED;
+ // no break here
+ }
+ case BEACON_STATE_BEACON_MISSED:
+ {
+ // We have to update the beacon time, since we missed a beacon
+ BeaconCtx.BeaconTime += ( BeaconCtx.Cfg.Interval / 1000 );
+
+ // Update symbol timeout
+ BeaconCtx.SymbolTimeout *= BeaconCtx.Cfg.SymbolToExpansionFactor;
+ if( BeaconCtx.SymbolTimeout > BeaconCtx.Cfg.SymbolToExpansionMax )
+ {
+ BeaconCtx.SymbolTimeout = BeaconCtx.Cfg.SymbolToExpansionMax;
+ }
+ PingSlotCtx.SymbolTimeout *= PingSlotCtx.Cfg.SymbolToExpansionFactor;
+ if( PingSlotCtx.SymbolTimeout > PingSlotCtx.Cfg.SymbolToExpansionMax )
+ {
+ PingSlotCtx.SymbolTimeout = PingSlotCtx.Cfg.SymbolToExpansionMax;
+ }
+ // Setup next state
+ BeaconState = BEACON_STATE_REACQUISITION;
+ // no break here
+ }
+ case BEACON_STATE_REACQUISITION:
+ {
+ if( ( currentTime - BeaconCtx.LastBeaconRx ) > BeaconCtx.Cfg.MaxBeaconLessPeriod )
+ {
+ activateTimer = true;
+ BeaconState = BEACON_STATE_SWITCH_CLASS;
+ }
+ else
+ {
+ activateTimer = true;
+ // Calculate the point in time of the next beacon
+ beaconEventTime = ( ( currentTime - BeaconCtx.LastBeaconRx ) % BeaconCtx.Cfg.Interval );
+ beaconEventTime = BeaconCtx.Cfg.Interval - beaconEventTime;
+ // Take window enlargement into account
+ beaconEventTime -= ( ( BeaconCtx.ListenTime * BeaconCtx.Cfg.SymbolToExpansionFactor ) >> 1 );
+ beaconEventTime = TimerTempCompensation( beaconEventTime, BeaconCtx.Temperature );
+ BeaconCtx.NextBeaconRx = currentTime + beaconEventTime;
+
+ // Make sure to transit to the correct state
+ if( ( currentTime + BeaconCtx.Cfg.Guard ) < BeaconCtx.NextBeaconRx )
+ {
+ beaconEventTime -= BeaconCtx.Cfg.Guard;
+ BeaconState = BEACON_STATE_IDLE;
+ }
+ else
+ {
+ BeaconState = BEACON_STATE_GUARD;
+ }
+
+ if( PingSlotCtx.Ctrl.Assigned == 1 )
+ {
+ PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ TimerSetValue( &PingSlotTimer, 1 );
+ TimerStart( &PingSlotTimer );
+
+ MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ TimerSetValue( &MulticastSlotTimer, 1 );
+ TimerStart( &MulticastSlotTimer );
+ }
+ }
+ BeaconCtx.Ctrl.BeaconAcquired = 0;
+
+ if( BeaconCtx.Ctrl.ResumeBeaconing == 0 )
+ {
+ LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON;
+ LoRaMacClassBParams.MlmeIndication->Status = LORAMAC_EVENT_INFO_STATUS_BEACON_LOST;
+ LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1;
+
+ TimerSetValue( LoRaMacClassBParams.MacStateCheckTimer, 1 );
+ TimerStart( LoRaMacClassBParams.MacStateCheckTimer );
+ LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1;
+ }
+ BeaconCtx.Ctrl.ResumeBeaconing = 0;
+ break;
+ }
+ case BEACON_STATE_LOCKED:
+ {
+ activateTimer = true;
+ // Calculate the point in time of the next beacon
+ beaconEventTime = ( ( currentTime - BeaconCtx.LastBeaconRx ) % BeaconCtx.Cfg.Interval );
+ beaconEventTime = BeaconCtx.Cfg.Interval - beaconEventTime;
+ beaconEventTime = TimerTempCompensation( beaconEventTime, BeaconCtx.Temperature );
+ BeaconCtx.NextBeaconRx = currentTime + beaconEventTime;
+
+ // Make sure to transit to the correct state
+ if( ( currentTime + BeaconCtx.Cfg.Guard ) < BeaconCtx.NextBeaconRx )
+ {
+ beaconEventTime -= BeaconCtx.Cfg.Guard;
+ BeaconState = BEACON_STATE_IDLE;
+ }
+ else
+ {
+ BeaconState = BEACON_STATE_GUARD;
+ }
+
+ if( LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 )
+ {
+ // index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_BEACON_ACQUISITION, MlmeConfirmQueueCnt );
+ index = LoRaMacClassBCallbacks.GetMlmeConfrimIndex( LoRaMacClassBParams.MlmeConfirmQueue, MLME_BEACON_ACQUISITION );
+ if( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
+ {
+ LoRaMacClassBParams.MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_OK;
+ LoRaMacClassBParams.MlmeConfirm->TxTimeOnAir = 0;
+ }
+ }
+
+ if( PingSlotCtx.Ctrl.Assigned == 1 )
+ {
+ PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ TimerSetValue( &PingSlotTimer, 1 );
+ TimerStart( &PingSlotTimer );
+
+ MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ TimerSetValue( &MulticastSlotTimer, 1 );
+ TimerStart( &MulticastSlotTimer );
+ }
+ BeaconCtx.Ctrl.AcquisitionPending = 0;
+
+ if( BeaconCtx.Ctrl.ResumeBeaconing == 0 )
+ {
+ LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON;
+ LoRaMacClassBParams.MlmeIndication->Status = LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED;
+ LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1;
+
+ TimerSetValue( LoRaMacClassBParams.MacStateCheckTimer, 1 );
+ TimerStart( LoRaMacClassBParams.MacStateCheckTimer );
+ LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1;
+ }
+ BeaconCtx.Ctrl.ResumeBeaconing = 0;
+ break;
+ }
+ case BEACON_STATE_IDLE:
+ {
+ activateTimer = true;
+ GetTemperatureLevel( &LoRaMacClassBCallbacks, &BeaconCtx );
+ beaconEventTime = BeaconCtx.NextBeaconRx - RADIO_WAKEUP_TIME;
+ currentTime = TimerGetCurrentTime( );
+
+ if( beaconEventTime > currentTime )
+ {
+ BeaconState = BEACON_STATE_GUARD;
+ beaconEventTime -= currentTime;
+ beaconEventTime = TimerTempCompensation( beaconEventTime, BeaconCtx.Temperature );
+ }
+ else
+ {
+ BeaconState = BEACON_STATE_REACQUISITION;
+ beaconEventTime = 1;
+ }
+ break;
+ }
+ case BEACON_STATE_GUARD:
+ {
+ BeaconState = BEACON_STATE_RX;
+
+ rxBeaconSetup.SymbolTimeout = BeaconCtx.SymbolTimeout;
+ rxBeaconSetup.RxTime = BeaconCtx.Cfg.Reserved;
+ rxBeaconSetup.DeviceAddress = *LoRaMacClassBParams.LoRaMacDevAddr;
+ rxBeaconSetup.BeaconTimingChannel = BeaconCtx.BeaconTimingChannel;
+ rxBeaconSetup.CustomFrequencyEnabled = BeaconCtx.Ctrl.CustomFreq;
+ rxBeaconSetup.BeaconChannelSet = BeaconCtx.Ctrl.BeaconChannelSet;
+ rxBeaconSetup.CustomFrequency = BeaconCtx.Frequency;
+ rxBeaconSetup.BeaconTime = BeaconCtx.BeaconTime;
+ rxBeaconSetup.BeaconInterval = BeaconCtx.Cfg.Interval;
+
+ RegionRxBeaconSetup( *LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &LoRaMacClassBParams.McpsIndication->RxDatarate, &beaconChannelSet );
+ BeaconCtx.Ctrl.BeaconChannelSet = beaconChannelSet;
+ break;
+ }
+ case BEACON_STATE_SWITCH_CLASS:
+ {
+ if( LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 )
+ {
+ // index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_BEACON_ACQUISITION, MlmeConfirmQueueCnt );
+ index = LoRaMacClassBCallbacks.GetMlmeConfrimIndex( LoRaMacClassBParams.MlmeConfirmQueue, MLME_BEACON_ACQUISITION );
+ if( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
+ {
+ LoRaMacClassBParams.MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND;
+ }
+ }
+ else
+ {
+ LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_SWITCH_CLASS;
+ LoRaMacClassBParams.MlmeIndication->Status = LORAMAC_EVENT_INFO_STATUS_OK;
+ PingSlotCtx.Ctrl.Assigned = 0;
+ LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1;
+ }
+ BeaconState = BEACON_STATE_ACQUISITION;
+
+ BeaconCtx.Ctrl.BeaconMode = 0;
+ BeaconCtx.Ctrl.AcquisitionPending = 0;
+ BeaconCtx.Ctrl.AcquisitionTimerSet = 0;
+ LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1;
+
+ TimerSetValue( LoRaMacClassBParams.MacStateCheckTimer, beaconEventTime );
+ TimerStart( LoRaMacClassBParams.MacStateCheckTimer );
+ break;
+ }
+ default:
+ {
+ BeaconState = BEACON_STATE_ACQUISITION;
+ break;
+ }
+ }
+
+ if( activateTimer == true )
+ {
+ TimerSetValue( &BeaconTimer, beaconEventTime );
+ TimerStart( &BeaconTimer );
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBPingSlotTimerEvent( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ RxConfigParams_t pingSlotRxConfig;
+ TimerTime_t pingSlotTime = 0;
+
+ TimerStop( &PingSlotTimer );
+
+ switch( PingSlotState )
+ {
+ case PINGSLOT_STATE_CALC_PING_OFFSET:
+ {
+ LoRaMacBeaconComputePingOffset( BeaconCtx.BeaconTime,
+ *LoRaMacClassBParams.LoRaMacDevAddr,
+ PingSlotCtx.PingPeriod,
+ &( PingSlotCtx.PingOffset ) );
+ PingSlotState = PINGSLOT_STATE_SET_TIMER;
+ // no break
+ }
+ case PINGSLOT_STATE_SET_TIMER:
+ {
+ if( CalcNextSlotTime( PingSlotCtx.PingOffset, PingSlotCtx.PingPeriod, &pingSlotTime ) == true )
+ {
+ // Start the timer if the ping slot time is in range
+ PingSlotState = PINGSLOT_STATE_IDLE;
+ TimerSetValue( &PingSlotTimer, pingSlotTime );
+ TimerStart( &PingSlotTimer );
+ }
+ break;
+ }
+ case PINGSLOT_STATE_IDLE:
+ {
+ uint32_t frequency = PingSlotCtx.Frequency;
+
+ if( PingSlotCtx.Ctrl.CustomFreq == 0 )
+ {
+ // Restore floor plan
+ getPhy.Attribute = PHY_PINGSLOT_CHANNEL_FREQ;
+ getPhy.DeviceAddress = *LoRaMacClassBParams.LoRaMacDevAddr;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ frequency = phyParam.Value;
+ }
+
+ if( MulticastSlotState != PINGSLOT_STATE_RX )
+ {
+ if( BeaconCtx.Ctrl.BeaconAcquired == 1 )
+ {
+ RegionComputeRxWindowParameters( *LoRaMacClassBParams.LoRaMacRegion,
+ PingSlotCtx.Datarate,
+ LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
+ LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError,
+ &pingSlotRxConfig );
+ PingSlotCtx.SymbolTimeout = pingSlotRxConfig.WindowTimeout;
+ }
+ PingSlotState = PINGSLOT_STATE_RX;
+
+ pingSlotRxConfig.Datarate = PingSlotCtx.Datarate;
+ pingSlotRxConfig.DownlinkDwellTime = LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
+ pingSlotRxConfig.RepeaterSupport = LoRaMacClassBParams.LoRaMacParams->RepeaterSupport;
+ pingSlotRxConfig.Frequency = frequency;
+ pingSlotRxConfig.RxContinuous = false;
+ pingSlotRxConfig.Window = 1;
+
+ RegionRxConfig( *LoRaMacClassBParams.LoRaMacRegion, &pingSlotRxConfig, ( int8_t* )&LoRaMacClassBParams.McpsIndication->RxDatarate );
+
+ if( pingSlotRxConfig.RxContinuous == false )
+ {
+ Radio.Rx( LoRaMacClassBParams.LoRaMacParams->MaxRxWindow );
+ }
+ else
+ {
+ Radio.Rx( 0 ); // Continuous mode
+ }
+ }
+ else
+ {
+ // Multicast slots have priority. Skip Rx
+ PingSlotState = PINGSLOT_STATE_SET_TIMER;
+ TimerSetValue( &PingSlotTimer, PingSlotCtx.Cfg.PingSlotWindow );
+ TimerStart( &PingSlotTimer );
+ }
+ break;
+ }
+ default:
+ {
+ PingSlotState = PINGSLOT_STATE_SET_TIMER;
+ break;
+ }
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBMulticastSlotTimerEvent( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ RxConfigParams_t multicastSlotRxConfig;
+ TimerTime_t multicastSlotTime = 0;
+ TimerTime_t slotTime = 0;
+ MulticastParams_t *cur = LoRaMacClassBParams.MulticastChannels;
+
+ TimerStop( &MulticastSlotTimer );
+
+ if( cur == NULL )
+ {
+ return;
+ }
+
+ switch( MulticastSlotState )
+ {
+ case PINGSLOT_STATE_CALC_PING_OFFSET:
+ {
+ while( cur != NULL )
+ {
+ LoRaMacBeaconComputePingOffset( BeaconCtx.BeaconTime,
+ cur->Address,
+ PingSlotCtx.PingPeriod,
+ &( cur->PingOffset ) );
+ cur = cur->Next;
+ }
+ MulticastSlotState = PINGSLOT_STATE_SET_TIMER;
+ // no break
+ }
+ case PINGSLOT_STATE_SET_TIMER:
+ {
+ cur = LoRaMacClassBParams.MulticastChannels;
+ PingSlotCtx.NextMulticastChannel = NULL;
+
+ while( cur != NULL )
+ {
+ if( CalcNextSlotTime( cur->PingOffset, PingSlotCtx.PingPeriod, &slotTime ) == true )
+ {
+ if( ( multicastSlotTime == 0 ) || ( multicastSlotTime > slotTime ) )
+ {
+ // Update the slot time and the next multicast channel
+ multicastSlotTime = slotTime;
+ PingSlotCtx.NextMulticastChannel = cur;
+ }
+ }
+ cur = cur->Next;
+ }
+
+ if( PingSlotCtx.NextMulticastChannel != NULL )
+ {
+ // Start the timer if the ping slot time is in range
+ MulticastSlotState = PINGSLOT_STATE_IDLE;
+ TimerSetValue( &MulticastSlotTimer, multicastSlotTime );
+ TimerStart( &MulticastSlotTimer );
+ }
+ break;
+ }
+ case PINGSLOT_STATE_IDLE:
+ {
+ uint32_t frequency = PingSlotCtx.Frequency;
+
+ if( PingSlotCtx.NextMulticastChannel == NULL )
+ {
+ MulticastSlotState = PINGSLOT_STATE_SET_TIMER;
+ TimerSetValue( &MulticastSlotTimer, 1 );
+ TimerStart( &MulticastSlotTimer );
+ break;
+ }
+
+ if( PingSlotCtx.Ctrl.CustomFreq == 0 )
+ {
+ // Restore floor plan
+ getPhy.Attribute = PHY_PINGSLOT_CHANNEL_FREQ;
+ getPhy.DeviceAddress = PingSlotCtx.NextMulticastChannel->Address;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ frequency = phyParam.Value;
+ }
+
+ if( BeaconCtx.Ctrl.BeaconAcquired == 1 )
+ {
+ RegionComputeRxWindowParameters( *LoRaMacClassBParams.LoRaMacRegion,
+ PingSlotCtx.Datarate,
+ LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
+ LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError,
+ &multicastSlotRxConfig );
+ PingSlotCtx.SymbolTimeout = multicastSlotRxConfig.WindowTimeout;
+ }
+ MulticastSlotState = PINGSLOT_STATE_RX;
+
+ multicastSlotRxConfig.Datarate = PingSlotCtx.Datarate;
+ multicastSlotRxConfig.DownlinkDwellTime = LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
+ multicastSlotRxConfig.RepeaterSupport = LoRaMacClassBParams.LoRaMacParams->RepeaterSupport;
+ multicastSlotRxConfig.Frequency = frequency;
+ multicastSlotRxConfig.RxContinuous = false;
+ multicastSlotRxConfig.Window = 1;
+
+ RegionRxConfig( *LoRaMacClassBParams.LoRaMacRegion, &multicastSlotRxConfig, ( int8_t* )&LoRaMacClassBParams.McpsIndication->RxDatarate );
+
+ if( multicastSlotRxConfig.RxContinuous == false )
+ {
+ Radio.Rx( LoRaMacClassBParams.LoRaMacParams->MaxRxWindow );
+ }
+ else
+ {
+ Radio.Rx( 0 ); // Continuous mode
+ }
+ break;
+ }
+ default:
+ {
+ MulticastSlotState = PINGSLOT_STATE_SET_TIMER;
+ break;
+ }
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBRxBeacon( uint8_t *payload, uint16_t size )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ bool beaconReceived = false;
+ uint16_t crc0 = 0;
+ uint16_t crc1 = 0;
+ uint16_t beaconCrc0 = 0;
+ uint16_t beaconCrc1 = 0;
+ uint8_t rfuOffset1 = 0;
+ uint8_t rfuOffset2 = 0;
+
+ getPhy.Attribute = PHY_BEACON_SIZE;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+
+ // For beacon payload sizes > 17 we need to apply an offset
+ if( phyParam.Value > 17 )
+ {
+ rfuOffset1 = 1;
+ rfuOffset2 = 1;
+ }
+
+ // Verify if we are in the state where we expect a beacon
+ if( ( BeaconState == BEACON_STATE_RX ) || ( BeaconCtx.Ctrl.AcquisitionPending == 1 ) )
+ {
+ if( size == phyParam.Value )
+ {
+ beaconCrc0 = ( ( uint16_t ) payload[6 + rfuOffset1] ) & 0x00FF;
+ beaconCrc0 |= ( ( uint16_t ) payload[7 + rfuOffset1] << 8 ) & 0xFF00;
+ crc0 = BeaconCrc( payload, 6 + rfuOffset1 );
+
+ // Validate the first crc of the beacon frame
+ if( crc0 == beaconCrc0 )
+ {
+ BeaconCtx.BeaconTime = ( ( uint32_t ) payload[2 + rfuOffset1] ) & 0x000000FF;
+ BeaconCtx.BeaconTime |= ( ( uint32_t ) ( payload[3 + rfuOffset1] << 8 ) ) & 0x0000FF00;
+ BeaconCtx.BeaconTime |= ( ( uint32_t ) ( payload[4 + rfuOffset1] << 16 ) ) & 0x00FF0000;
+ BeaconCtx.BeaconTime |= ( ( uint32_t ) ( payload[5 + rfuOffset1] << 24 ) ) & 0xFF000000;
+ LoRaMacClassBParams.MlmeIndication->BeaconInfo.Time = BeaconCtx.BeaconTime;
+ beaconReceived = true;
+ }
+
+ beaconCrc1 = ( ( uint16_t ) payload[15 + rfuOffset1 + rfuOffset2] ) & 0x00FF;
+ beaconCrc1 |= ( ( uint16_t ) payload[16 + rfuOffset1 + rfuOffset2] << 8 ) & 0xFF00;
+ crc1 = BeaconCrc( &payload[8 + rfuOffset1], 7 + rfuOffset2 );
+
+ // Validate the second crc of the beacon frame
+ if( crc1 == beaconCrc1 )
+ {
+ // Beacon valid, apply data
+ LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.InfoDesc = payload[8 + rfuOffset1];
+ memcpy1( LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.Info, &payload[9 + rfuOffset1], 7 );
+ beaconReceived = true;
+ }
+
+ // Reset beacon variables, if one of the crc is valid
+ if( beaconReceived == true )
+ {
+ BeaconCtx.LastBeaconRx = TimerGetCurrentTime( ) - Radio.TimeOnAir( MODEM_LORA, size );
+ BeaconCtx.Ctrl.BeaconAcquired = 1;
+ BeaconCtx.Ctrl.BeaconMode = 1;
+ BeaconCtx.SymbolTimeout = BeaconCtx.Cfg.SymbolToDefault;
+ BeaconState = BEACON_STATE_LOCKED;
+
+ LoRaMacClassBBeaconTimerEvent( );
+ }
+ }
+
+ if( BeaconState == BEACON_STATE_RX )
+ {
+ BeaconState = BEACON_STATE_TIMEOUT;
+ LoRaMacClassBBeaconTimerEvent( );
+ }
+ // Return always true, when we expect a beacon.
+ beaconReceived = true;
+ }
+
+ return beaconReceived;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsBeaconExpected( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( ( BeaconCtx.Ctrl.AcquisitionPending == 1 ) ||
+ ( BeaconCtx.Ctrl.AcquisitionTimerSet == 1 ) ||
+ ( BeaconState == BEACON_STATE_RX ) )
+ {
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsPingExpected( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( PingSlotState == PINGSLOT_STATE_RX )
+ {
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsAcquisitionPending( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( BeaconCtx.Ctrl.AcquisitionPending == 1 )
+ {
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsAcquisitionTimerSet( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( BeaconCtx.Ctrl.AcquisitionTimerSet == 1 )
+ {
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsBeaconModeActive( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( BeaconCtx.Ctrl.BeaconMode == 1 )
+ {
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBSetPingSlotInfo( uint8_t periodicity )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ PingSlotCtx.PingNb = 128 / ( 1 << periodicity );
+ PingSlotCtx.PingPeriod = BeaconCtx.Cfg.WindowSlots / PingSlotCtx.PingNb;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBHaltBeaconing( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( ( BeaconState == BEACON_STATE_TIMEOUT ) ||
+ ( BeaconState == BEACON_STATE_SWITCH_CLASS ) )
+ {
+ // Update the state machine before halt
+ LoRaMacClassBBeaconTimerEvent( );
+ }
+
+ // Halt beacon state machine
+ BeaconState = BEACON_STATE_HALT;
+
+ // Halt ping slot state machine
+ TimerStop( &BeaconTimer );
+
+ // Halt ping slot state machine
+ TimerStop( &PingSlotTimer );
+
+ // Halt multicast ping slot state machine
+ TimerStop( &MulticastSlotTimer );
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBResumeBeaconing( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( BeaconState == BEACON_STATE_HALT )
+ {
+ BeaconCtx.Ctrl.ResumeBeaconing = 1;
+
+ // Set default state
+ BeaconState = BEACON_STATE_LOCKED;
+
+ if( BeaconCtx.Ctrl.BeaconAcquired == 0 )
+ {
+ // Set the default state for beacon less operation
+ BeaconState = BEACON_STATE_REACQUISITION;
+ }
+ TimerSetValue( &BeaconTimer, 1 );
+ TimerStart( &BeaconTimer );
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+LoRaMacStatus_t LoRaMacClassBSwitchClass( DeviceClass_t nextClass )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( nextClass == CLASS_B )
+ {// Switch to from class a to class b
+ if( ( BeaconCtx.Ctrl.BeaconMode == 1 ) && ( PingSlotCtx.Ctrl.Assigned == 1 ) )
+ {
+ return LORAMAC_STATUS_OK;
+ }
+ }
+ if( nextClass == CLASS_A )
+ {// Switch from class b to class a
+ BeaconState = BEACON_STATE_ACQUISITION;
+ return LORAMAC_STATUS_OK;
+ }
+ return LORAMAC_STATUS_SERVICE_UNKNOWN;
+#else
+ return LORAMAC_STATUS_SERVICE_UNKNOWN;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+LoRaMacStatus_t LoRaMacClassBMibGetRequestConfirm( MibRequestConfirm_t *mibGet )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ LoRaMacStatus_t status;
+
+ switch( mibGet->Type )
+ {
+ case MIB_BEACON_INTERVAL:
+ {
+ mibGet->Param.BeaconInterval = BeaconCtx.Cfg.Interval;
+ break;
+ }
+ case MIB_BEACON_RESERVED:
+ {
+ mibGet->Param.BeaconReserved = BeaconCtx.Cfg.Reserved;
+ break;
+ }
+ case MIB_BEACON_GUARD:
+ {
+ mibGet->Param.BeaconGuard = BeaconCtx.Cfg.Guard;
+ break;
+ }
+ case MIB_BEACON_WINDOW:
+ {
+ mibGet->Param.BeaconWindow = BeaconCtx.Cfg.Window;
+ break;
+ }
+ case MIB_BEACON_WINDOW_SLOTS:
+ {
+ mibGet->Param.BeaconWindowSlots = BeaconCtx.Cfg.WindowSlots;
+ break;
+ }
+ case MIB_PING_SLOT_WINDOW:
+ {
+ mibGet->Param.PingSlotWindow = PingSlotCtx.Cfg.PingSlotWindow;
+ break;
+ }
+ case MIB_BEACON_SYMBOL_TO_DEFAULT:
+ {
+ mibGet->Param.BeaconSymbolToDefault = BeaconCtx.Cfg.SymbolToDefault;
+ break;
+ }
+ case MIB_BEACON_SYMBOL_TO_EXPANSION_MAX:
+ {
+ mibGet->Param.BeaconSymbolToExpansionMax = BeaconCtx.Cfg.SymbolToExpansionMax;
+ break;
+ }
+ case MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+ {
+ mibGet->Param.PingSlotSymbolToExpansionMax = PingSlotCtx.Cfg.SymbolToExpansionMax;
+ break;
+ }
+ case MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ mibGet->Param.BeaconSymbolToExpansionFactor = BeaconCtx.Cfg.SymbolToExpansionFactor;
+ break;
+ }
+ case MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ mibGet->Param.PingSlotSymbolToExpansionFactor = PingSlotCtx.Cfg.SymbolToExpansionFactor;
+ break;
+ }
+ case MIB_MAX_BEACON_LESS_PERIOD:
+ {
+ mibGet->Param.MaxBeaconLessPeriod = BeaconCtx.Cfg.MaxBeaconLessPeriod;
+ break;
+ }
+ default:
+ {
+ status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+ break;
+ }
+ }
+ return status;
+#else
+ return LORAMAC_STATUS_SERVICE_UNKNOWN;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+LoRaMacStatus_t LoRaMacMibClassBSetRequestConfirm( MibRequestConfirm_t *mibSet )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ LoRaMacStatus_t status;
+
+ switch( mibSet->Type )
+ {
+ case MIB_BEACON_INTERVAL:
+ {
+ BeaconCtx.Cfg.Interval = mibSet->Param.BeaconInterval;
+ break;
+ }
+ case MIB_BEACON_RESERVED:
+ {
+ BeaconCtx.Cfg.Reserved = mibSet->Param.BeaconReserved;
+ break;
+ }
+ case MIB_BEACON_GUARD:
+ {
+ BeaconCtx.Cfg.Guard = mibSet->Param.BeaconGuard;
+ break;
+ }
+ case MIB_BEACON_WINDOW:
+ {
+ BeaconCtx.Cfg.Window = mibSet->Param.BeaconWindow;
+ break;
+ }
+ case MIB_BEACON_WINDOW_SLOTS:
+ {
+ BeaconCtx.Cfg.WindowSlots = mibSet->Param.BeaconWindowSlots;
+ break;
+ }
+ case MIB_PING_SLOT_WINDOW:
+ {
+ PingSlotCtx.Cfg.PingSlotWindow = mibSet->Param.PingSlotWindow;
+ break;
+ }
+ case MIB_BEACON_SYMBOL_TO_DEFAULT:
+ {
+ BeaconCtx.Cfg.SymbolToDefault = mibSet->Param.BeaconSymbolToDefault;
+ break;
+ }
+ case MIB_BEACON_SYMBOL_TO_EXPANSION_MAX:
+ {
+ BeaconCtx.Cfg.SymbolToExpansionMax = mibSet->Param.BeaconSymbolToExpansionMax;
+ break;
+ }
+ case MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+ {
+ PingSlotCtx.Cfg.SymbolToExpansionMax = mibSet->Param.PingSlotSymbolToExpansionMax;
+ break;
+ }
+ case MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ BeaconCtx.Cfg.SymbolToExpansionFactor = mibSet->Param.BeaconSymbolToExpansionFactor;
+ break;
+ }
+ case MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ PingSlotCtx.Cfg.SymbolToExpansionFactor = mibSet->Param.PingSlotSymbolToExpansionFactor;
+ break;
+ }
+ case MIB_MAX_BEACON_LESS_PERIOD:
+ {
+ BeaconCtx.Cfg.MaxBeaconLessPeriod = mibSet->Param.MaxBeaconLessPeriod;
+ break;
+ }
+ default:
+ {
+ status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+ break;
+ }
+ }
+ return status;
+#else
+ return LORAMAC_STATUS_SERVICE_UNKNOWN;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBPingSlotInfoAns( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ uint8_t index = LORA_MAC_MLME_CONFIRM_QUEUE_LEN;
+
+ index = LoRaMacClassBCallbacks.GetMlmeConfrimIndex( LoRaMacClassBParams.MlmeConfirmQueue, MLME_PING_SLOT_INFO );
+ if( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
+ {
+ LoRaMacClassBParams.MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_OK;
+ PingSlotCtx.Ctrl.Assigned = 1;
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+uint8_t LoRaMacClassBPingSlotChannelReq( uint8_t datarate, uint32_t frequency )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ uint8_t status = 0x03;
+ VerifyParams_t verify;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ if( frequency != 0 )
+ {
+ if( Radio.CheckRfFrequency( frequency ) == false )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ verify.DatarateParams.Datarate = datarate;
+ verify.DatarateParams.DownlinkDwellTime = LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
+
+ if( RegionVerify( *LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_RX_DR ) == false )
+ {
+ status &= 0xFD; // Datarate range KO
+ }
+
+ if( status == 0x03 )
+ {
+ PingSlotCtx.Ctrl.CustomFreq = 1;
+ PingSlotCtx.Frequency = frequency;
+ PingSlotCtx.Datarate = datarate;
+ }
+ }
+ else
+ {
+ getPhy.Attribute = PHY_BEACON_CHANNEL_DR;
+ phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+
+ PingSlotCtx.Ctrl.CustomFreq = 0;
+ PingSlotCtx.Datarate = phyParam.Value;
+ }
+ return status;
+#else
+ return 0;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ uint8_t index = LORA_MAC_MLME_CONFIRM_QUEUE_LEN;
+ TimerTime_t currentTime = TimerGetCurrentTime( );
+
+ BeaconCtx.BeaconTimingDelay = ( BeaconCtx.Cfg.DelayBeaconTimingAns * beaconTimingDelay );
+ BeaconCtx.BeaconTimingChannel = beaconTimingChannel;
+
+ index = LoRaMacClassBCallbacks.GetMlmeConfrimIndex( LoRaMacClassBParams.MlmeConfirmQueue, MLME_BEACON_TIMING );
+ if( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
+ {
+ if( BeaconCtx.BeaconTimingDelay > BeaconCtx.Cfg.Interval )
+ {
+ // We missed the beacon already
+ BeaconCtx.BeaconTimingDelay = 0;
+ BeaconCtx.BeaconTimingChannel = 0;
+ LoRaMacClassBParams.MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND;
+ }
+ else
+ {
+ BeaconCtx.Ctrl.BeaconDelaySet = 1;
+ BeaconCtx.Ctrl.BeaconChannelSet = 1;
+ BeaconCtx.NextBeaconRx = currentTime + BeaconCtx.BeaconTimingDelay;
+ LoRaMacClassBParams.MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_OK;
+ }
+
+ LoRaMacClassBParams.MlmeConfirm->BeaconTimingDelay = BeaconCtx.BeaconTimingDelay;
+ LoRaMacClassBParams.MlmeConfirm->BeaconTimingChannel = BeaconCtx.BeaconTimingChannel;
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBBeaconFreqReq( uint32_t frequency )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( frequency != 0 )
+ {
+ if( Radio.CheckRfFrequency( frequency ) == true )
+ {
+ BeaconCtx.Ctrl.CustomFreq = 1;
+ BeaconCtx.Frequency = frequency;
+ return true;
+ }
+ }
+ else
+ {
+ BeaconCtx.Ctrl.CustomFreq = 0;
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+TimerTime_t LoRaMacClassBGetBeaconReservedTime( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ return BeaconCtx.Cfg.Reserved;
+#else
+ return 0;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+TimerTime_t LoRaMacClassBGetPingSlotWinTime( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ return PingSlotCtx.Cfg.PingSlotWindow;
+#else
+ return 0;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+TimerTime_t LoRaMacClassBIsUplinkCollision( TimerTime_t txTimeOnAir )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ TimerTime_t currentTime = TimerGetCurrentTime( );
+ TimerTime_t beaconReserved = 0;
+
+ beaconReserved = BeaconCtx.NextBeaconRx -
+ BeaconCtx.Cfg.Guard -
+ LoRaMacClassBParams.LoRaMacParams->ReceiveDelay1 -
+ LoRaMacClassBParams.LoRaMacParams->ReceiveDelay2 -
+ txTimeOnAir;
+
+ // Check if the next beacon will be received during the next uplink.
+ if( ( currentTime >= beaconReserved ) && ( currentTime < ( BeaconCtx.NextBeaconRx + BeaconCtx.Cfg.Reserved ) ) )
+ {// Next beacon will be sent during the next uplink.
+ return BeaconCtx.Cfg.Reserved;
+ }
+ return 0;
+#else
+ return 0;
+#endif // LORAMAC_CLASSB_ENABLED
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LoRaMacClassB.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,565 @@
+/*!
+ * \file LoRaMacClassB.h
+ *
+ * \brief LoRa MAC Class B layer implementation
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup LORAMACCLASSB LoRa MAC Class B layer implementation
+ * This module specifies the API implementation of the LoRaMAC Class B layer.
+ * This is a placeholder for a detailed description of the LoRaMac
+ * layer and the supported features.
+ * \{
+ */
+#ifndef __LORAMACCLASSB_H__
+#define __LORAMACCLASSB_H__
+
+ /*!
+ * States of the class B beacon acquisition and tracking
+ */
+typedef enum eBeaconState
+{
+ /*!
+ * Initial state to acquire the beacon
+ */
+ BEACON_STATE_ACQUISITION,
+ /*!
+ * Handles the state when the beacon reception fails
+ */
+ BEACON_STATE_TIMEOUT,
+ /*!
+ * Handles the state when the beacon was missed due to an uplink
+ */
+ BEACON_STATE_BEACON_MISSED,
+ /*!
+ * Reacquisition state which applies the algorithm to enlarge the reception
+ * windows
+ */
+ BEACON_STATE_REACQUISITION,
+ /*!
+ * The node has locked a beacon successfully
+ */
+ BEACON_STATE_LOCKED,
+ /*!
+ * The beacon state machine is stopped due to operations with higher priority
+ */
+ BEACON_STATE_HALT,
+ /*!
+ * The node currently operates in the beacon window and is idle. In this
+ * state, the temperature measurement takes place
+ */
+ BEACON_STATE_IDLE,
+ /*!
+ * The node operates in the guard time of class B
+ */
+ BEACON_STATE_GUARD,
+ /*!
+ * The node is in receive mode to lock a beacon
+ */
+ BEACON_STATE_RX,
+ /*!
+ * The nodes switches the device class
+ */
+ BEACON_STATE_SWITCH_CLASS,
+}BeaconState_t;
+
+/*!
+ * States of the class B ping slot mechanism
+ */
+typedef enum ePingSlotState
+{
+ /*!
+ * Calculation of the ping slot offset
+ */
+ PINGSLOT_STATE_CALC_PING_OFFSET,
+ /*!
+ * State to set the timer to open the next ping slot
+ */
+ PINGSLOT_STATE_SET_TIMER,
+ /*!
+ * The node is in idle state
+ */
+ PINGSLOT_STATE_IDLE,
+ /*!
+ * The node opens up a ping slot window
+ */
+ PINGSLOT_STATE_RX,
+}PingSlotState_t;
+
+/*!
+ * Class B ping slot context structure
+ */
+typedef struct sPingSlotContext
+{
+ struct sPingSlotCtrl
+ {
+ /*!
+ * Set when the server assigned a ping slot to the node
+ */
+ uint8_t Assigned : 1;
+ /*!
+ * Set when a custom frequency is used
+ */
+ uint8_t CustomFreq : 1;
+ }Ctrl;
+
+ struct sPingSlotCfg
+ {
+ /*!
+ * Ping slot length time in ms
+ */
+ uint32_t PingSlotWindow;
+ /*!
+ * Maximum symbol timeout for ping slots
+ */
+ uint32_t SymbolToExpansionMax;
+ /*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+ uint32_t SymbolToExpansionFactor;
+ }Cfg;
+ /*!
+ * Number of ping slots
+ */
+ uint8_t PingNb;
+ /*!
+ * Period of the ping slots
+ */
+ uint16_t PingPeriod;
+ /*!
+ * Ping offset
+ */
+ uint16_t PingOffset;
+ /*!
+ * Reception frequency of the ping slot windows
+ */
+ uint32_t Frequency;
+ /*!
+ * Datarate of the ping slot
+ */
+ int8_t Datarate;
+ /*!
+ * Current symbol timeout. The node enlarges this variable in case of beacon
+ * loss.
+ */
+ uint16_t SymbolTimeout;
+ /*!
+ * The multicast channel which will be enabled next.
+ */
+ MulticastParams_t *NextMulticastChannel;
+}PingSlotContext_t;
+
+
+/*!
+ * Class B beacon context structure
+ */
+typedef struct sBeaconContext
+{
+ struct sBeaconCtrl
+ {
+ /*!
+ * Set if the node receives beacons
+ */
+ uint8_t BeaconMode : 1;
+ /*!
+ * Set if the node has acquired the beacon
+ */
+ uint8_t BeaconAcquired : 1;
+ /*!
+ * Set if the node has a custom frequency for beaconing and ping slots
+ */
+ uint8_t CustomFreq : 1;
+ /*!
+ * Set if a beacon delay was set for the beacon acquisition
+ */
+ uint8_t BeaconDelaySet : 1;
+ /*!
+ * Set if a beacon channel was set for the beacon acquisition
+ */
+ uint8_t BeaconChannelSet : 1;
+ /*!
+ * Set if beacon acquisition is pending
+ */
+ uint8_t AcquisitionPending : 1;
+ /*!
+ * Set if beacon timer is set to acquire a beacon
+ */
+ uint8_t AcquisitionTimerSet : 1;
+ /*!
+ * Set if the beacon state machine will be resumed
+ */
+ uint8_t ResumeBeaconing : 1;
+ }Ctrl;
+
+ struct sBeaconCfg
+ {
+ /*!
+ * Beacon interval in ms
+ */
+ uint32_t Interval;
+ /*!
+ * Beacon reserved time in ms
+ */
+ uint32_t Reserved;
+ /*!
+ * Beacon guard time in ms
+ */
+ uint32_t Guard;
+ /*!
+ * Beacon window time in ms
+ */
+ uint32_t Window;
+ /*!
+ * Beacon window time in numer of slots
+ */
+ uint32_t WindowSlots;
+ /*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+ uint32_t SymbolToDefault;
+ /*!
+ * Maximum symbol timeout for beacons
+ */
+ uint32_t SymbolToExpansionMax;
+ /*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+ uint32_t SymbolToExpansionFactor;
+ /*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+ uint32_t MaxBeaconLessPeriod;
+ /*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+ uint32_t DelayBeaconTimingAns;
+ }Cfg;
+ /*!
+ * Beacon reception frequency
+ */
+ uint32_t Frequency;
+ /*!
+ * Current temperature
+ */
+ float Temperature;
+ /*!
+ * Beacon time received with the beacon frame
+ */
+ TimerTime_t BeaconTime;
+ /*!
+ * Time when the last beacon was received
+ */
+ TimerTime_t LastBeaconRx;
+ /*!
+ * Time when the next beacon will be received
+ */
+ TimerTime_t NextBeaconRx;
+ /*!
+ * Current symbol timeout. The node enlarges this variable in case of beacon
+ * loss.
+ */
+ uint16_t SymbolTimeout;
+ /*!
+ * Listen time for the beacon in case of reception timeout
+ */
+ TimerTime_t ListenTime;
+ /*!
+ * Beacon timing channel for next beacon
+ */
+ uint8_t BeaconTimingChannel;
+ /*!
+ * Delay for next beacon in ms
+ */
+ TimerTime_t BeaconTimingDelay;
+}BeaconContext_t;
+
+/*!
+ * Data structure which contains the callbacks
+ */
+typedef struct sLoRaMacClassBCallback
+{
+ /*!
+ * \brief Measures the temperature level
+ *
+ * \retval Temperature level
+ */
+ float ( *GetTemperatureLevel )( void );
+ /*!
+ * \brief Gets the index of the confirm queue of a specific MLME-request
+ *
+ * \param [IN] queue MLME-Confirm queue pointer
+ * \param [IN] req MLME-Request to validate
+ * \retval Index of the MLME-Confirm. 0xFF is no entry found.
+ */
+ uint8_t ( *GetMlmeConfrimIndex )( MlmeConfirmQueue_t* queue, Mlme_t req );
+}LoRaMacClassBCallback_t;
+
+/*!
+ * Data structure which pointers to the properties LoRaMAC
+ */
+typedef struct sLoRaMacClassBParams
+{
+ /*!
+ * Pointer to the MlmeIndication structure
+ */
+ MlmeIndication_t *MlmeIndication;
+ /*!
+ * Pointer to the McpsIndication structure
+ */
+ McpsIndication_t *McpsIndication;
+ /*!
+ * Pointer to the MlmeConfirm structure
+ */
+ MlmeConfirm_t *MlmeConfirm;
+ /*!
+ * Pointer to the LoRaMacFlags structure
+ */
+ LoRaMacFlags_t *LoRaMacFlags;
+ /*!
+ * Pointer to the LoRaMac device address
+ */
+ uint32_t *LoRaMacDevAddr;
+ /*!
+ * Pointer to the MLME confirmation queue
+ */
+ MlmeConfirmQueue_t *MlmeConfirmQueue;
+ /*!
+ * Pointer to the LoRaMac region definition
+ */
+ LoRaMacRegion_t *LoRaMacRegion;
+ /*!
+ * Pointer to the MacStateCheckTimer timer
+ */
+ TimerEvent_t *MacStateCheckTimer;
+ /*!
+ * Pointer to the LoRaMacParams structure
+ */
+ LoRaMacParams_t *LoRaMacParams;
+ /*!
+ * Pointer to the multicast channel list
+ */
+ MulticastParams_t *MulticastChannels;
+}LoRaMacClassBParams_t;
+
+/*!
+ * \brief Initialize LoRaWAN Class B
+ *
+ * \param [IN] classBParams Information and feedback parameter
+ * \param [IN] callbacks Contains the callback which the Class B implementation needs
+ */
+void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks );
+
+/*!
+ * \brief Set the state of the beacon state machine
+ *
+ * \param [IN] beaconState Beacon state.
+ */
+void LoRaMacClassBSetBeaconState( BeaconState_t beaconState );
+
+/*!
+ * \brief Set the state of the ping slot state machine
+ *
+ * \param [IN] pingSlotState Ping slot state.
+ */
+void LoRaMacClassBSetPingSlotState( PingSlotState_t pingSlotState );
+
+/*!
+ * \brief State machine of the Class B for beaconing
+ */
+void LoRaMacClassBBeaconTimerEvent( void );
+
+/*!
+ * \brief State machine of the Class B for ping slots
+ */
+void LoRaMacClassBPingSlotTimerEvent( void );
+
+/*!
+ * \brief State machine of the Class B for multicast slots
+ */
+void LoRaMacClassBMulticastSlotTimerEvent( void );
+
+/*!
+ * \brief Receives and decodes the beacon frame
+ *
+ * \param [IN] payload Pointer to the payload
+ * \param [IN] size Size of the payload
+ * \retval [true, if the node has received a beacon; false, if not]
+ */
+bool LoRaMacClassBRxBeacon( uint8_t *payload, uint16_t size );
+
+/*!
+ * \brief The function validates, if the node expects a beacon
+ * at the current time.
+ *
+ * \retval [true, if the node expects a beacon; false, if not]
+ */
+bool LoRaMacClassBIsBeaconExpected( void );
+
+/*!
+ * \brief The function validates, if the node expects a ping slot
+ * at the current time.
+ *
+ * \retval [true, if the node expects a ping slot; false, if not]
+ */
+bool LoRaMacClassBIsPingExpected( void );
+
+/*!
+ * \brief Verifies if the acquisition pending bit is set
+ *
+ * \retval [true, if the bit is set; false, if not]
+ */
+bool LoRaMacClassBIsAcquisitionPending( void );
+
+/*!
+ * \brief Verifies if the acquisition timer bit is set
+ *
+ * \retval [true, if the bit is set; false, if not]
+ */
+bool LoRaMacClassBIsAcquisitionTimerSet( void );
+
+/*!
+ * \brief Verifies if the beacon mode active bit is set
+ *
+ * \retval [true, if the bit is set; false, if not]
+ */
+bool LoRaMacClassBIsBeaconModeActive( void );
+
+/*!
+ * \brief Stops the beacon and ping slot operation
+ */
+void LoRaMacClassBHaltBeaconing( void );
+
+/*!
+ * \brief Resumes the beacon and ping slot operation
+ */
+void LoRaMacClassBResumeBeaconing( void );
+
+/*!
+ * \brief Sets the periodicity of the ping slots
+ *
+ * \param [IN] periodicity Periodicity
+ */
+void LoRaMacClassBSetPingSlotInfo( uint8_t periodicity );
+
+/*!
+ * \brief Switches the device class
+ *
+ * \param [IN] nextClass Device class to switch to
+ *
+ * \retval LoRaMacStatus_t Status of the operation.
+ */
+LoRaMacStatus_t LoRaMacClassBSwitchClass( DeviceClass_t nextClass );
+
+/*!
+ * \brief LoRaMAC ClassB MIB-Get
+ *
+ * \details The mac information base service to get attributes of the LoRaMac
+ * Class B layer.
+ *
+ * \param [IN] mibRequest - MIB-GET-Request to perform. Refer to \ref MibRequestConfirm_t.
+ *
+ * \retval LoRaMacStatus_t Status of the operation. Possible returns are:
+ * \ref LORAMAC_STATUS_OK,
+ * \ref LORAMAC_STATUS_SERVICE_UNKNOWN,
+ * \ref LORAMAC_STATUS_PARAMETER_INVALID.
+ */
+LoRaMacStatus_t LoRaMacClassBMibGetRequestConfirm( MibRequestConfirm_t *mibGet );
+
+/*!
+ * \brief LoRaMAC Class B MIB-Set
+ *
+ * \details The mac information base service to set attributes of the LoRaMac
+ * Class B layer.
+ *
+ * \param [IN] mibRequest - MIB-SET-Request to perform. Refer to \ref MibRequestConfirm_t.
+ *
+ * \retval LoRaMacStatus_t Status of the operation. Possible returns are:
+ * \ref LORAMAC_STATUS_OK,
+ * \ref LORAMAC_STATUS_BUSY,
+ * \ref LORAMAC_STATUS_SERVICE_UNKNOWN,
+ * \ref LORAMAC_STATUS_PARAMETER_INVALID.
+ */
+LoRaMacStatus_t LoRaMacMibClassBSetRequestConfirm( MibRequestConfirm_t *mibSet );
+
+/*!
+ * \brief This function handles the PING_SLOT_FREQ_ANS
+ */
+void LoRaMacClassBPingSlotInfoAns( void );
+
+/*!
+ * \brief This function handles the PING_SLOT_CHANNEL_REQ
+ *
+ * \param [IN] datarate Device class to switch to
+ * \param [IN] frequency Device class to switch to
+ *
+ * \retval Status for the MAC answer.
+ */
+uint8_t LoRaMacClassBPingSlotChannelReq( uint8_t datarate, uint32_t frequency );
+
+/*!
+ * \brief This function handles the BEACON_TIMING_ANS
+ *
+ * \param [IN] beaconTimingDelay The beacon timing delay
+ * \param [IN] beaconTimingChannel The beacon timing channel
+ */
+void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel );
+
+/*!
+ * \brief This function handles the BEACON_FREQ_REQ
+ *
+ * \param [IN] frequency Frequency to set
+ *
+ * \retval [true, if MAC shall send an answer; false, if not]
+ */
+bool LoRaMacClassBBeaconFreqReq( uint32_t frequency );
+
+/*!
+ * \brief Queries the beacon reserved time
+ *
+ * \retval Beacon reserved time
+ */
+TimerTime_t LoRaMacClassBGetBeaconReservedTime( void );
+
+/*!
+ * \brief Queries the ping slot window time
+ *
+ * \retval Ping slot window time
+ */
+TimerTime_t LoRaMacClassBGetPingSlotWinTime( void );
+
+/*!
+ * \brief Queries the ping slot window time
+ *
+ * \param [IN] txTimeOnAir TX time on air for the next uplink
+ *
+ * \retval Returns the time the uplink should be delayed
+ */
+TimerTime_t LoRaMacClassBIsUplinkCollision( TimerTime_t txTimeOnAir );
+
+#endif // __LORAMACCLASSB_H__
--- a/LoRaMacCrypto.cpp Tue Aug 08 19:27:17 2017 -0400
+++ b/LoRaMacCrypto.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -15,7 +15,7 @@
License: Revised BSD License, see LICENSE.TXT file include in the project
-Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jäckle ( STACKFORCE )
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
*/
#include <stdlib.h>
#include <stdint.h>
@@ -200,3 +200,37 @@
memcpy1( nonce + 7, pDevNonce, 2 );
aes_encrypt( nonce, appSKey, &AesContext );
}
+
+void LoRaMacBeaconComputePingOffset( uint64_t beaconTime, uint32_t address, uint16_t pingPeriod, uint16_t *pingOffset )
+{
+ uint8_t zeroKey[16];
+ uint8_t buffer[16];
+ uint8_t cipher[16];
+ uint32_t result = 0;
+ /* Refer to chapter 15.2 of the LoRaWAN specification v1.1. The beacon time
+ * GPS time in seconds modulo 2^32
+ */
+ uint32_t time = ( beaconTime % ( ( ( uint64_t ) 1 ) << 32 ) );
+
+ memset1( zeroKey, 0, 16 );
+ memset1( buffer, 0, 16 );
+ memset1( cipher, 0, 16 );
+ memset1( AesContext.ksch, '\0', 240 );
+
+ buffer[0] = ( time ) & 0xFF;
+ buffer[1] = ( time >> 8 ) & 0xFF;
+ buffer[2] = ( time >> 16 ) & 0xFF;
+ buffer[3] = ( time >> 24 ) & 0xFF;
+
+ buffer[4] = ( address ) & 0xFF;
+ buffer[5] = ( address >> 8 ) & 0xFF;
+ buffer[6] = ( address >> 16 ) & 0xFF;
+ buffer[7] = ( address >> 24 ) & 0xFF;
+
+ aes_set_key( zeroKey, 16, &AesContext );
+ aes_encrypt( buffer, cipher, &AesContext );
+
+ result = ( ( ( uint32_t ) cipher[0] ) + ( ( ( uint32_t ) cipher[1] ) * 256 ) );
+
+ *pingOffset = ( uint16_t )( result % pingPeriod );
+}
--- a/LoRaMacCrypto.h Tue Aug 08 19:27:17 2017 -0400 +++ b/LoRaMacCrypto.h Wed Aug 09 16:20:21 2017 -0400 @@ -26,7 +26,7 @@ * * \author Gregory Cristian ( Semtech ) * - * \author Daniel Jäckle ( STACKFORCE ) + * \author Daniel Jaeckle ( STACKFORCE ) * * \defgroup LORAMAC_CRYPTO LoRa MAC layer cryptography implementation * This module covers the implementation of cryptographic functions @@ -106,6 +106,16 @@ */ void LoRaMacJoinComputeSKeys( const uint8_t *key, const uint8_t *appNonce, uint16_t devNonce, uint8_t *nwkSKey, uint8_t *appSKey ); +/*! + * Computes the LoRaMAC join frame decryption + * + * \param [IN] beaconTime - Time of the recent received beacon + * \param [IN] address - Frame address + * \param [IN] pingPeriod - Ping period of the node + * \param [OUT] pingOffset - Pseudo random ping offset + */ +void LoRaMacBeaconComputePingOffset( uint64_t beaconTime, uint32_t address, uint16_t pingPeriod, uint16_t *pingOffset ); + /*! \} defgroup LORAMAC */ #endif // __LORAMAC_CRYPTO_H__
--- a/LoRaMacTest.h Tue Aug 08 19:27:17 2017 -0400 +++ b/LoRaMacTest.h Wed Aug 09 16:20:21 2017 -0400 @@ -26,7 +26,7 @@ * * \author Gregory Cristian ( Semtech ) * - * \author Daniel Jäckle ( STACKFORCE ) + * \author Daniel Jaeckle ( STACKFORCE ) * * \defgroup LORAMACTEST LoRa MAC layer test function implementation * This module specifies the API implementation of test function of the LoRaMAC layer.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/Region.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1078 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "timer.h"
+#include "LoRaMac.h"
+
+
+
+// Regional includes
+#include "Region.h"
+
+
+
+// Setup regions
+#ifdef REGION_AS923
+#include "RegionAS923.h"
+#define AS923_CASE case LORAMAC_REGION_AS923:
+#define AS923_IS_ACTIVE( ) AS923_CASE { return true; }
+#define AS923_GET_PHY_PARAM( ) AS923_CASE { return RegionAS923GetPhyParam( getPhy ); }
+#define AS923_SET_BAND_TX_DONE( ) AS923_CASE { RegionAS923SetBandTxDone( txDone ); break; }
+#define AS923_INIT_DEFAULTS( ) AS923_CASE { RegionAS923InitDefaults( type ); break; }
+#define AS923_VERIFY( ) AS923_CASE { return RegionAS923Verify( verify, phyAttribute ); }
+#define AS923_APPLY_CF_LIST( ) AS923_CASE { RegionAS923ApplyCFList( applyCFList ); break; }
+#define AS923_CHAN_MASK_SET( ) AS923_CASE { return RegionAS923ChanMaskSet( chanMaskSet ); }
+#define AS923_ADR_NEXT( ) AS923_CASE { return RegionAS923AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define AS923_COMPUTE_RX_WINDOW_PARAMETERS( ) AS923_CASE { RegionAS923ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define AS923_RX_CONFIG( ) AS923_CASE { return RegionAS923RxConfig( rxConfig, datarate ); }
+#define AS923_TX_CONFIG( ) AS923_CASE { return RegionAS923TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define AS923_LINK_ADR_REQ( ) AS923_CASE { return RegionAS923LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define AS923_RX_PARAM_SETUP_REQ( ) AS923_CASE { return RegionAS923RxParamSetupReq( rxParamSetupReq ); }
+#define AS923_NEW_CHANNEL_REQ( ) AS923_CASE { return RegionAS923NewChannelReq( newChannelReq ); }
+#define AS923_TX_PARAM_SETUP_REQ( ) AS923_CASE { return RegionAS923TxParamSetupReq( txParamSetupReq ); }
+#define AS923_DL_CHANNEL_REQ( ) AS923_CASE { return RegionAS923DlChannelReq( dlChannelReq ); }
+#define AS923_ALTERNATE_DR( ) AS923_CASE { return RegionAS923AlternateDr( alternateDr ); }
+#define AS923_CALC_BACKOFF( ) AS923_CASE { RegionAS923CalcBackOff( calcBackOff ); break; }
+#define AS923_NEXT_CHANNEL( ) AS923_CASE { return RegionAS923NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define AS923_CHANNEL_ADD( ) AS923_CASE { return RegionAS923ChannelAdd( channelAdd ); }
+#define AS923_CHANNEL_REMOVE( ) AS923_CASE { return RegionAS923ChannelsRemove( channelRemove ); }
+#define AS923_SET_CONTINUOUS_WAVE( ) AS923_CASE { RegionAS923SetContinuousWave( continuousWave ); break; }
+#define AS923_APPLY_DR_OFFSET( ) AS923_CASE { return RegionAS923ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define AS923_RX_BEACON_SETUP( )
+#else
+#define AS923_IS_ACTIVE( )
+#define AS923_GET_PHY_PARAM( )
+#define AS923_SET_BAND_TX_DONE( )
+#define AS923_INIT_DEFAULTS( )
+#define AS923_VERIFY( )
+#define AS923_APPLY_CF_LIST( )
+#define AS923_CHAN_MASK_SET( )
+#define AS923_ADR_NEXT( )
+#define AS923_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define AS923_RX_CONFIG( )
+#define AS923_TX_CONFIG( )
+#define AS923_LINK_ADR_REQ( )
+#define AS923_RX_PARAM_SETUP_REQ( )
+#define AS923_NEW_CHANNEL_REQ( )
+#define AS923_TX_PARAM_SETUP_REQ( )
+#define AS923_DL_CHANNEL_REQ( )
+#define AS923_ALTERNATE_DR( )
+#define AS923_CALC_BACKOFF( )
+#define AS923_NEXT_CHANNEL( )
+#define AS923_CHANNEL_ADD( )
+#define AS923_CHANNEL_REMOVE( )
+#define AS923_SET_CONTINUOUS_WAVE( )
+#define AS923_APPLY_DR_OFFSET( )
+#define AS923_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_AU915
+#include "RegionAU915.h"
+#define AU915_CASE case LORAMAC_REGION_AU915:
+#define AU915_IS_ACTIVE( ) AU915_CASE { return true; }
+#define AU915_GET_PHY_PARAM( ) AU915_CASE { return RegionAU915GetPhyParam( getPhy ); }
+#define AU915_SET_BAND_TX_DONE( ) AU915_CASE { RegionAU915SetBandTxDone( txDone ); break; }
+#define AU915_INIT_DEFAULTS( ) AU915_CASE { RegionAU915InitDefaults( type ); break; }
+#define AU915_VERIFY( ) AU915_CASE { return RegionAU915Verify( verify, phyAttribute ); }
+#define AU915_APPLY_CF_LIST( ) AU915_CASE { RegionAU915ApplyCFList( applyCFList ); break; }
+#define AU915_CHAN_MASK_SET( ) AU915_CASE { return RegionAU915ChanMaskSet( chanMaskSet ); }
+#define AU915_ADR_NEXT( ) AU915_CASE { return RegionAU915AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define AU915_COMPUTE_RX_WINDOW_PARAMETERS( ) AU915_CASE { RegionAU915ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define AU915_RX_CONFIG( ) AU915_CASE { return RegionAU915RxConfig( rxConfig, datarate ); }
+#define AU915_TX_CONFIG( ) AU915_CASE { return RegionAU915TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define AU915_LINK_ADR_REQ( ) AU915_CASE { return RegionAU915LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define AU915_RX_PARAM_SETUP_REQ( ) AU915_CASE { return RegionAU915RxParamSetupReq( rxParamSetupReq ); }
+#define AU915_NEW_CHANNEL_REQ( ) AU915_CASE { return RegionAU915NewChannelReq( newChannelReq ); }
+#define AU915_TX_PARAM_SETUP_REQ( ) AU915_CASE { return RegionAU915TxParamSetupReq( txParamSetupReq ); }
+#define AU915_DL_CHANNEL_REQ( ) AU915_CASE { return RegionAU915DlChannelReq( dlChannelReq ); }
+#define AU915_ALTERNATE_DR( ) AU915_CASE { return RegionAU915AlternateDr( alternateDr ); }
+#define AU915_CALC_BACKOFF( ) AU915_CASE { RegionAU915CalcBackOff( calcBackOff ); break; }
+#define AU915_NEXT_CHANNEL( ) AU915_CASE { return RegionAU915NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define AU915_CHANNEL_ADD( ) AU915_CASE { return RegionAU915ChannelAdd( channelAdd ); }
+#define AU915_CHANNEL_REMOVE( ) AU915_CASE { return RegionAU915ChannelsRemove( channelRemove ); }
+#define AU915_SET_CONTINUOUS_WAVE( ) AU915_CASE { RegionAU915SetContinuousWave( continuousWave ); break; }
+#define AU915_APPLY_DR_OFFSET( ) AU915_CASE { return RegionAU915ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define AU915_RX_BEACON_SETUP( )
+#else
+#define AU915_IS_ACTIVE( )
+#define AU915_GET_PHY_PARAM( )
+#define AU915_SET_BAND_TX_DONE( )
+#define AU915_INIT_DEFAULTS( )
+#define AU915_VERIFY( )
+#define AU915_APPLY_CF_LIST( )
+#define AU915_CHAN_MASK_SET( )
+#define AU915_ADR_NEXT( )
+#define AU915_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define AU915_RX_CONFIG( )
+#define AU915_TX_CONFIG( )
+#define AU915_LINK_ADR_REQ( )
+#define AU915_RX_PARAM_SETUP_REQ( )
+#define AU915_NEW_CHANNEL_REQ( )
+#define AU915_TX_PARAM_SETUP_REQ( )
+#define AU915_DL_CHANNEL_REQ( )
+#define AU915_ALTERNATE_DR( )
+#define AU915_CALC_BACKOFF( )
+#define AU915_NEXT_CHANNEL( )
+#define AU915_CHANNEL_ADD( )
+#define AU915_CHANNEL_REMOVE( )
+#define AU915_SET_CONTINUOUS_WAVE( )
+#define AU915_APPLY_DR_OFFSET( )
+#define AU915_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_CN470
+#include "RegionCN470.h"
+#define CN470_CASE case LORAMAC_REGION_CN470:
+#define CN470_IS_ACTIVE( ) CN470_CASE { return true; }
+#define CN470_GET_PHY_PARAM( ) CN470_CASE { return RegionCN470GetPhyParam( getPhy ); }
+#define CN470_SET_BAND_TX_DONE( ) CN470_CASE { RegionCN470SetBandTxDone( txDone ); break; }
+#define CN470_INIT_DEFAULTS( ) CN470_CASE { RegionCN470InitDefaults( type ); break; }
+#define CN470_VERIFY( ) CN470_CASE { return RegionCN470Verify( verify, phyAttribute ); }
+#define CN470_APPLY_CF_LIST( ) CN470_CASE { RegionCN470ApplyCFList( applyCFList ); break; }
+#define CN470_CHAN_MASK_SET( ) CN470_CASE { return RegionCN470ChanMaskSet( chanMaskSet ); }
+#define CN470_ADR_NEXT( ) CN470_CASE { return RegionCN470AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define CN470_COMPUTE_RX_WINDOW_PARAMETERS( ) CN470_CASE { RegionCN470ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define CN470_RX_CONFIG( ) CN470_CASE { return RegionCN470RxConfig( rxConfig, datarate ); }
+#define CN470_TX_CONFIG( ) CN470_CASE { return RegionCN470TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define CN470_LINK_ADR_REQ( ) CN470_CASE { return RegionCN470LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define CN470_RX_PARAM_SETUP_REQ( ) CN470_CASE { return RegionCN470RxParamSetupReq( rxParamSetupReq ); }
+#define CN470_NEW_CHANNEL_REQ( ) CN470_CASE { return RegionCN470NewChannelReq( newChannelReq ); }
+#define CN470_TX_PARAM_SETUP_REQ( ) CN470_CASE { return RegionCN470TxParamSetupReq( txParamSetupReq ); }
+#define CN470_DL_CHANNEL_REQ( ) CN470_CASE { return RegionCN470DlChannelReq( dlChannelReq ); }
+#define CN470_ALTERNATE_DR( ) CN470_CASE { return RegionCN470AlternateDr( alternateDr ); }
+#define CN470_CALC_BACKOFF( ) CN470_CASE { RegionCN470CalcBackOff( calcBackOff ); break; }
+#define CN470_NEXT_CHANNEL( ) CN470_CASE { return RegionCN470NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define CN470_CHANNEL_ADD( ) CN470_CASE { return RegionCN470ChannelAdd( channelAdd ); }
+#define CN470_CHANNEL_REMOVE( ) CN470_CASE { return RegionCN470ChannelsRemove( channelRemove ); }
+#define CN470_SET_CONTINUOUS_WAVE( ) CN470_CASE { RegionCN470SetContinuousWave( continuousWave ); break; }
+#define CN470_APPLY_DR_OFFSET( ) CN470_CASE { return RegionCN470ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define CN470_RX_BEACON_SETUP( )
+#else
+#define CN470_IS_ACTIVE( )
+#define CN470_GET_PHY_PARAM( )
+#define CN470_SET_BAND_TX_DONE( )
+#define CN470_INIT_DEFAULTS( )
+#define CN470_VERIFY( )
+#define CN470_APPLY_CF_LIST( )
+#define CN470_CHAN_MASK_SET( )
+#define CN470_ADR_NEXT( )
+#define CN470_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define CN470_RX_CONFIG( )
+#define CN470_TX_CONFIG( )
+#define CN470_LINK_ADR_REQ( )
+#define CN470_RX_PARAM_SETUP_REQ( )
+#define CN470_NEW_CHANNEL_REQ( )
+#define CN470_TX_PARAM_SETUP_REQ( )
+#define CN470_DL_CHANNEL_REQ( )
+#define CN470_ALTERNATE_DR( )
+#define CN470_CALC_BACKOFF( )
+#define CN470_NEXT_CHANNEL( )
+#define CN470_CHANNEL_ADD( )
+#define CN470_CHANNEL_REMOVE( )
+#define CN470_SET_CONTINUOUS_WAVE( )
+#define CN470_APPLY_DR_OFFSET( )
+#define CN470_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_CN779
+#include "RegionCN779.h"
+#define CN779_CASE case LORAMAC_REGION_CN779:
+#define CN779_IS_ACTIVE( ) CN779_CASE { return true; }
+#define CN779_GET_PHY_PARAM( ) CN779_CASE { return RegionCN779GetPhyParam( getPhy ); }
+#define CN779_SET_BAND_TX_DONE( ) CN779_CASE { RegionCN779SetBandTxDone( txDone ); break; }
+#define CN779_INIT_DEFAULTS( ) CN779_CASE { RegionCN779InitDefaults( type ); break; }
+#define CN779_VERIFY( ) CN779_CASE { return RegionCN779Verify( verify, phyAttribute ); }
+#define CN779_APPLY_CF_LIST( ) CN779_CASE { RegionCN779ApplyCFList( applyCFList ); break; }
+#define CN779_CHAN_MASK_SET( ) CN779_CASE { return RegionCN779ChanMaskSet( chanMaskSet ); }
+#define CN779_ADR_NEXT( ) CN779_CASE { return RegionCN779AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define CN779_COMPUTE_RX_WINDOW_PARAMETERS( ) CN779_CASE { RegionCN779ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define CN779_RX_CONFIG( ) CN779_CASE { return RegionCN779RxConfig( rxConfig, datarate ); }
+#define CN779_TX_CONFIG( ) CN779_CASE { return RegionCN779TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define CN779_LINK_ADR_REQ( ) CN779_CASE { return RegionCN779LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define CN779_RX_PARAM_SETUP_REQ( ) CN779_CASE { return RegionCN779RxParamSetupReq( rxParamSetupReq ); }
+#define CN779_NEW_CHANNEL_REQ( ) CN779_CASE { return RegionCN779NewChannelReq( newChannelReq ); }
+#define CN779_TX_PARAM_SETUP_REQ( ) CN779_CASE { return RegionCN779TxParamSetupReq( txParamSetupReq ); }
+#define CN779_DL_CHANNEL_REQ( ) CN779_CASE { return RegionCN779DlChannelReq( dlChannelReq ); }
+#define CN779_ALTERNATE_DR( ) CN779_CASE { return RegionCN779AlternateDr( alternateDr ); }
+#define CN779_CALC_BACKOFF( ) CN779_CASE { RegionCN779CalcBackOff( calcBackOff ); break; }
+#define CN779_NEXT_CHANNEL( ) CN779_CASE { return RegionCN779NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define CN779_CHANNEL_ADD( ) CN779_CASE { return RegionCN779ChannelAdd( channelAdd ); }
+#define CN779_CHANNEL_REMOVE( ) CN779_CASE { return RegionCN779ChannelsRemove( channelRemove ); }
+#define CN779_SET_CONTINUOUS_WAVE( ) CN779_CASE { RegionCN779SetContinuousWave( continuousWave ); break; }
+#define CN779_APPLY_DR_OFFSET( ) CN779_CASE { return RegionCN779ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define CN779_RX_BEACON_SETUP( )
+#else
+#define CN779_IS_ACTIVE( )
+#define CN779_GET_PHY_PARAM( )
+#define CN779_SET_BAND_TX_DONE( )
+#define CN779_INIT_DEFAULTS( )
+#define CN779_VERIFY( )
+#define CN779_APPLY_CF_LIST( )
+#define CN779_CHAN_MASK_SET( )
+#define CN779_ADR_NEXT( )
+#define CN779_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define CN779_RX_CONFIG( )
+#define CN779_TX_CONFIG( )
+#define CN779_LINK_ADR_REQ( )
+#define CN779_RX_PARAM_SETUP_REQ( )
+#define CN779_NEW_CHANNEL_REQ( )
+#define CN779_TX_PARAM_SETUP_REQ( )
+#define CN779_DL_CHANNEL_REQ( )
+#define CN779_ALTERNATE_DR( )
+#define CN779_CALC_BACKOFF( )
+#define CN779_NEXT_CHANNEL( )
+#define CN779_CHANNEL_ADD( )
+#define CN779_CHANNEL_REMOVE( )
+#define CN779_SET_CONTINUOUS_WAVE( )
+#define CN779_APPLY_DR_OFFSET( )
+#define CN779_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_EU433
+#include "RegionEU433.h"
+#define EU433_CASE case LORAMAC_REGION_EU433:
+#define EU433_IS_ACTIVE( ) EU433_CASE { return true; }
+#define EU433_GET_PHY_PARAM( ) EU433_CASE { return RegionEU433GetPhyParam( getPhy ); }
+#define EU433_SET_BAND_TX_DONE( ) EU433_CASE { RegionEU433SetBandTxDone( txDone ); break; }
+#define EU433_INIT_DEFAULTS( ) EU433_CASE { RegionEU433InitDefaults( type ); break; }
+#define EU433_VERIFY( ) EU433_CASE { return RegionEU433Verify( verify, phyAttribute ); }
+#define EU433_APPLY_CF_LIST( ) EU433_CASE { RegionEU433ApplyCFList( applyCFList ); break; }
+#define EU433_CHAN_MASK_SET( ) EU433_CASE { return RegionEU433ChanMaskSet( chanMaskSet ); }
+#define EU433_ADR_NEXT( ) EU433_CASE { return RegionEU433AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define EU433_COMPUTE_RX_WINDOW_PARAMETERS( ) EU433_CASE { RegionEU433ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define EU433_RX_CONFIG( ) EU433_CASE { return RegionEU433RxConfig( rxConfig, datarate ); }
+#define EU433_TX_CONFIG( ) EU433_CASE { return RegionEU433TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define EU433_LINK_ADR_REQ( ) EU433_CASE { return RegionEU433LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define EU433_RX_PARAM_SETUP_REQ( ) EU433_CASE { return RegionEU433RxParamSetupReq( rxParamSetupReq ); }
+#define EU433_NEW_CHANNEL_REQ( ) EU433_CASE { return RegionEU433NewChannelReq( newChannelReq ); }
+#define EU433_TX_PARAM_SETUP_REQ( ) EU433_CASE { return RegionEU433TxParamSetupReq( txParamSetupReq ); }
+#define EU433_DL_CHANNEL_REQ( ) EU433_CASE { return RegionEU433DlChannelReq( dlChannelReq ); }
+#define EU433_ALTERNATE_DR( ) EU433_CASE { return RegionEU433AlternateDr( alternateDr ); }
+#define EU433_CALC_BACKOFF( ) EU433_CASE { RegionEU433CalcBackOff( calcBackOff ); break; }
+#define EU433_NEXT_CHANNEL( ) EU433_CASE { return RegionEU433NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define EU433_CHANNEL_ADD( ) EU433_CASE { return RegionEU433ChannelAdd( channelAdd ); }
+#define EU433_CHANNEL_REMOVE( ) EU433_CASE { return RegionEU433ChannelsRemove( channelRemove ); }
+#define EU433_SET_CONTINUOUS_WAVE( ) EU433_CASE { RegionEU433SetContinuousWave( continuousWave ); break; }
+#define EU433_APPLY_DR_OFFSET( ) EU433_CASE { return RegionEU433ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define EU433_RX_BEACON_SETUP( )
+#else
+#define EU433_IS_ACTIVE( )
+#define EU433_GET_PHY_PARAM( )
+#define EU433_SET_BAND_TX_DONE( )
+#define EU433_INIT_DEFAULTS( )
+#define EU433_VERIFY( )
+#define EU433_APPLY_CF_LIST( )
+#define EU433_CHAN_MASK_SET( )
+#define EU433_ADR_NEXT( )
+#define EU433_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define EU433_RX_CONFIG( )
+#define EU433_TX_CONFIG( )
+#define EU433_LINK_ADR_REQ( )
+#define EU433_RX_PARAM_SETUP_REQ( )
+#define EU433_NEW_CHANNEL_REQ( )
+#define EU433_TX_PARAM_SETUP_REQ( )
+#define EU433_DL_CHANNEL_REQ( )
+#define EU433_ALTERNATE_DR( )
+#define EU433_CALC_BACKOFF( )
+#define EU433_NEXT_CHANNEL( )
+#define EU433_CHANNEL_ADD( )
+#define EU433_CHANNEL_REMOVE( )
+#define EU433_SET_CONTINUOUS_WAVE( )
+#define EU433_APPLY_DR_OFFSET( )
+#define EU433_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_EU868
+#include "RegionEU868.h"
+#define EU868_CASE case LORAMAC_REGION_EU868:
+#define EU868_IS_ACTIVE( ) EU868_CASE { return true; }
+#define EU868_GET_PHY_PARAM( ) EU868_CASE { return RegionEU868GetPhyParam( getPhy ); }
+#define EU868_SET_BAND_TX_DONE( ) EU868_CASE { RegionEU868SetBandTxDone( txDone ); break; }
+#define EU868_INIT_DEFAULTS( ) EU868_CASE { RegionEU868InitDefaults( type ); break; }
+#define EU868_VERIFY( ) EU868_CASE { return RegionEU868Verify( verify, phyAttribute ); }
+#define EU868_APPLY_CF_LIST( ) EU868_CASE { RegionEU868ApplyCFList( applyCFList ); break; }
+#define EU868_CHAN_MASK_SET( ) EU868_CASE { return RegionEU868ChanMaskSet( chanMaskSet ); }
+#define EU868_ADR_NEXT( ) EU868_CASE { return RegionEU868AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define EU868_COMPUTE_RX_WINDOW_PARAMETERS( ) EU868_CASE { RegionEU868ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define EU868_RX_CONFIG( ) EU868_CASE { return RegionEU868RxConfig( rxConfig, datarate ); }
+#define EU868_TX_CONFIG( ) EU868_CASE { return RegionEU868TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define EU868_LINK_ADR_REQ( ) EU868_CASE { return RegionEU868LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define EU868_RX_PARAM_SETUP_REQ( ) EU868_CASE { return RegionEU868RxParamSetupReq( rxParamSetupReq ); }
+#define EU868_NEW_CHANNEL_REQ( ) EU868_CASE { return RegionEU868NewChannelReq( newChannelReq ); }
+#define EU868_TX_PARAM_SETUP_REQ( ) EU868_CASE { return RegionEU868TxParamSetupReq( txParamSetupReq ); }
+#define EU868_DL_CHANNEL_REQ( ) EU868_CASE { return RegionEU868DlChannelReq( dlChannelReq ); }
+#define EU868_ALTERNATE_DR( ) EU868_CASE { return RegionEU868AlternateDr( alternateDr ); }
+#define EU868_CALC_BACKOFF( ) EU868_CASE { RegionEU868CalcBackOff( calcBackOff ); break; }
+#define EU868_NEXT_CHANNEL( ) EU868_CASE { return RegionEU868NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define EU868_CHANNEL_ADD( ) EU868_CASE { return RegionEU868ChannelAdd( channelAdd ); }
+#define EU868_CHANNEL_REMOVE( ) EU868_CASE { return RegionEU868ChannelsRemove( channelRemove ); }
+#define EU868_SET_CONTINUOUS_WAVE( ) EU868_CASE { RegionEU868SetContinuousWave( continuousWave ); break; }
+#define EU868_APPLY_DR_OFFSET( ) EU868_CASE { return RegionEU868ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define EU868_RX_BEACON_SETUP( ) EU868_CASE { RegionEU868RxBeaconSetup( rxBeaconSetup, outDr, beaconChannelSet ); }
+#else
+#define EU868_IS_ACTIVE( )
+#define EU868_GET_PHY_PARAM( )
+#define EU868_SET_BAND_TX_DONE( )
+#define EU868_INIT_DEFAULTS( )
+#define EU868_VERIFY( )
+#define EU868_APPLY_CF_LIST( )
+#define EU868_CHAN_MASK_SET( )
+#define EU868_ADR_NEXT( )
+#define EU868_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define EU868_RX_CONFIG( )
+#define EU868_TX_CONFIG( )
+#define EU868_LINK_ADR_REQ( )
+#define EU868_RX_PARAM_SETUP_REQ( )
+#define EU868_NEW_CHANNEL_REQ( )
+#define EU868_TX_PARAM_SETUP_REQ( )
+#define EU868_DL_CHANNEL_REQ( )
+#define EU868_ALTERNATE_DR( )
+#define EU868_CALC_BACKOFF( )
+#define EU868_NEXT_CHANNEL( )
+#define EU868_CHANNEL_ADD( )
+#define EU868_CHANNEL_REMOVE( )
+#define EU868_SET_CONTINUOUS_WAVE( )
+#define EU868_APPLY_DR_OFFSET( )
+#define EU868_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_KR920
+#include "RegionKR920.h"
+#define KR920_CASE case LORAMAC_REGION_KR920:
+#define KR920_IS_ACTIVE( ) KR920_CASE { return true; }
+#define KR920_GET_PHY_PARAM( ) KR920_CASE { return RegionKR920GetPhyParam( getPhy ); }
+#define KR920_SET_BAND_TX_DONE( ) KR920_CASE { RegionKR920SetBandTxDone( txDone ); break; }
+#define KR920_INIT_DEFAULTS( ) KR920_CASE { RegionKR920InitDefaults( type ); break; }
+#define KR920_VERIFY( ) KR920_CASE { return RegionKR920Verify( verify, phyAttribute ); }
+#define KR920_APPLY_CF_LIST( ) KR920_CASE { RegionKR920ApplyCFList( applyCFList ); break; }
+#define KR920_CHAN_MASK_SET( ) KR920_CASE { return RegionKR920ChanMaskSet( chanMaskSet ); }
+#define KR920_ADR_NEXT( ) KR920_CASE { return RegionKR920AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define KR920_COMPUTE_RX_WINDOW_PARAMETERS( ) KR920_CASE { RegionKR920ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define KR920_RX_CONFIG( ) KR920_CASE { return RegionKR920RxConfig( rxConfig, datarate ); }
+#define KR920_TX_CONFIG( ) KR920_CASE { return RegionKR920TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define KR920_LINK_ADR_REQ( ) KR920_CASE { return RegionKR920LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define KR920_RX_PARAM_SETUP_REQ( ) KR920_CASE { return RegionKR920RxParamSetupReq( rxParamSetupReq ); }
+#define KR920_NEW_CHANNEL_REQ( ) KR920_CASE { return RegionKR920NewChannelReq( newChannelReq ); }
+#define KR920_TX_PARAM_SETUP_REQ( ) KR920_CASE { return RegionKR920TxParamSetupReq( txParamSetupReq ); }
+#define KR920_DL_CHANNEL_REQ( ) KR920_CASE { return RegionKR920DlChannelReq( dlChannelReq ); }
+#define KR920_ALTERNATE_DR( ) KR920_CASE { return RegionKR920AlternateDr( alternateDr ); }
+#define KR920_CALC_BACKOFF( ) KR920_CASE { RegionKR920CalcBackOff( calcBackOff ); break; }
+#define KR920_NEXT_CHANNEL( ) KR920_CASE { return RegionKR920NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define KR920_CHANNEL_ADD( ) KR920_CASE { return RegionKR920ChannelAdd( channelAdd ); }
+#define KR920_CHANNEL_REMOVE( ) KR920_CASE { return RegionKR920ChannelsRemove( channelRemove ); }
+#define KR920_SET_CONTINUOUS_WAVE( ) KR920_CASE { RegionKR920SetContinuousWave( continuousWave ); break; }
+#define KR920_APPLY_DR_OFFSET( ) KR920_CASE { return RegionKR920ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define KR920_RX_BEACON_SETUP( )
+#else
+#define KR920_IS_ACTIVE( )
+#define KR920_GET_PHY_PARAM( )
+#define KR920_SET_BAND_TX_DONE( )
+#define KR920_INIT_DEFAULTS( )
+#define KR920_VERIFY( )
+#define KR920_APPLY_CF_LIST( )
+#define KR920_CHAN_MASK_SET( )
+#define KR920_ADR_NEXT( )
+#define KR920_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define KR920_RX_CONFIG( )
+#define KR920_TX_CONFIG( )
+#define KR920_LINK_ADR_REQ( )
+#define KR920_RX_PARAM_SETUP_REQ( )
+#define KR920_NEW_CHANNEL_REQ( )
+#define KR920_TX_PARAM_SETUP_REQ( )
+#define KR920_DL_CHANNEL_REQ( )
+#define KR920_ALTERNATE_DR( )
+#define KR920_CALC_BACKOFF( )
+#define KR920_NEXT_CHANNEL( )
+#define KR920_CHANNEL_ADD( )
+#define KR920_CHANNEL_REMOVE( )
+#define KR920_SET_CONTINUOUS_WAVE( )
+#define KR920_APPLY_DR_OFFSET( )
+#define KR920_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_IN865
+#include "RegionIN865.h"
+#define IN865_CASE case LORAMAC_REGION_IN865:
+#define IN865_IS_ACTIVE( ) IN865_CASE { return true; }
+#define IN865_GET_PHY_PARAM( ) IN865_CASE { return RegionIN865GetPhyParam( getPhy ); }
+#define IN865_SET_BAND_TX_DONE( ) IN865_CASE { RegionIN865SetBandTxDone( txDone ); break; }
+#define IN865_INIT_DEFAULTS( ) IN865_CASE { RegionIN865InitDefaults( type ); break; }
+#define IN865_VERIFY( ) IN865_CASE { return RegionIN865Verify( verify, phyAttribute ); }
+#define IN865_APPLY_CF_LIST( ) IN865_CASE { RegionIN865ApplyCFList( applyCFList ); break; }
+#define IN865_CHAN_MASK_SET( ) IN865_CASE { return RegionIN865ChanMaskSet( chanMaskSet ); }
+#define IN865_ADR_NEXT( ) IN865_CASE { return RegionIN865AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define IN865_COMPUTE_RX_WINDOW_PARAMETERS( ) IN865_CASE { RegionIN865ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define IN865_RX_CONFIG( ) IN865_CASE { return RegionIN865RxConfig( rxConfig, datarate ); }
+#define IN865_TX_CONFIG( ) IN865_CASE { return RegionIN865TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define IN865_LINK_ADR_REQ( ) IN865_CASE { return RegionIN865LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define IN865_RX_PARAM_SETUP_REQ( ) IN865_CASE { return RegionIN865RxParamSetupReq( rxParamSetupReq ); }
+#define IN865_NEW_CHANNEL_REQ( ) IN865_CASE { return RegionIN865NewChannelReq( newChannelReq ); }
+#define IN865_TX_PARAM_SETUP_REQ( ) IN865_CASE { return RegionIN865TxParamSetupReq( txParamSetupReq ); }
+#define IN865_DL_CHANNEL_REQ( ) IN865_CASE { return RegionIN865DlChannelReq( dlChannelReq ); }
+#define IN865_ALTERNATE_DR( ) IN865_CASE { return RegionIN865AlternateDr( alternateDr ); }
+#define IN865_CALC_BACKOFF( ) IN865_CASE { RegionIN865CalcBackOff( calcBackOff ); break; }
+#define IN865_NEXT_CHANNEL( ) IN865_CASE { return RegionIN865NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define IN865_CHANNEL_ADD( ) IN865_CASE { return RegionIN865ChannelAdd( channelAdd ); }
+#define IN865_CHANNEL_REMOVE( ) IN865_CASE { return RegionIN865ChannelsRemove( channelRemove ); }
+#define IN865_SET_CONTINUOUS_WAVE( ) IN865_CASE { RegionIN865SetContinuousWave( continuousWave ); break; }
+#define IN865_APPLY_DR_OFFSET( ) IN865_CASE { return RegionIN865ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define IN865_RX_BEACON_SETUP( )
+#else
+#define IN865_IS_ACTIVE( )
+#define IN865_GET_PHY_PARAM( )
+#define IN865_SET_BAND_TX_DONE( )
+#define IN865_INIT_DEFAULTS( )
+#define IN865_VERIFY( )
+#define IN865_APPLY_CF_LIST( )
+#define IN865_CHAN_MASK_SET( )
+#define IN865_ADR_NEXT( )
+#define IN865_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define IN865_RX_CONFIG( )
+#define IN865_TX_CONFIG( )
+#define IN865_LINK_ADR_REQ( )
+#define IN865_RX_PARAM_SETUP_REQ( )
+#define IN865_NEW_CHANNEL_REQ( )
+#define IN865_TX_PARAM_SETUP_REQ( )
+#define IN865_DL_CHANNEL_REQ( )
+#define IN865_ALTERNATE_DR( )
+#define IN865_CALC_BACKOFF( )
+#define IN865_NEXT_CHANNEL( )
+#define IN865_CHANNEL_ADD( )
+#define IN865_CHANNEL_REMOVE( )
+#define IN865_SET_CONTINUOUS_WAVE( )
+#define IN865_APPLY_DR_OFFSET( )
+#define IN865_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_US915
+#include "RegionUS915.h"
+#define US915_CASE case LORAMAC_REGION_US915:
+#define US915_IS_ACTIVE( ) US915_CASE { return true; }
+#define US915_GET_PHY_PARAM( ) US915_CASE { return RegionUS915GetPhyParam( getPhy ); }
+#define US915_SET_BAND_TX_DONE( ) US915_CASE { RegionUS915SetBandTxDone( txDone ); break; }
+#define US915_INIT_DEFAULTS( ) US915_CASE { RegionUS915InitDefaults( type ); break; }
+#define US915_VERIFY( ) US915_CASE { return RegionUS915Verify( verify, phyAttribute ); }
+#define US915_APPLY_CF_LIST( ) US915_CASE { RegionUS915ApplyCFList( applyCFList ); break; }
+#define US915_CHAN_MASK_SET( ) US915_CASE { return RegionUS915ChanMaskSet( chanMaskSet ); }
+#define US915_ADR_NEXT( ) US915_CASE { return RegionUS915AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define US915_COMPUTE_RX_WINDOW_PARAMETERS( ) US915_CASE { RegionUS915ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define US915_RX_CONFIG( ) US915_CASE { return RegionUS915RxConfig( rxConfig, datarate ); }
+#define US915_TX_CONFIG( ) US915_CASE { return RegionUS915TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define US915_LINK_ADR_REQ( ) US915_CASE { return RegionUS915LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define US915_RX_PARAM_SETUP_REQ( ) US915_CASE { return RegionUS915RxParamSetupReq( rxParamSetupReq ); }
+#define US915_NEW_CHANNEL_REQ( ) US915_CASE { return RegionUS915NewChannelReq( newChannelReq ); }
+#define US915_TX_PARAM_SETUP_REQ( ) US915_CASE { return RegionUS915TxParamSetupReq( txParamSetupReq ); }
+#define US915_DL_CHANNEL_REQ( ) US915_CASE { return RegionUS915DlChannelReq( dlChannelReq ); }
+#define US915_ALTERNATE_DR( ) US915_CASE { return RegionUS915AlternateDr( alternateDr ); }
+#define US915_CALC_BACKOFF( ) US915_CASE { RegionUS915CalcBackOff( calcBackOff ); break; }
+#define US915_NEXT_CHANNEL( ) US915_CASE { return RegionUS915NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define US915_CHANNEL_ADD( ) US915_CASE { return RegionUS915ChannelAdd( channelAdd ); }
+#define US915_CHANNEL_REMOVE( ) US915_CASE { return RegionUS915ChannelsRemove( channelRemove ); }
+#define US915_SET_CONTINUOUS_WAVE( ) US915_CASE { RegionUS915SetContinuousWave( continuousWave ); break; }
+#define US915_APPLY_DR_OFFSET( ) US915_CASE { return RegionUS915ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define US915_RX_BEACON_SETUP( )
+#else
+#define US915_IS_ACTIVE( )
+#define US915_GET_PHY_PARAM( )
+#define US915_SET_BAND_TX_DONE( )
+#define US915_INIT_DEFAULTS( )
+#define US915_VERIFY( )
+#define US915_APPLY_CF_LIST( )
+#define US915_CHAN_MASK_SET( )
+#define US915_ADR_NEXT( )
+#define US915_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define US915_RX_CONFIG( )
+#define US915_TX_CONFIG( )
+#define US915_LINK_ADR_REQ( )
+#define US915_RX_PARAM_SETUP_REQ( )
+#define US915_NEW_CHANNEL_REQ( )
+#define US915_TX_PARAM_SETUP_REQ( )
+#define US915_DL_CHANNEL_REQ( )
+#define US915_ALTERNATE_DR( )
+#define US915_CALC_BACKOFF( )
+#define US915_NEXT_CHANNEL( )
+#define US915_CHANNEL_ADD( )
+#define US915_CHANNEL_REMOVE( )
+#define US915_SET_CONTINUOUS_WAVE( )
+#define US915_APPLY_DR_OFFSET( )
+#define US915_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_US915_HYBRID
+#include "RegionUS915-Hybrid.h"
+#define US915_HYBRID_CASE case LORAMAC_REGION_US915_HYBRID:
+#define US915_HYBRID_IS_ACTIVE( ) US915_HYBRID_CASE { return true; }
+#define US915_HYBRID_GET_PHY_PARAM( ) US915_HYBRID_CASE { return RegionUS915HybridGetPhyParam( getPhy ); }
+#define US915_HYBRID_SET_BAND_TX_DONE( ) US915_HYBRID_CASE { RegionUS915HybridSetBandTxDone( txDone ); break; }
+#define US915_HYBRID_INIT_DEFAULTS( ) US915_HYBRID_CASE { RegionUS915HybridInitDefaults( type ); break; }
+#define US915_HYBRID_VERIFY( ) US915_HYBRID_CASE { return RegionUS915HybridVerify( verify, phyAttribute ); }
+#define US915_HYBRID_APPLY_CF_LIST( ) US915_HYBRID_CASE { RegionUS915HybridApplyCFList( applyCFList ); break; }
+#define US915_HYBRID_CHAN_MASK_SET( ) US915_HYBRID_CASE { return RegionUS915HybridChanMaskSet( chanMaskSet ); }
+#define US915_HYBRID_ADR_NEXT( ) US915_HYBRID_CASE { return RegionUS915HybridAdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define US915_HYBRID_COMPUTE_RX_WINDOW_PARAMETERS( ) US915_HYBRID_CASE { RegionUS915HybridComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define US915_HYBRID_RX_CONFIG( ) US915_HYBRID_CASE { return RegionUS915HybridRxConfig( rxConfig, datarate ); }
+#define US915_HYBRID_TX_CONFIG( ) US915_HYBRID_CASE { return RegionUS915HybridTxConfig( txConfig, txPower, txTimeOnAir ); }
+#define US915_HYBRID_LINK_ADR_REQ( ) US915_HYBRID_CASE { return RegionUS915HybridLinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define US915_HYBRID_RX_PARAM_SETUP_REQ( ) US915_HYBRID_CASE { return RegionUS915HybridRxParamSetupReq( rxParamSetupReq ); }
+#define US915_HYBRID_NEW_CHANNEL_REQ( ) US915_HYBRID_CASE { return RegionUS915HybridNewChannelReq( newChannelReq ); }
+#define US915_HYBRID_TX_PARAM_SETUP_REQ( ) US915_HYBRID_CASE { return RegionUS915HybridTxParamSetupReq( txParamSetupReq ); }
+#define US915_HYBRID_DL_CHANNEL_REQ( ) US915_HYBRID_CASE { return RegionUS915HybridDlChannelReq( dlChannelReq ); }
+#define US915_HYBRID_ALTERNATE_DR( ) US915_HYBRID_CASE { return RegionUS915HybridAlternateDr( alternateDr ); }
+#define US915_HYBRID_CALC_BACKOFF( ) US915_HYBRID_CASE { RegionUS915HybridCalcBackOff( calcBackOff ); break; }
+#define US915_HYBRID_NEXT_CHANNEL( ) US915_HYBRID_CASE { return RegionUS915HybridNextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define US915_HYBRID_CHANNEL_ADD( ) US915_HYBRID_CASE { return RegionUS915HybridChannelAdd( channelAdd ); }
+#define US915_HYBRID_CHANNEL_REMOVE( ) US915_HYBRID_CASE { return RegionUS915HybridChannelsRemove( channelRemove ); }
+#define US915_HYBRID_SET_CONTINUOUS_WAVE( ) US915_HYBRID_CASE { RegionUS915HybridSetContinuousWave( continuousWave ); break; }
+#define US915_HYBRID_APPLY_DR_OFFSET( ) US915_HYBRID_CASE { return RegionUS915HybridApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define US915_HYBRID_RX_BEACON_SETUP( )
+#else
+#define US915_HYBRID_IS_ACTIVE( )
+#define US915_HYBRID_GET_PHY_PARAM( )
+#define US915_HYBRID_SET_BAND_TX_DONE( )
+#define US915_HYBRID_INIT_DEFAULTS( )
+#define US915_HYBRID_VERIFY( )
+#define US915_HYBRID_APPLY_CF_LIST( )
+#define US915_HYBRID_CHAN_MASK_SET( )
+#define US915_HYBRID_ADR_NEXT( )
+#define US915_HYBRID_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define US915_HYBRID_RX_CONFIG( )
+#define US915_HYBRID_TX_CONFIG( )
+#define US915_HYBRID_LINK_ADR_REQ( )
+#define US915_HYBRID_RX_PARAM_SETUP_REQ( )
+#define US915_HYBRID_NEW_CHANNEL_REQ( )
+#define US915_HYBRID_TX_PARAM_SETUP_REQ( )
+#define US915_HYBRID_DL_CHANNEL_REQ( )
+#define US915_HYBRID_ALTERNATE_DR( )
+#define US915_HYBRID_CALC_BACKOFF( )
+#define US915_HYBRID_NEXT_CHANNEL( )
+#define US915_HYBRID_CHANNEL_ADD( )
+#define US915_HYBRID_CHANNEL_REMOVE( )
+#define US915_HYBRID_SET_CONTINUOUS_WAVE( )
+#define US915_HYBRID_APPLY_DR_OFFSET( )
+#define US915_HYBRID_RX_BEACON_SETUP( )
+#endif
+
+bool RegionIsActive( LoRaMacRegion_t region )
+{
+ switch( region )
+ {
+ AS923_IS_ACTIVE( );
+ AU915_IS_ACTIVE( );
+ CN470_IS_ACTIVE( );
+ CN779_IS_ACTIVE( );
+ EU433_IS_ACTIVE( );
+ EU868_IS_ACTIVE( );
+ KR920_IS_ACTIVE( );
+ IN865_IS_ACTIVE( );
+ US915_IS_ACTIVE( );
+ US915_HYBRID_IS_ACTIVE( );
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+PhyParam_t RegionGetPhyParam( LoRaMacRegion_t region, GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+ switch( region )
+ {
+ AS923_GET_PHY_PARAM( );
+ AU915_GET_PHY_PARAM( );
+ CN470_GET_PHY_PARAM( );
+ CN779_GET_PHY_PARAM( );
+ EU433_GET_PHY_PARAM( );
+ EU868_GET_PHY_PARAM( );
+ KR920_GET_PHY_PARAM( );
+ IN865_GET_PHY_PARAM( );
+ US915_GET_PHY_PARAM( );
+ US915_HYBRID_GET_PHY_PARAM( );
+ default:
+ {
+ return phyParam;
+ }
+ }
+}
+
+void RegionSetBandTxDone( LoRaMacRegion_t region, SetBandTxDoneParams_t* txDone )
+{
+ switch( region )
+ {
+ AS923_SET_BAND_TX_DONE( );
+ AU915_SET_BAND_TX_DONE( );
+ CN470_SET_BAND_TX_DONE( );
+ CN779_SET_BAND_TX_DONE( );
+ EU433_SET_BAND_TX_DONE( );
+ EU868_SET_BAND_TX_DONE( );
+ KR920_SET_BAND_TX_DONE( );
+ IN865_SET_BAND_TX_DONE( );
+ US915_SET_BAND_TX_DONE( );
+ US915_HYBRID_SET_BAND_TX_DONE( );
+ default:
+ {
+ return;
+ }
+ }
+}
+
+void RegionInitDefaults( LoRaMacRegion_t region, InitType_t type )
+{
+ switch( region )
+ {
+ AS923_INIT_DEFAULTS( );
+ AU915_INIT_DEFAULTS( );
+ CN470_INIT_DEFAULTS( );
+ CN779_INIT_DEFAULTS( );
+ EU433_INIT_DEFAULTS( );
+ EU868_INIT_DEFAULTS( );
+ KR920_INIT_DEFAULTS( );
+ IN865_INIT_DEFAULTS( );
+ US915_INIT_DEFAULTS( );
+ US915_HYBRID_INIT_DEFAULTS( );
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( region )
+ {
+ AS923_VERIFY( );
+ AU915_VERIFY( );
+ CN470_VERIFY( );
+ CN779_VERIFY( );
+ EU433_VERIFY( );
+ EU868_VERIFY( );
+ KR920_VERIFY( );
+ IN865_VERIFY( );
+ US915_VERIFY( );
+ US915_HYBRID_VERIFY( );
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+void RegionApplyCFList( LoRaMacRegion_t region, ApplyCFListParams_t* applyCFList )
+{
+ switch( region )
+ {
+ AS923_APPLY_CF_LIST( );
+ AU915_APPLY_CF_LIST( );
+ CN470_APPLY_CF_LIST( );
+ CN779_APPLY_CF_LIST( );
+ EU433_APPLY_CF_LIST( );
+ EU868_APPLY_CF_LIST( );
+ KR920_APPLY_CF_LIST( );
+ IN865_APPLY_CF_LIST( );
+ US915_APPLY_CF_LIST( );
+ US915_HYBRID_APPLY_CF_LIST( );
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionChanMaskSet( LoRaMacRegion_t region, ChanMaskSetParams_t* chanMaskSet )
+{
+ switch( region )
+ {
+ AS923_CHAN_MASK_SET( );
+ AU915_CHAN_MASK_SET( );
+ CN470_CHAN_MASK_SET( );
+ CN779_CHAN_MASK_SET( );
+ EU433_CHAN_MASK_SET( );
+ EU868_CHAN_MASK_SET( );
+ KR920_CHAN_MASK_SET( );
+ IN865_CHAN_MASK_SET( );
+ US915_CHAN_MASK_SET( );
+ US915_HYBRID_CHAN_MASK_SET( );
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+bool RegionAdrNext( LoRaMacRegion_t region, AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ switch( region )
+ {
+ AS923_ADR_NEXT( );
+ AU915_ADR_NEXT( );
+ CN470_ADR_NEXT( );
+ CN779_ADR_NEXT( );
+ EU433_ADR_NEXT( );
+ EU868_ADR_NEXT( );
+ KR920_ADR_NEXT( );
+ IN865_ADR_NEXT( );
+ US915_ADR_NEXT( );
+ US915_HYBRID_ADR_NEXT( );
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+void RegionComputeRxWindowParameters( LoRaMacRegion_t region, int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+ switch( region )
+ {
+ AS923_COMPUTE_RX_WINDOW_PARAMETERS( );
+ AU915_COMPUTE_RX_WINDOW_PARAMETERS( );
+ CN470_COMPUTE_RX_WINDOW_PARAMETERS( );
+ CN779_COMPUTE_RX_WINDOW_PARAMETERS( );
+ EU433_COMPUTE_RX_WINDOW_PARAMETERS( );
+ EU868_COMPUTE_RX_WINDOW_PARAMETERS( );
+ KR920_COMPUTE_RX_WINDOW_PARAMETERS( );
+ IN865_COMPUTE_RX_WINDOW_PARAMETERS( );
+ US915_COMPUTE_RX_WINDOW_PARAMETERS( );
+ US915_HYBRID_COMPUTE_RX_WINDOW_PARAMETERS( );
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionRxConfig( LoRaMacRegion_t region, RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+ switch( region )
+ {
+ AS923_RX_CONFIG( );
+ AU915_RX_CONFIG( );
+ CN470_RX_CONFIG( );
+ CN779_RX_CONFIG( );
+ EU433_RX_CONFIG( );
+ EU868_RX_CONFIG( );
+ KR920_RX_CONFIG( );
+ IN865_RX_CONFIG( );
+ US915_RX_CONFIG( );
+ US915_HYBRID_RX_CONFIG( );
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+bool RegionTxConfig( LoRaMacRegion_t region, TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ switch( region )
+ {
+ AS923_TX_CONFIG( );
+ AU915_TX_CONFIG( );
+ CN470_TX_CONFIG( );
+ CN779_TX_CONFIG( );
+ EU433_TX_CONFIG( );
+ EU868_TX_CONFIG( );
+ KR920_TX_CONFIG( );
+ IN865_TX_CONFIG( );
+ US915_TX_CONFIG( );
+ US915_HYBRID_TX_CONFIG( );
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+uint8_t RegionLinkAdrReq( LoRaMacRegion_t region, LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+ switch( region )
+ {
+ AS923_LINK_ADR_REQ( );
+ AU915_LINK_ADR_REQ( );
+ CN470_LINK_ADR_REQ( );
+ CN779_LINK_ADR_REQ( );
+ EU433_LINK_ADR_REQ( );
+ EU868_LINK_ADR_REQ( );
+ KR920_LINK_ADR_REQ( );
+ IN865_LINK_ADR_REQ( );
+ US915_LINK_ADR_REQ( );
+ US915_HYBRID_LINK_ADR_REQ( );
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+uint8_t RegionRxParamSetupReq( LoRaMacRegion_t region, RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ switch( region )
+ {
+ AS923_RX_PARAM_SETUP_REQ( );
+ AU915_RX_PARAM_SETUP_REQ( );
+ CN470_RX_PARAM_SETUP_REQ( );
+ CN779_RX_PARAM_SETUP_REQ( );
+ EU433_RX_PARAM_SETUP_REQ( );
+ EU868_RX_PARAM_SETUP_REQ( );
+ KR920_RX_PARAM_SETUP_REQ( );
+ IN865_RX_PARAM_SETUP_REQ( );
+ US915_RX_PARAM_SETUP_REQ( );
+ US915_HYBRID_RX_PARAM_SETUP_REQ( );
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+uint8_t RegionNewChannelReq( LoRaMacRegion_t region, NewChannelReqParams_t* newChannelReq )
+{
+ switch( region )
+ {
+ AS923_NEW_CHANNEL_REQ( );
+ AU915_NEW_CHANNEL_REQ( );
+ CN470_NEW_CHANNEL_REQ( );
+ CN779_NEW_CHANNEL_REQ( );
+ EU433_NEW_CHANNEL_REQ( );
+ EU868_NEW_CHANNEL_REQ( );
+ KR920_NEW_CHANNEL_REQ( );
+ IN865_NEW_CHANNEL_REQ( );
+ US915_NEW_CHANNEL_REQ( );
+ US915_HYBRID_NEW_CHANNEL_REQ( );
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+int8_t RegionTxParamSetupReq( LoRaMacRegion_t region, TxParamSetupReqParams_t* txParamSetupReq )
+{
+ switch( region )
+ {
+ AS923_TX_PARAM_SETUP_REQ( );
+ AU915_TX_PARAM_SETUP_REQ( );
+ CN470_TX_PARAM_SETUP_REQ( );
+ CN779_TX_PARAM_SETUP_REQ( );
+ EU433_TX_PARAM_SETUP_REQ( );
+ EU868_TX_PARAM_SETUP_REQ( );
+ KR920_TX_PARAM_SETUP_REQ( );
+ IN865_TX_PARAM_SETUP_REQ( );
+ US915_TX_PARAM_SETUP_REQ( );
+ US915_HYBRID_TX_PARAM_SETUP_REQ( );
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+uint8_t RegionDlChannelReq( LoRaMacRegion_t region, DlChannelReqParams_t* dlChannelReq )
+{
+ switch( region )
+ {
+ AS923_DL_CHANNEL_REQ( );
+ AU915_DL_CHANNEL_REQ( );
+ CN470_DL_CHANNEL_REQ( );
+ CN779_DL_CHANNEL_REQ( );
+ EU433_DL_CHANNEL_REQ( );
+ EU868_DL_CHANNEL_REQ( );
+ KR920_DL_CHANNEL_REQ( );
+ IN865_DL_CHANNEL_REQ( );
+ US915_DL_CHANNEL_REQ( );
+ US915_HYBRID_DL_CHANNEL_REQ( );
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+int8_t RegionAlternateDr( LoRaMacRegion_t region, AlternateDrParams_t* alternateDr )
+{
+ switch( region )
+ {
+ AS923_ALTERNATE_DR( );
+ AU915_ALTERNATE_DR( );
+ CN470_ALTERNATE_DR( );
+ CN779_ALTERNATE_DR( );
+ EU433_ALTERNATE_DR( );
+ EU868_ALTERNATE_DR( );
+ KR920_ALTERNATE_DR( );
+ IN865_ALTERNATE_DR( );
+ US915_ALTERNATE_DR( );
+ US915_HYBRID_ALTERNATE_DR( );
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+void RegionCalcBackOff( LoRaMacRegion_t region, CalcBackOffParams_t* calcBackOff )
+{
+ switch( region )
+ {
+ AS923_CALC_BACKOFF( );
+ AU915_CALC_BACKOFF( );
+ CN470_CALC_BACKOFF( );
+ CN779_CALC_BACKOFF( );
+ EU433_CALC_BACKOFF( );
+ EU868_CALC_BACKOFF( );
+ KR920_CALC_BACKOFF( );
+ IN865_CALC_BACKOFF( );
+ US915_CALC_BACKOFF( );
+ US915_HYBRID_CALC_BACKOFF( );
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionNextChannel( LoRaMacRegion_t region, NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ switch( region )
+ {
+ AS923_NEXT_CHANNEL( );
+ AU915_NEXT_CHANNEL( );
+ CN470_NEXT_CHANNEL( );
+ CN779_NEXT_CHANNEL( );
+ EU433_NEXT_CHANNEL( );
+ EU868_NEXT_CHANNEL( );
+ KR920_NEXT_CHANNEL( );
+ IN865_NEXT_CHANNEL( );
+ US915_NEXT_CHANNEL( );
+ US915_HYBRID_NEXT_CHANNEL( );
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+LoRaMacStatus_t RegionChannelAdd( LoRaMacRegion_t region, ChannelAddParams_t* channelAdd )
+{
+ switch( region )
+ {
+ AS923_CHANNEL_ADD( );
+ AU915_CHANNEL_ADD( );
+ CN470_CHANNEL_ADD( );
+ CN779_CHANNEL_ADD( );
+ EU433_CHANNEL_ADD( );
+ EU868_CHANNEL_ADD( );
+ KR920_CHANNEL_ADD( );
+ IN865_CHANNEL_ADD( );
+ US915_CHANNEL_ADD( );
+ US915_HYBRID_CHANNEL_ADD( );
+ default:
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ }
+}
+
+bool RegionChannelsRemove( LoRaMacRegion_t region, ChannelRemoveParams_t* channelRemove )
+{
+ switch( region )
+ {
+ AS923_CHANNEL_REMOVE( );
+ AU915_CHANNEL_REMOVE( );
+ CN470_CHANNEL_REMOVE( );
+ CN779_CHANNEL_REMOVE( );
+ EU433_CHANNEL_REMOVE( );
+ EU868_CHANNEL_REMOVE( );
+ KR920_CHANNEL_REMOVE( );
+ IN865_CHANNEL_REMOVE( );
+ US915_CHANNEL_REMOVE( );
+ US915_HYBRID_CHANNEL_REMOVE( );
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+void RegionSetContinuousWave( LoRaMacRegion_t region, ContinuousWaveParams_t* continuousWave )
+{
+ switch( region )
+ {
+ AS923_SET_CONTINUOUS_WAVE( );
+ AU915_SET_CONTINUOUS_WAVE( );
+ CN470_SET_CONTINUOUS_WAVE( );
+ CN779_SET_CONTINUOUS_WAVE( );
+ EU433_SET_CONTINUOUS_WAVE( );
+ EU868_SET_CONTINUOUS_WAVE( );
+ KR920_SET_CONTINUOUS_WAVE( );
+ IN865_SET_CONTINUOUS_WAVE( );
+ US915_SET_CONTINUOUS_WAVE( );
+ US915_HYBRID_SET_CONTINUOUS_WAVE( );
+ default:
+ {
+ break;
+ }
+ }
+}
+
+uint8_t RegionApplyDrOffset( LoRaMacRegion_t region, uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ switch( region )
+ {
+ AS923_APPLY_DR_OFFSET( );
+ AU915_APPLY_DR_OFFSET( );
+ CN470_APPLY_DR_OFFSET( );
+ CN779_APPLY_DR_OFFSET( );
+ EU433_APPLY_DR_OFFSET( );
+ EU868_APPLY_DR_OFFSET( );
+ KR920_APPLY_DR_OFFSET( );
+ IN865_APPLY_DR_OFFSET( );
+ US915_APPLY_DR_OFFSET( );
+ US915_HYBRID_APPLY_DR_OFFSET( );
+ default:
+ {
+ return dr;
+ }
+ }
+}
+
+void RegionRxBeaconSetup( LoRaMacRegion_t region, RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+ switch( region )
+ {
+ AS923_RX_BEACON_SETUP( );
+ AU915_RX_BEACON_SETUP( );
+ CN470_RX_BEACON_SETUP( );
+ CN779_RX_BEACON_SETUP( );
+ EU433_RX_BEACON_SETUP( );
+ EU868_RX_BEACON_SETUP( );
+ KR920_RX_BEACON_SETUP( );
+ IN865_RX_BEACON_SETUP( );
+ US915_RX_BEACON_SETUP( );
+ US915_HYBRID_RX_BEACON_SETUP( );
+ default:
+ {
+ break;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/Region.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1612 @@
+/*!
+ * \file Region.h
+ *
+ * \brief Region implementation.
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGION Region implementation
+ * This is the common API to access the specific
+ * regional implementations.
+ *
+ * Preprocessor options:
+ * - LoRaWAN regions can be activated by defining the related preprocessor
+ * definition. It is possible to define more than one region.
+ * The following regions are supported:
+ * - #define REGION_AS923
+ * - #define REGION_AU915
+ * - #define REGION_CN470
+ * - #define REGION_CN779
+ * - #define REGION_EU433
+ * - #define REGION_EU868
+ * - #define REGION_KR920
+ * - #define REGION_IN865
+ * - #define REGION_US915
+ * - #define REGION_US915_HYBRID
+ *
+ * \{
+ */
+#ifndef __REGION_H__
+#define __REGION_H__
+
+
+
+
+/*!
+ * Macro to compute bit of a channel index.
+ */
+#define LC( channelIndex ) ( uint16_t )( 1 << ( channelIndex - 1 ) )
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | SF12 - BW125
+ * AU915 | SF10 - BW125
+ * CN470 | SF12 - BW125
+ * CN779 | SF12 - BW125
+ * EU433 | SF12 - BW125
+ * EU868 | SF12 - BW125
+ * IN865 | SF12 - BW125
+ * KR920 | SF12 - BW125
+ * US915 | SF10 - BW125
+ * US915_HYBRID | SF10 - BW125
+ */
+#define DR_0 0
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | SF11 - BW125
+ * AU915 | SF9 - BW125
+ * CN470 | SF11 - BW125
+ * CN779 | SF11 - BW125
+ * EU433 | SF11 - BW125
+ * EU868 | SF11 - BW125
+ * IN865 | SF11 - BW125
+ * KR920 | SF11 - BW125
+ * US915 | SF9 - BW125
+ * US915_HYBRID | SF9 - BW125
+ */
+#define DR_1 1
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | SF10 - BW125
+ * AU915 | SF8 - BW125
+ * CN470 | SF10 - BW125
+ * CN779 | SF10 - BW125
+ * EU433 | SF10 - BW125
+ * EU868 | SF10 - BW125
+ * IN865 | SF10 - BW125
+ * KR920 | SF10 - BW125
+ * US915 | SF8 - BW125
+ * US915_HYBRID | SF8 - BW125
+ */
+#define DR_2 2
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | SF9 - BW125
+ * AU915 | SF7 - BW125
+ * CN470 | SF9 - BW125
+ * CN779 | SF9 - BW125
+ * EU433 | SF9 - BW125
+ * EU868 | SF9 - BW125
+ * IN865 | SF9 - BW125
+ * KR920 | SF9 - BW125
+ * US915 | SF7 - BW125
+ * US915_HYBRID | SF7 - BW125
+ */
+#define DR_3 3
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | SF8 - BW125
+ * AU915 | SF8 - BW500
+ * CN470 | SF8 - BW125
+ * CN779 | SF8 - BW125
+ * EU433 | SF8 - BW125
+ * EU868 | SF8 - BW125
+ * IN865 | SF8 - BW125
+ * KR920 | SF8 - BW125
+ * US915 | SF8 - BW500
+ * US915_HYBRID | SF8 - BW500
+ */
+#define DR_4 4
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | SF7 - BW125
+ * AU915 | RFU
+ * CN470 | SF7 - BW125
+ * CN779 | SF7 - BW125
+ * EU433 | SF7 - BW125
+ * EU868 | SF7 - BW125
+ * IN865 | SF7 - BW125
+ * KR920 | SF7 - BW125
+ * US915 | RFU
+ * US915_HYBRID | RFU
+ */
+#define DR_5 5
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | SF7 - BW250
+ * AU915 | RFU
+ * CN470 | SF12 - BW125
+ * CN779 | SF7 - BW250
+ * EU433 | SF7 - BW250
+ * EU868 | SF7 - BW250
+ * IN865 | SF7 - BW250
+ * KR920 | RFU
+ * US915 | RFU
+ * US915_HYBRID | RFU
+ */
+#define DR_6 6
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | FSK
+ * AU915 | RFU
+ * CN470 | SF12 - BW125
+ * CN779 | FSK
+ * EU433 | FSK
+ * EU868 | FSK
+ * IN865 | FSK
+ * KR920 | RFU
+ * US915 | RFU
+ * US915_HYBRID | RFU
+ */
+#define DR_7 7
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | RFU
+ * AU915 | SF12 - BW500
+ * CN470 | RFU
+ * CN779 | RFU
+ * EU433 | RFU
+ * EU868 | RFU
+ * IN865 | RFU
+ * KR920 | RFU
+ * US915 | SF12 - BW500
+ * US915_HYBRID | SF12 - BW500
+ */
+#define DR_8 8
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | RFU
+ * AU915 | SF11 - BW500
+ * CN470 | RFU
+ * CN779 | RFU
+ * EU433 | RFU
+ * EU868 | RFU
+ * IN865 | RFU
+ * KR920 | RFU
+ * US915 | SF11 - BW500
+ * US915_HYBRID | SF11 - BW500
+ */
+#define DR_9 9
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | RFU
+ * AU915 | SF10 - BW500
+ * CN470 | RFU
+ * CN779 | RFU
+ * EU433 | RFU
+ * EU868 | RFU
+ * IN865 | RFU
+ * KR920 | RFU
+ * US915 | SF10 - BW500
+ * US915_HYBRID | SF10 - BW500
+ */
+#define DR_10 10
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | RFU
+ * AU915 | SF9 - BW500
+ * CN470 | RFU
+ * CN779 | RFU
+ * EU433 | RFU
+ * EU868 | RFU
+ * IN865 | RFU
+ * KR920 | RFU
+ * US915 | SF9 - BW500
+ * US915_HYBRID | SF9 - BW500
+ */
+#define DR_11 11
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | RFU
+ * AU915 | SF8 - BW500
+ * CN470 | RFU
+ * CN779 | RFU
+ * EU433 | RFU
+ * EU868 | RFU
+ * IN865 | RFU
+ * KR920 | RFU
+ * US915 | SF8 - BW500
+ * US915_HYBRID | SF8 - BW500
+ */
+#define DR_12 12
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | RFU
+ * AU915 | SF7 - BW500
+ * CN470 | RFU
+ * CN779 | RFU
+ * EU433 | RFU
+ * EU868 | RFU
+ * IN865 | RFU
+ * KR920 | RFU
+ * US915 | SF7 - BW500
+ * US915_HYBRID | SF7 - BW500
+ */
+#define DR_13 13
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | RFU
+ * AU915 | RFU
+ * CN470 | RFU
+ * CN779 | RFU
+ * EU433 | RFU
+ * EU868 | RFU
+ * IN865 | RFU
+ * KR920 | RFU
+ * US915 | RFU
+ * US915_HYBRID | RFU
+ */
+#define DR_14 14
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | RFU
+ * AU915 | RFU
+ * CN470 | RFU
+ * CN779 | RFU
+ * EU433 | RFU
+ * EU868 | RFU
+ * IN865 | RFU
+ * KR920 | RFU
+ * US915 | RFU
+ * US915_HYBRID | RFU
+ */
+#define DR_15 15
+
+
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | Max EIRP
+ * AU915 | Max EIRP
+ * CN470 | Max EIRP
+ * CN779 | Max EIRP
+ * EU433 | Max EIRP
+ * EU868 | Max EIRP
+ * IN865 | Max EIRP
+ * KR920 | Max EIRP
+ * US915 | Max ERP
+ * US915_HYBRID | Max ERP
+ */
+#define TX_POWER_0 0
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | Max EIRP - 2
+ * AU915 | Max EIRP - 2
+ * CN470 | Max EIRP - 2
+ * CN779 | Max EIRP - 2
+ * EU433 | Max EIRP - 2
+ * EU868 | Max EIRP - 2
+ * IN865 | Max EIRP - 2
+ * KR920 | Max EIRP - 2
+ * US915 | Max ERP - 2
+ * US915_HYBRID | Max ERP - 2
+ */
+#define TX_POWER_1 1
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | Max EIRP - 4
+ * AU915 | Max EIRP - 4
+ * CN470 | Max EIRP - 4
+ * CN779 | Max EIRP - 4
+ * EU433 | Max EIRP - 4
+ * EU868 | Max EIRP - 4
+ * IN865 | Max EIRP - 4
+ * KR920 | Max EIRP - 4
+ * US915 | Max ERP - 4
+ * US915_HYBRID | Max ERP - 4
+ */
+#define TX_POWER_2 2
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | Max EIRP - 6
+ * AU915 | Max EIRP - 6
+ * CN470 | Max EIRP - 6
+ * CN779 | Max EIRP - 6
+ * EU433 | Max EIRP - 6
+ * EU868 | Max EIRP - 6
+ * IN865 | Max EIRP - 6
+ * KR920 | Max EIRP - 6
+ * US915 | Max ERP - 6
+ * US915_HYBRID | Max ERP - 6
+ */
+#define TX_POWER_3 3
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | Max EIRP - 8
+ * AU915 | Max EIRP - 8
+ * CN470 | Max EIRP - 8
+ * CN779 | Max EIRP - 8
+ * EU433 | Max EIRP - 8
+ * EU868 | Max EIRP - 8
+ * IN865 | Max EIRP - 8
+ * KR920 | Max EIRP - 8
+ * US915 | Max ERP - 8
+ * US915_HYBRID | Max ERP - 8
+ */
+#define TX_POWER_4 4
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | Max EIRP - 10
+ * AU915 | Max EIRP - 10
+ * CN470 | Max EIRP - 10
+ * CN779 | Max EIRP - 10
+ * EU433 | Max EIRP - 10
+ * EU868 | Max EIRP - 10
+ * IN865 | Max EIRP - 10
+ * KR920 | Max EIRP - 10
+ * US915 | Max ERP - 10
+ * US915_HYBRID | Max ERP - 10
+ */
+#define TX_POWER_5 5
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | Max EIRP - 12
+ * AU915 | Max EIRP - 12
+ * CN470 | Max EIRP - 12
+ * CN779 | -
+ * EU433 | -
+ * EU868 | Max EIRP - 12
+ * IN865 | Max EIRP - 12
+ * KR920 | Max EIRP - 12
+ * US915 | Max ERP - 12
+ * US915_HYBRID | Max ERP - 12
+ */
+#define TX_POWER_6 6
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | Max EIRP - 14
+ * AU915 | Max EIRP - 14
+ * CN470 | Max EIRP - 14
+ * CN779 | -
+ * EU433 | -
+ * EU868 | Max EIRP - 14
+ * IN865 | Max EIRP - 14
+ * KR920 | Max EIRP - 14
+ * US915 | Max ERP - 14
+ * US915_HYBRID | Max ERP - 14
+ */
+#define TX_POWER_7 7
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | -
+ * AU915 | Max EIRP - 16
+ * CN470 | -
+ * CN779 | -
+ * EU433 | -
+ * EU868 | -
+ * IN865 | Max EIRP - 16
+ * KR920 | -
+ * US915 | Max ERP - 16
+ * US915_HYBRID | Max ERP -16
+ */
+#define TX_POWER_8 8
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | -
+ * AU915 | Max EIRP - 18
+ * CN470 | -
+ * CN779 | -
+ * EU433 | -
+ * EU868 | -
+ * IN865 | Max EIRP - 18
+ * KR920 | -
+ * US915 | Max ERP - 16
+ * US915_HYBRID | Max ERP - 16
+ */
+#define TX_POWER_9 9
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | -
+ * AU915 | Max EIRP - 20
+ * CN470 | -
+ * CN779 | -
+ * EU433 | -
+ * EU868 | -
+ * IN865 | Max EIRP - 20
+ * KR920 | -
+ * US915 | Max ERP - 10
+ * US915_HYBRID | Max ERP - 10
+ */
+#define TX_POWER_10 10
+
+/*!
+ * RFU
+ */
+#define TX_POWER_11 11
+
+/*!
+ * RFU
+ */
+#define TX_POWER_12 12
+
+/*!
+ * RFU
+ */
+#define TX_POWER_13 13
+
+/*!
+ * RFU
+ */
+#define TX_POWER_14 14
+
+/*!
+ * RFU
+ */
+#define TX_POWER_15 15
+
+
+
+/*!
+ * Enumeration of phy attributes.
+ */
+typedef enum ePhyAttribute
+{
+ /*!
+ * Minimum RX datarate.
+ */
+ PHY_MIN_RX_DR,
+ /*!
+ * Minimum TX datarate.
+ */
+ PHY_MIN_TX_DR,
+ /*!
+ * Maximum RX datarate.
+ */
+ PHY_MAX_RX_DR,
+ /*!
+ * Maximum TX datarate.
+ */
+ PHY_MAX_TX_DR,
+ /*!
+ * TX datarate.
+ */
+ PHY_TX_DR,
+ /*!
+ * Default TX datarate.
+ */
+ PHY_DEF_TX_DR,
+ /*!
+ * RX datarate.
+ */
+ PHY_RX_DR,
+ /*!
+ * TX power.
+ */
+ PHY_TX_POWER,
+ /*!
+ * Default TX power.
+ */
+ PHY_DEF_TX_POWER,
+ /*!
+ * Maximum payload possible.
+ */
+ PHY_MAX_PAYLOAD,
+ /*!
+ * Maximum payload possible when repeater support is enabled.
+ */
+ PHY_MAX_PAYLOAD_REPEATER,
+ /*!
+ * Duty cycle.
+ */
+ PHY_DUTY_CYCLE,
+ /*!
+ * Maximum receive window duration.
+ */
+ PHY_MAX_RX_WINDOW,
+ /*!
+ * Receive delay for window 1.
+ */
+ PHY_RECEIVE_DELAY1,
+ /*!
+ * Receive delay for window 2.
+ */
+ PHY_RECEIVE_DELAY2,
+ /*!
+ * Join accept delay for window 1.
+ */
+ PHY_JOIN_ACCEPT_DELAY1,
+ /*!
+ * Join accept delay for window 2.
+ */
+ PHY_JOIN_ACCEPT_DELAY2,
+ /*!
+ * Maximum frame counter gap.
+ */
+ PHY_MAX_FCNT_GAP,
+ /*!
+ * Acknowledgement time out.
+ */
+ PHY_ACK_TIMEOUT,
+ /*!
+ * Default datarate offset for window 1.
+ */
+ PHY_DEF_DR1_OFFSET,
+ /*!
+ * Default receive window 2 frequency.
+ */
+ PHY_DEF_RX2_FREQUENCY,
+ /*!
+ * Default receive window 2 datarate.
+ */
+ PHY_DEF_RX2_DR,
+ /*!
+ * Channels mask.
+ */
+ PHY_CHANNELS_MASK,
+ /*!
+ * Channels default mask.
+ */
+ PHY_CHANNELS_DEFAULT_MASK,
+ /*!
+ * Maximum number of supported channels
+ */
+ PHY_MAX_NB_CHANNELS,
+ /*!
+ * Channels.
+ */
+ PHY_CHANNELS,
+ /*!
+ * Default value of the uplink dwell time.
+ */
+ PHY_DEF_UPLINK_DWELL_TIME,
+ /*!
+ * Default value of the downlink dwell time.
+ */
+ PHY_DEF_DOWNLINK_DWELL_TIME,
+ /*!
+ * Default value of the MaxEIRP.
+ */
+ PHY_DEF_MAX_EIRP,
+ /*!
+ * Default value of the antenna gain.
+ */
+ PHY_DEF_ANTENNA_GAIN,
+ /*!
+ * Value for the number of join trials.
+ */
+ PHY_NB_JOIN_TRIALS,
+ /*!
+ * Next lower datarate.
+ */
+ PHY_NEXT_LOWER_TX_DR,
+ /*!
+ * Default value for the number of join trials.
+ */
+ PHY_DEF_NB_JOIN_TRIALS,
+ /*!
+ * Beacon interval in ms.
+ */
+ PHY_BEACON_INTERVAL,
+ /*!
+ * Beacon reserved time in ms.
+ */
+ PHY_BEACON_RESERVED,
+ /*!
+ * Beacon guard time in ms.
+ */
+ PHY_BEACON_GUARD,
+ /*!
+ * Beacon window time in ms.
+ */
+ PHY_BEACON_WINDOW,
+ /*!
+ * Beacon window time in numer of slots.
+ */
+ PHY_BEACON_WINDOW_SLOTS,
+ /*!
+ * Ping slot length time in ms.
+ */
+ PHY_PING_SLOT_WINDOW,
+ /*!
+ * Default symbol timeout for beacons and ping slot windows.
+ */
+ PHY_BEACON_SYMBOL_TO_DEFAULT,
+ /*!
+ * Maximum symbol timeout for beacons.
+ */
+ PHY_BEACON_SYMBOL_TO_EXPANSION_MAX,
+ /*!
+ * Maximum symbol timeout for ping slots.
+ */
+ PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX,
+ /*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols.
+ */
+ PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR,
+ /*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols.
+ */
+ PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR,
+ /*!
+ * Maximum allowed beacon less time in ms.
+ */
+ PHY_MAX_BEACON_LESS_PERIOD,
+ /*!
+ * Delay time for the BeaconTimingAns in ms.
+ */
+ PHY_BEACON_DELAY_BEACON_TIMING_ANS,
+ /*!
+ * Delay time for the BeaconTimingAns in ms.
+ */
+ PHY_BEACON_CHANNEL_FREQ,
+ /*!
+ * Delay time for the BeaconTimingAns in ms.
+ */
+ PHY_BEACON_CHANNEL_FREQ_IDX,
+ /*!
+ * Delay time for the BeaconTimingAns in ms.
+ */
+ PHY_PINGSLOT_CHANNEL_FREQ,
+ PHY_BEACON_SIZE,
+ PHY_BEACON_CHANNEL_DR,
+}PhyAttribute_t;
+
+/*!
+ * Enumeration of initialization types.
+ */
+typedef enum eInitType
+{
+ /*!
+ * Performs an initialization and overwrites all existing data.
+ */
+ INIT_TYPE_INIT,
+ /*!
+ * Restores default channels only.
+ */
+ INIT_TYPE_RESTORE
+}InitType_t;
+
+typedef enum eChannelsMask
+{
+ /*!
+ * The channels mask.
+ */
+ CHANNELS_MASK,
+ /*!
+ * The channels default mask.
+ */
+ CHANNELS_DEFAULT_MASK
+}ChannelsMask_t;
+
+/*!
+ * Union for the structure uGetPhyParams
+ */
+typedef union uPhyParam
+{
+ /*!
+ * A parameter value.
+ */
+ uint32_t Value;
+ /*!
+ * A floating point value.
+ */
+ float fValue;
+ /*!
+ * Pointer to the channels mask.
+ */
+ uint16_t* ChannelsMask;
+ /*!
+ * Pointer to the channels.
+ */
+ ChannelParams_t* Channels;
+}PhyParam_t;
+
+/*!
+ * Parameter structure for the function RegionGetPhyParam.
+ */
+typedef struct sGetPhyParams
+{
+ /*!
+ * Setup the parameter to get.
+ */
+ PhyAttribute_t Attribute;
+ /*!
+ * Datarate.
+ * The parameter is needed for the following queries:
+ * PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_NEXT_LOWER_TX_DR.
+ */
+ int8_t Datarate;
+ /*!
+ * Uplink dwell time. This parameter must be set to query:
+ * PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_MIN_TX_DR.
+ * The parameter is needed for the following queries:
+ * PHY_MIN_TX_DR, PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_NEXT_LOWER_TX_DR.
+ */
+ uint8_t UplinkDwellTime;
+ /*!
+ * Downlink dwell time.This parameter must be set to query:
+ * PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_MIN_RX_DR.
+ * The parameter is needed for the following queries:
+ * PHY_MIN_RX_DR, PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER.
+ */
+ uint8_t DownlinkDwellTime;
+ /*!
+ * LoRaMAC Device Address.This parameter must be set to query:
+ * PHY_PINGSLOT_CHANNEL_FREQ.
+ */
+ uint32_t DeviceAddress;
+}GetPhyParams_t;
+
+/*!
+ * Parameter structure for the function RegionSetBandTxDone.
+ */
+typedef struct sSetBandTxDoneParams
+{
+ /*!
+ * Channel to update.
+ */
+ uint8_t Channel;
+ /*!
+ * Joined Set to true, if the node has joined the network
+ */
+ bool Joined;
+ /*!
+ * Last TX done time.
+ */
+ TimerTime_t LastTxDoneTime;
+}SetBandTxDoneParams_t;
+
+/*!
+ * Parameter structure for the function RegionVerify.
+ */
+typedef union uVerifyParams
+{
+ /*!
+ * TX power to verify.
+ */
+ int8_t TxPower;
+ /*!
+ * Set to true, if the duty cycle is enabled, otherwise false.
+ */
+ bool DutyCycle;
+ /*!
+ * The number of join trials.
+ */
+ uint8_t NbJoinTrials;
+ /*!
+ * Datarate to verify.
+ */
+ struct sDatarateParams
+ {
+ /*!
+ * Datarate to verify.
+ */
+ int8_t Datarate;
+ /*!
+ * The downlink dwell time.
+ */
+ uint8_t DownlinkDwellTime;
+ /*!
+ * The up link dwell time.
+ */
+ uint8_t UplinkDwellTime;
+ }DatarateParams;
+}VerifyParams_t;
+
+/*!
+ * Parameter structure for the function RegionApplyCFList.
+ */
+typedef struct sApplyCFListParams
+{
+ /*!
+ * Payload which contains the CF list.
+ */
+ uint8_t* Payload;
+ /*!
+ * Size of the payload.
+ */
+ uint8_t Size;
+}ApplyCFListParams_t;
+
+/*!
+ * Parameter structure for the function RegionChanMaskSet.
+ */
+typedef struct sChanMaskSetParams
+{
+ /*!
+ * Pointer to the channels mask which should be set.
+ */
+ uint16_t* ChannelsMaskIn;
+ /*!
+ * Pointer to the channels mask which should be set.
+ */
+ ChannelsMask_t ChannelsMaskType;
+}ChanMaskSetParams_t;
+
+/*!
+ * Parameter structure for the function RegionAdrNext.
+ */
+typedef struct sAdrNextParams
+{
+ /*!
+ * Set to true, if the function should update the channels mask.
+ */
+ bool UpdateChanMask;
+ /*!
+ * Set to true, if ADR is enabled.
+ */
+ bool AdrEnabled;
+ /*!
+ * ADR ack counter.
+ */
+ uint32_t AdrAckCounter;
+ /*!
+ * Datarate used currently.
+ */
+ int8_t Datarate;
+ /*!
+ * TX power used currently.
+ */
+ int8_t TxPower;
+ /*!
+ * UplinkDwellTime
+ */
+ uint8_t UplinkDwellTime;
+}AdrNextParams_t;
+
+/*!
+ * Parameter structure for the function RegionRxConfig.
+ */
+typedef struct sRxConfigParams
+{
+ /*!
+ * The RX channel.
+ */
+ uint8_t Channel;
+ /*!
+ * RX datarate.
+ */
+ int8_t Datarate;
+ /*!
+ * RX bandwidth.
+ */
+ uint8_t Bandwidth;
+ /*!
+ * RX datarate offset.
+ */
+ int8_t DrOffset;
+ /*!
+ * RX frequency.
+ */
+ uint32_t Frequency;
+ /*!
+ * RX window timeout
+ */
+ uint32_t WindowTimeout;
+ /*!
+ * RX window offset
+ */
+ int32_t WindowOffset;
+ /*!
+ * Downlink dwell time.
+ */
+ uint8_t DownlinkDwellTime;
+ /*!
+ * Set to true, if a repeater is supported.
+ */
+ bool RepeaterSupport;
+ /*!
+ * Set to true, if RX should be continuous.
+ */
+ bool RxContinuous;
+ /*!
+ * Sets the RX window. 0: RX window 1, 1: RX window 2.
+ */
+ bool Window;
+}RxConfigParams_t;
+
+/*!
+ * Parameter structure for the function RegionTxConfig.
+ */
+typedef struct sTxConfigParams
+{
+ /*!
+ * The TX channel.
+ */
+ uint8_t Channel;
+ /*!
+ * The TX datarate.
+ */
+ int8_t Datarate;
+ /*!
+ * The TX power.
+ */
+ int8_t TxPower;
+ /*!
+ * The Max EIRP, if applicable.
+ */
+ float MaxEirp;
+ /*!
+ * The antenna gain, if applicable.
+ */
+ float AntennaGain;
+ /*!
+ * Frame length to setup.
+ */
+ uint16_t PktLen;
+}TxConfigParams_t;
+
+/*!
+ * Parameter structure for the function RegionLinkAdrReq.
+ */
+typedef struct sLinkAdrReqParams
+{
+ /*!
+ * Pointer to the payload which contains the MAC commands.
+ */
+ uint8_t* Payload;
+ /*!
+ * Size of the payload.
+ */
+ uint8_t PayloadSize;
+ /*!
+ * Uplink dwell time.
+ */
+ uint8_t UplinkDwellTime;
+ /*!
+ * Set to true, if ADR is enabled.
+ */
+ bool AdrEnabled;
+ /*!
+ * The current datarate.
+ */
+ int8_t CurrentDatarate;
+ /*!
+ * The current TX power.
+ */
+ int8_t CurrentTxPower;
+ /*!
+ * The current number of repetitions.
+ */
+ uint8_t CurrentNbRep;
+}LinkAdrReqParams_t;
+
+/*!
+ * Parameter structure for the function RegionRxParamSetupReq.
+ */
+typedef struct sRxParamSetupReqParams
+{
+ /*!
+ * The datarate to setup.
+ */
+ int8_t Datarate;
+ /*!
+ * Datarate offset.
+ */
+ int8_t DrOffset;
+ /*!
+ * The frequency to setup.
+ */
+ uint32_t Frequency;
+}RxParamSetupReqParams_t;
+
+/*!
+ * Parameter structure for the function RegionNewChannelReq.
+ */
+typedef struct sNewChannelReqParams
+{
+ /*!
+ * Pointer to the new channels.
+ */
+ ChannelParams_t* NewChannel;
+ /*!
+ * Channel id.
+ */
+ int8_t ChannelId;
+}NewChannelReqParams_t;
+
+/*!
+ * Parameter structure for the function RegionTxParamSetupReq.
+ */
+typedef struct sTxParamSetupReqParams
+{
+ /*!
+ * Uplink dwell time.
+ */
+ uint8_t UplinkDwellTime;
+ /*!
+ * Downlink dwell time.
+ */
+ uint8_t DownlinkDwellTime;
+ /*!
+ * Max EIRP.
+ */
+ uint8_t MaxEirp;
+}TxParamSetupReqParams_t;
+
+/*!
+ * Parameter structure for the function RegionDlChannelReq.
+ */
+typedef struct sDlChannelReqParams
+{
+ /*!
+ * Channel Id to add the frequency.
+ */
+ uint8_t ChannelId;
+ /*!
+ * Alternative frequency for the Rx1 window.
+ */
+ uint32_t Rx1Frequency;
+}DlChannelReqParams_t;
+
+/*!
+ * Parameter structure for the function RegionAlternateDr.
+ */
+typedef struct sAlternateDrParams
+{
+ /*!
+ * Number of trials.
+ */
+ uint16_t NbTrials;
+}AlternateDrParams_t;
+
+/*!
+ * Parameter structure for the function RegionCalcBackOff.
+ */
+typedef struct sCalcBackOffParams
+{
+ /*!
+ * Set to true, if the node has already joined a network, otherwise false.
+ */
+ bool Joined;
+ /*!
+ * Joined Set to true, if the last uplink was a join request
+ */
+ bool LastTxIsJoinRequest;
+ /*!
+ * Set to true, if the duty cycle is enabled, otherwise false.
+ */
+ bool DutyCycleEnabled;
+ /*!
+ * Current channel index.
+ */
+ uint8_t Channel;
+ /*!
+ * Elapsed time since the start of the node.
+ */
+ TimerTime_t ElapsedTime;
+ /*!
+ * Time-on-air of the last transmission.
+ */
+ TimerTime_t TxTimeOnAir;
+}CalcBackOffParams_t;
+
+/*!
+ * Parameter structure for the function RegionNextChannel.
+ */
+typedef struct sNextChanParams
+{
+ /*!
+ * Aggregated time-off time.
+ */
+ TimerTime_t AggrTimeOff;
+ /*!
+ * Time of the last aggregated TX.
+ */
+ TimerTime_t LastAggrTx;
+ /*!
+ * Current datarate.
+ */
+ int8_t Datarate;
+ /*!
+ * Set to true, if the node has already joined a network, otherwise false.
+ */
+ bool Joined;
+ /*!
+ * Set to true, if the duty cycle is enabled, otherwise false.
+ */
+ bool DutyCycleEnabled;
+}NextChanParams_t;
+
+/*!
+ * Parameter structure for the function RegionChannelsAdd.
+ */
+typedef struct sChannelAddParams
+{
+ /*!
+ * Pointer to the new channel to add.
+ */
+ ChannelParams_t* NewChannel;
+ /*!
+ * Channel id to add.
+ */
+ uint8_t ChannelId;
+}ChannelAddParams_t;
+
+/*!
+ * Parameter structure for the function RegionChannelsRemove.
+ */
+typedef struct sChannelRemoveParams
+{
+ /*!
+ * Channel id to remove.
+ */
+ uint8_t ChannelId;
+}ChannelRemoveParams_t;
+
+/*!
+ * Parameter structure for the function RegionContinuousWave.
+ */
+typedef struct sContinuousWaveParams
+{
+ /*!
+ * Current channel index.
+ */
+ uint8_t Channel;
+ /*!
+ * Datarate. Used to limit the TX power.
+ */
+ int8_t Datarate;
+ /*!
+ * The TX power to setup.
+ */
+ int8_t TxPower;
+ /*!
+ * Max EIRP, if applicable.
+ */
+ float MaxEirp;
+ /*!
+ * The antenna gain, if applicable.
+ */
+ float AntennaGain;
+ /*!
+ * Specifies the time the radio will stay in CW mode.
+ */
+ uint16_t Timeout;
+}ContinuousWaveParams_t;
+
+/*!
+ * Parameter structure for the function RegionRxBeaconSetup
+ */
+typedef struct sRxBeaconSetupParams
+{
+ /*!
+ * Symbol timeout.
+ */
+ uint16_t SymbolTimeout;
+ /*!
+ * Receive time.
+ */
+ uint32_t RxTime;
+ /*!
+ * The address of the node.
+ */
+ uint32_t DeviceAddress;
+ /*!
+ * The beacon timing channel.
+ */
+ uint8_t BeaconTimingChannel;
+ /*!
+ * Set to true if a custom frequency is enabled.
+ */
+ bool CustomFrequencyEnabled;
+ /*!
+ * Set to true if the beacon channel is set.
+ */
+ bool BeaconChannelSet;
+ /*!
+ * Custom frequency. Only valid of CustomFrequencyEnabled is set.
+ */
+ uint32_t CustomFrequency;
+ /*!
+ * THe beacon time.
+ */
+ TimerTime_t BeaconTime;
+ /*!
+ * The beacon interval.
+ */
+ TimerTime_t BeaconInterval;
+}RxBeaconSetup_t;
+
+
+
+/*!
+ * \brief The function verifies if a region is active or not. If a region
+ * is not active, it cannot be used.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \retval Return true, if the region is supported.
+ */
+bool RegionIsActive( LoRaMacRegion_t region );
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionGetPhyParam( LoRaMacRegion_t region, GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionSetBandTxDone( LoRaMacRegion_t region, SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionInitDefaults( LoRaMacRegion_t region, InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ * CF list.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionApplyCFList( LoRaMacRegion_t region, ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionChanMaskSet( LoRaMacRegion_t region, ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionAdrNext( LoRaMacRegion_t region, AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionRxConfig( LoRaMacRegion_t region, RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*
+ * Rx window precise timing
+ *
+ * For more details please consult the following document, chapter 3.1.2.
+ * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf
+ * or
+ * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf
+ *
+ * Downlink start: T = Tx + 1s (+/- 20 us)
+ * |
+ * TRxEarly | TRxLate
+ * | | |
+ * | | +---+---+---+---+---+---+---+---+
+ * | | | Latest Rx window |
+ * | | +---+---+---+---+---+---+---+---+
+ * | | |
+ * +---+---+---+---+---+---+---+---+
+ * | Earliest Rx window |
+ * +---+---+---+---+---+---+---+---+
+ * |
+ * +---+---+---+---+---+---+---+---+
+ *Downlink preamble 8 symbols | | | | | | | | |
+ * +---+---+---+---+---+---+---+---+
+ *
+ * Worst case Rx window timings
+ *
+ * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME
+ * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME
+ *
+ * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
+ *
+ * RxOffset = ( TRxLate + TRxEarly ) / 2
+ *
+ * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
+ * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME
+ *
+ * Minimal value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol
+ */
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] datarate Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ * The receiver will turn on in a [-rxError : +rxError] ms
+ * interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionComputeRxWindowParameters( LoRaMacRegion_t region, int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionTxConfig( LoRaMacRegion_t region, TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The datarate which was applied.
+ *
+ * \param [OUT] txPowOut The TX power which was applied.
+ *
+ * \param [OUT] nbRepOut The number of repetitions to apply.
+ *
+ * \param [OUT] nbBytesParsed The number bytes which were parsed.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionLinkAdrReq( LoRaMacRegion_t region, LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionRxParamSetupReq( LoRaMacRegion_t region, RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a New Channel Request.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionNewChannelReq( LoRaMacRegion_t region, NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ * Returns -1, if the functionality is not implemented. In this case, the end node
+ * shall ignore the command.
+ */
+int8_t RegionTxParamSetupReq( LoRaMacRegion_t region, TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionDlChannelReq( LoRaMacRegion_t region, DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionAlternateDr( LoRaMacRegion_t region, AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionCalcBackOff( LoRaMacRegion_t region, CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ * cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate].
+ */
+bool RegionNextChannel( LoRaMacRegion_t region, NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionChannelAdd( LoRaMacRegion_t region, ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionChannelsRemove( LoRaMacRegion_t region, ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionSetContinuousWave( LoRaMacRegion_t region, ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionApplyDrOffset( LoRaMacRegion_t region, uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+void RegionRxBeaconSetup( LoRaMacRegion_t region, RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet );
+
+/*! \} defgroup REGION */
+
+#endif // __REGION_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionAS923.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1203 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region AS923 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionAS923.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[AS923_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[AS923_MAX_NB_BANDS] =
+{
+ AS923_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+ uint8_t nextLowerDr = 0;
+
+ if( dr == minDr )
+ {
+ nextLowerDr = minDr;
+ }
+ else
+ {
+ nextLowerDr = dr - 1;
+ }
+ return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+ switch( BandwidthsAS923[drIndex] )
+ {
+ default:
+ case 125000:
+ return 0;
+ case 250000:
+ return 1;
+ case 500000:
+ return 2;
+ }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+ int8_t txPowerResult = txPower;
+
+ // Limit tx power to the band max
+ txPowerResult = MAX( txPower, maxBandTxPower );
+
+ return txPowerResult;
+}
+
+static bool VerifyTxFreq( uint32_t freq )
+{
+ // Check radio driver support
+ if( Radio.CheckRfFrequency( freq ) == false )
+ {
+ return false;
+ }
+
+ if( ( freq < 915000000 ) || ( freq > 928000000 ) )
+ {
+ return false;
+ }
+ return true;
+}
+
+static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTransmission = 0;
+
+ for( uint8_t i = 0, k = 0; i < AS923_MAX_NB_CHANNELS; i += 16, k++ )
+ {
+ for( uint8_t j = 0; j < 16; j++ )
+ {
+ if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+ {
+ if( channels[i + j].Frequency == 0 )
+ { // Check if the channel is enabled
+ continue;
+ }
+ if( joined == false )
+ {
+ if( ( AS923_JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+ {
+ continue;
+ }
+ }
+ if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+ channels[i + j].DrRange.Fields.Max ) == false )
+ { // Check if the current channel selection supports the given datarate
+ continue;
+ }
+ if( bands[channels[i + j].Band].TimeOff > 0 )
+ { // Check if the band is available for transmission
+ delayTransmission++;
+ continue;
+ }
+ enabledChannels[nbEnabledChannels++] = i + j;
+ }
+ }
+ }
+
+ *delayTx = delayTransmission;
+ return nbEnabledChannels;
+}
+
+PhyParam_t RegionAS923GetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ if( getPhy->DownlinkDwellTime == 0 )
+ {
+ phyParam.Value = AS923_RX_MIN_DATARATE;
+ }
+ else
+ {
+ phyParam.Value = AS923_DWELL_LIMIT_DATARATE;
+ }
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ if( getPhy->UplinkDwellTime == 0 )
+ {
+ phyParam.Value = AS923_TX_MIN_DATARATE;
+ }
+ else
+ {
+ phyParam.Value = AS923_DWELL_LIMIT_DATARATE;
+ }
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = AS923_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ if( getPhy->UplinkDwellTime == 0 )
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AS923_TX_MIN_DATARATE );
+ }
+ else
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AS923_DWELL_LIMIT_DATARATE );
+ }
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = AS923_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ if( getPhy->UplinkDwellTime == 0 )
+ {
+ phyParam.Value = MaxPayloadOfDatarateDwell0AS923[getPhy->Datarate];
+ }
+ else
+ {
+ phyParam.Value = MaxPayloadOfDatarateDwell1UpAS923[getPhy->Datarate];
+ }
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ if( getPhy->UplinkDwellTime == 0 )
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterDwell0AS923[getPhy->Datarate];
+ }
+ else
+ {
+ phyParam.Value = MaxPayloadOfDatarateDwell1UpAS923[getPhy->Datarate];
+ }
+ break;
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ phyParam.Value = AS923_DUTY_CYCLE_ENABLED;
+ break;
+ }
+ case PHY_MAX_RX_WINDOW:
+ {
+ phyParam.Value = AS923_MAX_RX_WINDOW;
+ break;
+ }
+ case PHY_RECEIVE_DELAY1:
+ {
+ phyParam.Value = AS923_RECEIVE_DELAY1;
+ break;
+ }
+ case PHY_RECEIVE_DELAY2:
+ {
+ phyParam.Value = AS923_RECEIVE_DELAY2;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY1:
+ {
+ phyParam.Value = AS923_JOIN_ACCEPT_DELAY1;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY2:
+ {
+ phyParam.Value = AS923_JOIN_ACCEPT_DELAY2;
+ break;
+ }
+ case PHY_MAX_FCNT_GAP:
+ {
+ phyParam.Value = AS923_MAX_FCNT_GAP;
+ break;
+ }
+ case PHY_ACK_TIMEOUT:
+ {
+ phyParam.Value = ( AS923_ACKTIMEOUT + randr( -AS923_ACK_TIMEOUT_RND, AS923_ACK_TIMEOUT_RND ) );
+ break;
+ }
+ case PHY_DEF_DR1_OFFSET:
+ {
+ phyParam.Value = AS923_DEFAULT_RX1_DR_OFFSET;
+ break;
+ }
+ case PHY_DEF_RX2_FREQUENCY:
+ {
+ phyParam.Value = AS923_RX_WND_2_FREQ;
+ break;
+ }
+ case PHY_DEF_RX2_DR:
+ {
+ phyParam.Value = AS923_RX_WND_2_DR;
+ break;
+ }
+ case PHY_CHANNELS_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = AS923_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = Channels;
+ break;
+ }
+ case PHY_DEF_UPLINK_DWELL_TIME:
+ {
+ phyParam.Value = AS923_DEFAULT_UPLINK_DWELL_TIME;
+ break;
+ }
+ case PHY_DEF_DOWNLINK_DWELL_TIME:
+ {
+ phyParam.Value = AS923_DEFAULT_DOWNLINK_DWELL_TIME;
+ break;
+ }
+ case PHY_DEF_MAX_EIRP:
+ {
+ phyParam.fValue = AS923_DEFAULT_MAX_EIRP;
+ break;
+ }
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = AS923_DEFAULT_ANTENNA_GAIN;
+ break;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ case PHY_DEF_NB_JOIN_TRIALS:
+ {
+ phyParam.Value = 1;
+ break;
+ }
+ case PHY_BEACON_INTERVAL:
+ {
+ phyParam.Value = AS923_BEACON_INTERVAL;
+ break;
+ }
+ case PHY_BEACON_RESERVED:
+ {
+ phyParam.Value = AS923_BEACON_RESERVED;
+ break;
+ }
+ case PHY_BEACON_GUARD:
+ {
+ phyParam.Value = AS923_BEACON_GUARD;
+ break;
+ }
+ case PHY_BEACON_WINDOW:
+ {
+ phyParam.Value = AS923_BEACON_WINDOW;
+ break;
+ }
+ case PHY_BEACON_WINDOW_SLOTS:
+ {
+ phyParam.Value = AS923_BEACON_WINDOW_SLOTS;
+ break;
+ }
+ case PHY_PING_SLOT_WINDOW:
+ {
+ phyParam.Value = AS923_PING_SLOT_WINDOW;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_DEFAULT:
+ {
+ phyParam.Value = AS923_BEACON_SYMBOL_TO_DEFAULT;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = AS923_BEACON_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = AS923_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = AS923_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = AS923_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_MAX_BEACON_LESS_PERIOD:
+ {
+ phyParam.Value = AS923_MAX_BEACON_LESS_PERIOD;
+ break;
+ }
+ case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+ {
+ phyParam.Value = AS923_BEACON_DELAY_BEACON_TIMING_ANS;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = AS923_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_PINGSLOT_CHANNEL_FREQ:
+ {
+ phyParam.Value = AS923_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_SIZE:
+ {
+ phyParam.Value = AS923_BEACON_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = AS923_BEACON_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionAS923SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionAS923InitDefaults( InitType_t type )
+{
+ switch( type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Channels
+ Channels[0] = ( ChannelParams_t ) AS923_LC1;
+ Channels[1] = ( ChannelParams_t ) AS923_LC2;
+
+ // Initialize the channels default mask
+ ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE:
+ {
+ // Restore channels default mask
+ ChannelsMask[0] |= ChannelsDefaultMask[0];
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionAS923Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_TX_DR:
+ {
+ if( verify->DatarateParams.UplinkDwellTime == 0 )
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE );
+ }
+ else
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_DWELL_LIMIT_DATARATE, AS923_TX_MAX_DATARATE );
+ }
+ }
+ case PHY_DEF_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+ }
+ case PHY_RX_DR:
+ {
+ if( verify->DatarateParams.DownlinkDwellTime == 0 )
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_RX_MIN_DATARATE, AS923_RX_MAX_DATARATE );
+ }
+ else
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_DWELL_LIMIT_DATARATE, AS923_RX_MAX_DATARATE );
+ }
+ }
+ case PHY_DEF_TX_POWER:
+ case PHY_TX_POWER:
+ {
+ // Remark: switched min and max!
+ return RegionCommonValueInRange( verify->TxPower, AS923_MAX_TX_POWER, AS923_MIN_TX_POWER );
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ return AS923_DUTY_CYCLE_ENABLED;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ {
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+void RegionAS923ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ ChannelParams_t newChannel;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ // Setup default datarate range
+ newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+ // Size of the optional CF list
+ if( applyCFList->Size != 16 )
+ {
+ return;
+ }
+
+ // Last byte is RFU, don't take it into account
+ for( uint8_t i = 0, chanIdx = AS923_NUMB_DEFAULT_CHANNELS; chanIdx < AS923_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+ {
+ if( chanIdx < ( AS923_NUMB_CHANNELS_CF_LIST + AS923_NUMB_DEFAULT_CHANNELS ) )
+ {
+ // Channel frequency
+ newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
+ newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
+ newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
+ newChannel.Frequency *= 100;
+
+ // Initialize alternative frequency to 0
+ newChannel.Rx1Frequency = 0;
+ }
+ else
+ {
+ newChannel.Frequency = 0;
+ newChannel.DrRange.Value = 0;
+ newChannel.Rx1Frequency = 0;
+ }
+
+ if( newChannel.Frequency != 0 )
+ {
+ channelAdd.NewChannel = &newChannel;
+ channelAdd.ChannelId = chanIdx;
+
+ // Try to add all channels
+ RegionAS923ChannelAdd( &channelAdd );
+ }
+ else
+ {
+ channelRemove.ChannelId = chanIdx;
+
+ RegionAS923ChannelsRemove( &channelRemove );
+ }
+ }
+}
+
+bool RegionAS923ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegionAS923AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ bool adrAckReq = false;
+ int8_t datarate = adrNext->Datarate;
+ int8_t minTxDatarate = 0;
+ int8_t txPower = adrNext->TxPower;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionAS923GetPhyParam( &getPhy );
+ minTxDatarate = phyParam.Value;
+
+ // Report back the adr ack counter
+ *adrAckCounter = adrNext->AdrAckCounter;
+
+ // Apply the minimum possible datarate.
+ datarate = MAX( datarate, minTxDatarate );
+
+ if( adrNext->AdrEnabled == true )
+ {
+ if( datarate == minTxDatarate )
+ {
+ *adrAckCounter = 0;
+ adrAckReq = false;
+ }
+ else
+ {
+ if( adrNext->AdrAckCounter >= AS923_ADR_ACK_LIMIT )
+ {
+ adrAckReq = true;
+ txPower = AS923_MAX_TX_POWER;
+ }
+ else
+ {
+ adrAckReq = false;
+ }
+ if( adrNext->AdrAckCounter >= ( AS923_ADR_ACK_LIMIT + AS923_ADR_ACK_DELAY ) )
+ {
+ if( ( adrNext->AdrAckCounter % AS923_ADR_ACK_DELAY ) == 1 )
+ {
+ // Decrease the datarate
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.Datarate = datarate;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionAS923GetPhyParam( &getPhy );
+ datarate = phyParam.Value;
+
+ if( datarate == minTxDatarate )
+ {
+ // We must set adrAckReq to false as soon as we reach the lowest datarate
+ adrAckReq = false;
+ if( adrNext->UpdateChanMask == true )
+ {
+ // Re-enable default channels
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *drOut = datarate;
+ *txPowOut = txPower;
+ return adrAckReq;
+}
+
+void RegionAS923ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+ double tSymbol = 0.0;
+
+ // Get the datarate, perform a boundary check
+ rxConfigParams->Datarate = MIN( datarate, AS923_RX_MAX_DATARATE );
+ rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+ if( rxConfigParams->Datarate == DR_7 )
+ { // FSK
+ tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesAS923[rxConfigParams->Datarate] );
+ }
+ else
+ { // LoRa
+ tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesAS923[rxConfigParams->Datarate], BandwidthsAS923[rxConfigParams->Datarate] );
+ }
+
+ RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+ RadioModems_t modem;
+ int8_t dr = rxConfig->Datarate;
+ uint8_t maxPayload = 0;
+ int8_t phyDr = 0;
+ uint32_t frequency = rxConfig->Frequency;
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->Window == 0 )
+ {
+ // Apply window 1 frequency
+ frequency = Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = Channels[rxConfig->Channel].Rx1Frequency;
+ }
+ }
+
+ // Read the physical datarate from the datarates table
+ phyDr = DataratesAS923[dr];
+
+ Radio.SetChannel( frequency );
+
+ // Radio configuration
+ if( dr == DR_7 )
+ {
+ modem = MODEM_FSK;
+ Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+ }
+
+ // Check for repeater support
+ if( rxConfig->RepeaterSupport == true )
+ {
+ maxPayload = MaxPayloadOfDatarateRepeaterDwell0AS923[dr];
+ }
+ else
+ {
+ maxPayload = MaxPayloadOfDatarateDwell0AS923[dr];
+ }
+
+ Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+ *datarate = (uint8_t) dr;
+ return true;
+}
+
+bool RegionAS923TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ RadioModems_t modem;
+ int8_t phyDr = DataratesAS923[txConfig->Datarate];
+ int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+ uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+ int8_t phyTxPower = 0;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+ // Setup the radio frequency
+ Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+ if( txConfig->Datarate == DR_7 )
+ { // High Speed FSK channel
+ modem = MODEM_FSK;
+ Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+ }
+
+ // Setup maximum payload lenght of the radio driver
+ Radio.SetMaxPayloadLength( modem, txConfig->PktLen );
+ // Get the time-on-air of the next tx frame
+ *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen );
+
+ *txPower = txPowerLimited;
+ return true;
+}
+
+uint8_t RegionAS923LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+ uint8_t status = 0x07;
+ RegionCommonLinkAdrParams_t linkAdrParams;
+ uint8_t nextIndex = 0;
+ uint8_t bytesProcessed = 0;
+ uint16_t chMask = 0;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+ while( bytesProcessed < linkAdrReq->PayloadSize )
+ {
+ // Get ADR request parameters
+ nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+ if( nextIndex == 0 )
+ break; // break loop, since no more request has been found
+
+ // Update bytes processed
+ bytesProcessed += nextIndex;
+
+ // Revert status, as we only check the last ADR request for the channel mask KO
+ status = 0x07;
+
+ // Setup temporary channels mask
+ chMask = linkAdrParams.ChMask;
+
+ // Verify channels mask
+ if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
+ {
+ status &= 0xFE; // Channel mask KO
+ }
+ else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
+ ( linkAdrParams.ChMaskCtrl >= 7 ) )
+ {
+ // RFU
+ status &= 0xFE; // Channel mask KO
+ }
+ else
+ {
+ for( uint8_t i = 0; i < AS923_MAX_NB_CHANNELS; i++ )
+ {
+ if( linkAdrParams.ChMaskCtrl == 6 )
+ {
+ if( Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( Channels[i].Frequency == 0 ) )
+ {// Trying to enable an undefined channel
+ status &= 0xFE; // Channel mask KO
+ }
+ }
+ }
+ }
+ }
+
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+ phyParam = RegionAS923GetPhyParam( &getPhy );
+
+ linkAdrVerifyParams.Status = status;
+ linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+ linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+ linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+ linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+ linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+ linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+ linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+ linkAdrVerifyParams.NbChannels = AS923_MAX_NB_CHANNELS;
+ linkAdrVerifyParams.ChannelsMask = &chMask;
+ linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+ linkAdrVerifyParams.MaxDatarate = AS923_TX_MAX_DATARATE;
+ linkAdrVerifyParams.Channels = Channels;
+ linkAdrVerifyParams.MinTxPower = AS923_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = AS923_MAX_TX_POWER;
+
+ // Verify the parameters and update, if necessary
+ status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+ // Update channelsMask if everything is correct
+ if( status == 0x07 )
+ {
+ // Set the channels mask to a default value
+ memset( ChannelsMask, 0, sizeof( ChannelsMask ) );
+ // Update the channels mask
+ ChannelsMask[0] = chMask;
+ }
+
+ // Update status variables
+ *drOut = linkAdrParams.Datarate;
+ *txPowOut = linkAdrParams.TxPower;
+ *nbRepOut = linkAdrParams.NbRep;
+ *nbBytesParsed = bytesProcessed;
+
+ return status;
+}
+
+uint8_t RegionAS923RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ uint8_t status = 0x07;
+
+ // Verify radio frequency
+ if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ // Verify datarate
+ if( RegionCommonValueInRange( rxParamSetupReq->Datarate, AS923_RX_MIN_DATARATE, AS923_RX_MAX_DATARATE ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify datarate offset
+ if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, AS923_MIN_RX1_DR_OFFSET, AS923_MAX_RX1_DR_OFFSET ) == false )
+ {
+ status &= 0xFB; // Rx1DrOffset range KO
+ }
+
+ return status;
+}
+
+uint8_t RegionAS923NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+ uint8_t status = 0x03;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ if( newChannelReq->NewChannel->Frequency == 0 )
+ {
+ channelRemove.ChannelId = newChannelReq->ChannelId;
+
+ // Remove
+ if( RegionAS923ChannelsRemove( &channelRemove ) == false )
+ {
+ status &= 0xFC;
+ }
+ }
+ else
+ {
+ channelAdd.NewChannel = newChannelReq->NewChannel;
+ channelAdd.ChannelId = newChannelReq->ChannelId;
+
+ switch( RegionAS923ChannelAdd( &channelAdd ) )
+ {
+ case LORAMAC_STATUS_OK:
+ {
+ break;
+ }
+ case LORAMAC_STATUS_FREQUENCY_INVALID:
+ {
+ status &= 0xFE;
+ break;
+ }
+ case LORAMAC_STATUS_DATARATE_INVALID:
+ {
+ status &= 0xFD;
+ break;
+ }
+ case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
+ {
+ status &= 0xFC;
+ break;
+ }
+ default:
+ {
+ status &= 0xFC;
+ break;
+ }
+ }
+ }
+
+ return status;
+}
+
+int8_t RegionAS923TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+ // Accept the request
+ return 0;
+}
+
+uint8_t RegionAS923DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ uint8_t status = 0x03;
+
+ // Verify if the frequency is supported
+ if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionAS923AlternateDr( AlternateDrParams_t* alternateDr )
+{
+ // Only AS923_DWELL_LIMIT_DATARATE is supported
+ return AS923_DWELL_LIMIT_DATARATE;
+}
+
+void RegionAS923CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = Channels;
+ calcBackOffParams.Bands = Bands;
+ calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+ calcBackOffParams.Joined = calcBackOff->Joined;
+ calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+ calcBackOffParams.Channel = calcBackOff->Channel;
+ calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+ calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+ RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionAS923NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ uint8_t channelNext = 0;
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTx = 0;
+ uint8_t enabledChannels[AS923_MAX_NB_CHANNELS] = { 0 };
+ TimerTime_t nextTxDelay = 0;
+
+ if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 );
+ }
+
+ if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, AS923_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ ChannelsMask, Channels,
+ Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ for( uint8_t i = 0, j = randr( 0, nbEnabledChannels - 1 ); i < AS923_MAX_NB_CHANNELS; i++ )
+ {
+ channelNext = enabledChannels[j];
+ j = ( j + 1 ) % nbEnabledChannels;
+
+ // Perform carrier sense for AS923_CARRIER_SENSE_TIME
+ // If the channel is free, we can stop the LBT mechanism
+ if( Radio.IsChannelFree( MODEM_LORA, Channels[channelNext].Frequency, AS923_RSSI_FREE_TH, AS923_CARRIER_SENSE_TIME ) == true )
+ {
+ // Free channel found
+ *channel = channelNext;
+ *time = 0;
+ return true;
+ }
+ }
+ return false;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return true;
+ }
+ // Datarate not supported by any channel, restore defaults
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 );
+ *time = 0;
+ return false;
+ }
+}
+
+LoRaMacStatus_t RegionAS923ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ uint8_t band = 0;
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id >= AS923_MAX_NB_CHANNELS )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ // Validate the datarate range
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+ {
+ drInvalid = true;
+ }
+
+ // Default channels don't accept all values
+ if( id < AS923_NUMB_DEFAULT_CHANNELS )
+ {
+ // Validate the datarate range for min: must be DR_0
+ if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 )
+ {
+ drInvalid = true;
+ }
+ // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, AS923_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ // We are not allowed to change the frequency
+ if( channelAdd->NewChannel->Frequency != Channels[id].Frequency )
+ {
+ freqInvalid = true;
+ }
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false )
+ {
+ freqInvalid = true;
+ }
+ }
+
+ // Check status
+ if( ( drInvalid == true ) && ( freqInvalid == true ) )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+ if( drInvalid == true )
+ {
+ return LORAMAC_STATUS_DATARATE_INVALID;
+ }
+ if( freqInvalid == true )
+ {
+ return LORAMAC_STATUS_FREQUENCY_INVALID;
+ }
+
+ memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) );
+ Channels[id].Band = band;
+ ChannelsMask[0] |= ( 1 << id );
+ return LORAMAC_STATUS_OK;
+}
+
+bool RegionAS923ChannelsRemove( ChannelRemoveParams_t* channelRemove )
+{
+ uint8_t id = channelRemove->ChannelId;
+
+ if( id < AS923_NUMB_DEFAULT_CHANNELS )
+ {
+ return false;
+ }
+
+ // Remove the channel from the list of channels
+ Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( ChannelsMask, id, AS923_MAX_NB_CHANNELS );
+}
+
+void RegionAS923SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+ Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionAS923ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ // Initialize minDr for a downlink dwell time configuration of 0
+ int8_t minDr = DR_0;
+
+ // Update the minDR for a downlink dwell time configuration of 1
+ if( downlinkDwellTime == 1 )
+ {
+ minDr = AS923_DWELL_LIMIT_DATARATE;
+ }
+
+ // Apply offset formula
+ return MIN( DR_5, MAX( minDr, dr - EffectiveRx1DrOffsetAS923[drOffset] ) );
+}
+
+void RegionAS923RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesAS923;
+ regionCommonRxBeaconSetup.ChannelPlanFrequency = AS923_BEACON_CHANNEL_FREQ;
+ regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = AS923_BEACON_CHANNEL_FREQ;
+ regionCommonRxBeaconSetup.BeaconSize = AS923_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = AS923_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = AS923_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+ regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+ regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup, beaconChannelSet );
+
+ // Store downlink datarate
+ *outDr = AS923_BEACON_CHANNEL_DR;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionAS923.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,593 @@
+/*!
+ * \file RegionAS923.h
+ *
+ * \brief Region definition for AS923
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGIONAS923 Region AS923
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_AS923_H__
+#define __REGION_AS923_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define AS923_MAX_NB_CHANNELS 16
+
+/*!
+ * Number of default channels
+ */
+#define AS923_NUMB_DEFAULT_CHANNELS 2
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define AS923_NUMB_CHANNELS_CF_LIST 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define AS923_TX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define AS923_TX_MAX_DATARATE DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define AS923_RX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define AS923_RX_MAX_DATARATE DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define AS923_DEFAULT_DATARATE DR_2
+
+/*!
+ * The minimum datarate which is used when the
+ * dwell time is limited.
+ */
+#define AS923_DWELL_LIMIT_DATARATE DR_2
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define AS923_MIN_RX1_DR_OFFSET 0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define AS923_MAX_RX1_DR_OFFSET 7
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define AS923_DEFAULT_RX1_DR_OFFSET 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define AS923_MIN_TX_POWER TX_POWER_7
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define AS923_MAX_TX_POWER TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define AS923_DEFAULT_TX_POWER TX_POWER_0
+
+/*!
+ * Default uplink dwell time configuration
+ */
+#define AS923_DEFAULT_UPLINK_DWELL_TIME 1
+
+/*!
+ * Default downlink dwell time configuration
+ */
+#define AS923_DEFAULT_DOWNLINK_DWELL_TIME 1
+
+/*!
+ * Default Max EIRP
+ */
+#define AS923_DEFAULT_MAX_EIRP 16.0f
+
+/*!
+ * Default antenna gain
+ */
+#define AS923_DEFAULT_ANTENNA_GAIN 2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define AS923_ADR_ACK_LIMIT 64
+
+/*!
+ * ADR Ack delay
+ */
+#define AS923_ADR_ACK_DELAY 32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define AS923_DUTY_CYCLE_ENABLED 0
+
+/*!
+ * Maximum RX window duration
+ */
+#define AS923_MAX_RX_WINDOW 3000
+
+/*!
+ * Receive delay 1
+ */
+#define AS923_RECEIVE_DELAY1 1000
+
+/*!
+ * Receive delay 2
+ */
+#define AS923_RECEIVE_DELAY2 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define AS923_JOIN_ACCEPT_DELAY1 5000
+
+/*!
+ * Join accept delay 2
+ */
+#define AS923_JOIN_ACCEPT_DELAY2 6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define AS923_MAX_FCNT_GAP 16384
+
+/*!
+ * Ack timeout
+ */
+#define AS923_ACKTIMEOUT 2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define AS923_ACK_TIMEOUT_RND 1000
+
+#if ( AS923_DEFAULT_DATARATE > DR_5 )
+#error "A default DR higher than DR_5 may lead to connectivity loss."
+#endif
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define AS923_RX_WND_2_FREQ 923200000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define AS923_RX_WND_2_DR DR_2
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define AS923_BEACON_INTERVAL 128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define AS923_BEACON_RESERVED 2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define AS923_BEACON_GUARD 3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define AS923_BEACON_WINDOW 122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define AS923_BEACON_WINDOW_SLOTS 4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define AS923_PING_SLOT_WINDOW 30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define AS923_BEACON_SYMBOL_TO_DEFAULT 8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define AS923_BEACON_SYMBOL_TO_EXPANSION_MAX 400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define AS923_PING_SLOT_SYMBOL_TO_EXPANSION_MAX 40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define AS923_BEACON_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define AS923_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define AS923_MAX_BEACON_LESS_PERIOD 7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define AS923_BEACON_DELAY_BEACON_TIMING_ANS 30
+
+/*!
+ * Beacon frequency
+ */
+#define AS923_BEACON_CHANNEL_FREQ 923400000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define AS923_BEACON_SIZE 17
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define AS923_BEACON_CHANNEL_DR DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define AS923_BEACON_CHANNEL_BW 0
+
+/*!
+ * Maximum number of bands
+ */
+#define AS923_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define AS923_BAND0 { 100, AS923_MAX_TX_POWER, 0, 0 } // 1.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define AS923_LC1 { 923200000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define AS923_LC2 { 923400000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define AS923_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) )
+
+/*!
+ * RSSI threshold for a free channel [dBm]
+ */
+#define AS923_RSSI_FREE_TH -85
+
+/*!
+ * Specifies the time the node performs a carrier sense
+ */
+#define AS923_CARRIER_SENSE_TIME 6
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesAS923[] = { 12, 11, 10, 9, 8, 7, 7, 50 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsAS923[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ * The table is valid for the dwell time configuration of 0 for uplinks and downlinks.
+ */
+static const uint8_t MaxPayloadOfDatarateDwell0AS923[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ * The table is valid for the dwell time configuration of 0 for uplinks and downlinks. The table provides
+ * repeater support.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterDwell0AS923[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with and without repeater.
+ * The table proides repeater support. The table is only valid for uplinks.
+ */
+static const uint8_t MaxPayloadOfDatarateDwell1UpAS923[] = { 0, 0, 11, 53, 125, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with and without repeater.
+ * The table proides repeater support. The table is only valid for downlinks.
+ */
+static const uint8_t MaxPayloadOfDatarateDwell1DownAS923[] = { 0, 0, 11, 53, 126, 242, 242, 242 };
+
+/*!
+ * Effective datarate offsets for receive window 1.
+ */
+static const int8_t EffectiveRx1DrOffsetAS923[] = { 0, 1, 2, 3, 4, 5, -1, -2 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionAS923GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionAS923SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionAS923InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionAS923Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ * CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionAS923ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionAS923ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionAS923AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ * The receiver will turn on in a [-rxError : +rxError] ms
+ * interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionAS923ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionAS923TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAS923LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAS923RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAS923NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ * Returns -1, if the functionality is not implemented. In this case, the end node
+ * shall not process the command.
+ */
+int8_t RegionAS923TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAS923DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionAS923AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionAS923CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ * cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionAS923NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionAS923ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionAS923ChannelsRemove( ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionAS923SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionAS923ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONAS923 */
+
+#endif // __REGION_AS923_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionAU915.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,968 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region AU915 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionAU915.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 6
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[AU915_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[AU915_MAX_NB_BANDS] =
+{
+ AU915_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels remaining
+ */
+static uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+ uint8_t nextLowerDr = 0;
+
+ if( dr == minDr )
+ {
+ nextLowerDr = minDr;
+ }
+ else
+ {
+ nextLowerDr = dr - 1;
+ }
+ return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+ switch( BandwidthsAU915[drIndex] )
+ {
+ default:
+ case 125000:
+ return 0;
+ case 250000:
+ return 1;
+ case 500000:
+ return 2;
+ }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+ int8_t txPowerResult = txPower;
+
+ // Limit tx power to the band max
+ txPowerResult = MAX( txPower, maxBandTxPower );
+
+ return txPowerResult;
+}
+
+static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTransmission = 0;
+
+ for( uint8_t i = 0, k = 0; i < AU915_MAX_NB_CHANNELS; i += 16, k++ )
+ {
+ for( uint8_t j = 0; j < 16; j++ )
+ {
+ if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+ {
+ if( channels[i + j].Frequency == 0 )
+ { // Check if the channel is enabled
+ continue;
+ }
+ if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+ channels[i + j].DrRange.Fields.Max ) == false )
+ { // Check if the current channel selection supports the given datarate
+ continue;
+ }
+ if( bands[channels[i + j].Band].TimeOff > 0 )
+ { // Check if the band is available for transmission
+ delayTransmission++;
+ continue;
+ }
+ enabledChannels[nbEnabledChannels++] = i + j;
+ }
+ }
+ }
+
+ *delayTx = delayTransmission;
+ return nbEnabledChannels;
+}
+
+static uint8_t BeaconChannel( uint32_t devAddr, TimerTime_t beaconTime, TimerTime_t beaconInterval )
+{
+ uint32_t frequency = 0;
+
+ frequency = devAddr + ( beaconTime / ( beaconInterval / 1000 ) );
+
+ return ( ( uint8_t )( frequency % 8 ) );
+}
+
+PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ phyParam.Value = AU915_RX_MIN_DATARATE;
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ phyParam.Value = AU915_TX_MIN_DATARATE;
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = AU915_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AU915_TX_MIN_DATARATE );
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = AU915_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ phyParam.Value = MaxPayloadOfDatarateAU915[getPhy->Datarate];
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterAU915[getPhy->Datarate];
+ break;
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ phyParam.Value = AU915_DUTY_CYCLE_ENABLED;
+ break;
+ }
+ case PHY_MAX_RX_WINDOW:
+ {
+ phyParam.Value = AU915_MAX_RX_WINDOW;
+ break;
+ }
+ case PHY_RECEIVE_DELAY1:
+ {
+ phyParam.Value = AU915_RECEIVE_DELAY1;
+ break;
+ }
+ case PHY_RECEIVE_DELAY2:
+ {
+ phyParam.Value = AU915_RECEIVE_DELAY2;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY1:
+ {
+ phyParam.Value = AU915_JOIN_ACCEPT_DELAY1;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY2:
+ {
+ phyParam.Value = AU915_JOIN_ACCEPT_DELAY2;
+ break;
+ }
+ case PHY_MAX_FCNT_GAP:
+ {
+ phyParam.Value = AU915_MAX_FCNT_GAP;
+ break;
+ }
+ case PHY_ACK_TIMEOUT:
+ {
+ phyParam.Value = ( AU915_ACKTIMEOUT + randr( -AU915_ACK_TIMEOUT_RND, AU915_ACK_TIMEOUT_RND ) );
+ break;
+ }
+ case PHY_DEF_DR1_OFFSET:
+ {
+ phyParam.Value = AU915_DEFAULT_RX1_DR_OFFSET;
+ break;
+ }
+ case PHY_DEF_RX2_FREQUENCY:
+ {
+ phyParam.Value = AU915_RX_WND_2_FREQ;
+ break;
+ }
+ case PHY_DEF_RX2_DR:
+ {
+ phyParam.Value = AU915_RX_WND_2_DR;
+ break;
+ }
+ case PHY_CHANNELS_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = AU915_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = Channels;
+ break;
+ }
+ case PHY_DEF_UPLINK_DWELL_TIME:
+ case PHY_DEF_DOWNLINK_DWELL_TIME:
+ {
+ phyParam.Value = 0;
+ break;
+ }
+ case PHY_DEF_MAX_EIRP:
+ {
+ phyParam.fValue = AU915_DEFAULT_MAX_EIRP;
+ break;
+ }
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = AU915_DEFAULT_ANTENNA_GAIN;
+ break;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ case PHY_DEF_NB_JOIN_TRIALS:
+ {
+ phyParam.Value = 2;
+ break;
+ }
+ case PHY_BEACON_INTERVAL:
+ {
+ phyParam.Value = AU915_BEACON_INTERVAL;
+ break;
+ }
+ case PHY_BEACON_RESERVED:
+ {
+ phyParam.Value = AU915_BEACON_RESERVED;
+ break;
+ }
+ case PHY_BEACON_GUARD:
+ {
+ phyParam.Value = AU915_BEACON_GUARD;
+ break;
+ }
+ case PHY_BEACON_WINDOW:
+ {
+ phyParam.Value = AU915_BEACON_WINDOW;
+ break;
+ }
+ case PHY_BEACON_WINDOW_SLOTS:
+ {
+ phyParam.Value = AU915_BEACON_WINDOW_SLOTS;
+ break;
+ }
+ case PHY_PING_SLOT_WINDOW:
+ {
+ phyParam.Value = AU915_PING_SLOT_WINDOW;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_DEFAULT:
+ {
+ phyParam.Value = AU915_BEACON_SYMBOL_TO_DEFAULT;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = AU915_BEACON_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = AU915_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = AU915_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = AU915_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_MAX_BEACON_LESS_PERIOD:
+ {
+ phyParam.Value = AU915_MAX_BEACON_LESS_PERIOD;
+ break;
+ }
+ case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+ {
+ phyParam.Value = AU915_BEACON_DELAY_BEACON_TIMING_ANS;
+ break;
+ }
+ case PHY_PINGSLOT_CHANNEL_FREQ:
+ {
+ phyParam.Value = AU915_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_SIZE:
+ {
+ phyParam.Value = AU915_BEACON_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = AU915_BEACON_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionAU915InitDefaults( InitType_t type )
+{
+ switch( type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Channels
+ // 125 kHz channels
+ for( uint8_t i = 0; i < AU915_MAX_NB_CHANNELS - 8; i++ )
+ {
+ Channels[i].Frequency = 915200000 + i * 200000;
+ Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0;
+ Channels[i].Band = 0;
+ }
+ // 500 kHz channels
+ for( uint8_t i = AU915_MAX_NB_CHANNELS - 8; i < AU915_MAX_NB_CHANNELS; i++ )
+ {
+ Channels[i].Frequency = 915900000 + ( i - ( AU915_MAX_NB_CHANNELS - 8 ) ) * 1600000;
+ Channels[i].DrRange.Value = ( DR_6 << 4 ) | DR_6;
+ Channels[i].Band = 0;
+ }
+
+ // Initialize channels default mask
+ ChannelsDefaultMask[0] = 0xFFFF;
+ ChannelsDefaultMask[1] = 0xFFFF;
+ ChannelsDefaultMask[2] = 0xFFFF;
+ ChannelsDefaultMask[3] = 0xFFFF;
+ ChannelsDefaultMask[4] = 0x00FF;
+ ChannelsDefaultMask[5] = 0x0000;
+
+ // Copy channels default mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+
+ // Copy into channels mask remaining
+ RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 );
+ break;
+ }
+ case INIT_TYPE_RESTORE:
+ {
+ // Copy channels default mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+
+ for( uint8_t i = 0; i < 6; i++ )
+ { // Copy-And the channels mask
+ ChannelsMaskRemaining[i] &= ChannelsMask[i];
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_TX_DR:
+ case PHY_DEF_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_TX_MIN_DATARATE, AU915_TX_MAX_DATARATE );
+ }
+ case PHY_RX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_POWER:
+ case PHY_TX_POWER:
+ {
+ // Remark: switched min and max!
+ return RegionCommonValueInRange( verify->TxPower, AU915_MAX_TX_POWER, AU915_MIN_TX_POWER );
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ return AU915_DUTY_CYCLE_ENABLED;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ {
+ if( verify->NbJoinTrials < 2 )
+ {
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ return;
+}
+
+bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ uint8_t nbChannels = RegionCommonCountChannels( chanMaskSet->ChannelsMaskIn, 0, 4 );
+
+ // Check the number of active channels
+ // According to ACMA regulation, we require at least 20 125KHz channels, if
+ // the node shall utilize 125KHz channels.
+ if( ( nbChannels < 20 ) &&
+ ( nbChannels > 0 ) )
+ {
+ return false;
+ }
+
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 );
+
+ for( uint8_t i = 0; i < 6; i++ )
+ { // Copy-And the channels mask
+ ChannelsMaskRemaining[i] &= ChannelsMask[i];
+ }
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegionAU915AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ bool adrAckReq = false;
+ int8_t datarate = adrNext->Datarate;
+ int8_t txPower = adrNext->TxPower;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Report back the adr ack counter
+ *adrAckCounter = adrNext->AdrAckCounter;
+
+ if( adrNext->AdrEnabled == true )
+ {
+ if( datarate == AU915_TX_MIN_DATARATE )
+ {
+ *adrAckCounter = 0;
+ adrAckReq = false;
+ }
+ else
+ {
+ if( adrNext->AdrAckCounter >= AU915_ADR_ACK_LIMIT )
+ {
+ adrAckReq = true;
+ txPower = AU915_MAX_TX_POWER;
+ }
+ else
+ {
+ adrAckReq = false;
+ }
+ if( adrNext->AdrAckCounter >= ( AU915_ADR_ACK_LIMIT + AU915_ADR_ACK_DELAY ) )
+ {
+ if( ( adrNext->AdrAckCounter % AU915_ADR_ACK_DELAY ) == 1 )
+ {
+ // Decrease the datarate
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.Datarate = datarate;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionAU915GetPhyParam( &getPhy );
+ datarate = phyParam.Value;
+
+ if( datarate == AU915_TX_MIN_DATARATE )
+ {
+ // We must set adrAckReq to false as soon as we reach the lowest datarate
+ adrAckReq = false;
+ if( adrNext->UpdateChanMask == true )
+ {
+ // Re-enable default channels
+ ChannelsMask[0] = 0xFFFF;
+ ChannelsMask[1] = 0xFFFF;
+ ChannelsMask[2] = 0xFFFF;
+ ChannelsMask[3] = 0xFFFF;
+ ChannelsMask[4] = 0x00FF;
+ ChannelsMask[5] = 0x0000;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *drOut = datarate;
+ *txPowOut = txPower;
+ return adrAckReq;
+}
+
+void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+ double tSymbol = 0.0;
+
+ // Get the datarate, perform a boundary check
+ rxConfigParams->Datarate = MIN( datarate, AU915_RX_MAX_DATARATE );
+ rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+ tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesAU915[rxConfigParams->Datarate], BandwidthsAU915[rxConfigParams->Datarate] );
+
+ RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+ int8_t dr = rxConfig->Datarate;
+ uint8_t maxPayload = 0;
+ int8_t phyDr = 0;
+ uint32_t frequency = rxConfig->Frequency;
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->Window == 0 )
+ {
+ // Apply window 1 frequency
+ frequency = AU915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * AU915_STEPWIDTH_RX1_CHANNEL;
+ }
+
+ // Read the physical datarate from the datarates table
+ phyDr = DataratesAU915[dr];
+
+ Radio.SetChannel( frequency );
+
+ // Radio configuration
+ Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+
+ if( rxConfig->RepeaterSupport == true )
+ {
+ maxPayload = MaxPayloadOfDatarateRepeaterAU915[dr];
+ }
+ else
+ {
+ maxPayload = MaxPayloadOfDatarateAU915[dr];
+ }
+ Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+ *datarate = (uint8_t) dr;
+ return true;
+}
+
+bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ int8_t phyDr = DataratesAU915[txConfig->Datarate];
+ int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+ uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+ int8_t phyTxPower = 0;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+ // Setup the radio frequency
+ Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+ Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+
+ // Setup maximum payload lenght of the radio driver
+ Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen );
+
+ *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen );
+ *txPower = txPowerLimited;
+
+ return true;
+}
+
+uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+ uint8_t status = 0x07;
+ RegionCommonLinkAdrParams_t linkAdrParams;
+ uint8_t nextIndex = 0;
+ uint8_t bytesProcessed = 0;
+ uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 };
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+ // Initialize local copy of channels mask
+ RegionCommonChanMaskCopy( channelsMask, ChannelsMask, 6 );
+
+ while( bytesProcessed < linkAdrReq->PayloadSize )
+ {
+ nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+ if( nextIndex == 0 )
+ break; // break loop, since no more request has been found
+
+ // Update bytes processed
+ bytesProcessed += nextIndex;
+
+ // Revert status, as we only check the last ADR request for the channel mask KO
+ status = 0x07;
+
+ if( linkAdrParams.ChMaskCtrl == 6 )
+ {
+ // Enable all 125 kHz channels
+ channelsMask[0] = 0xFFFF;
+ channelsMask[1] = 0xFFFF;
+ channelsMask[2] = 0xFFFF;
+ channelsMask[3] = 0xFFFF;
+ // Apply chMask to channels 64 to 71
+ channelsMask[4] = linkAdrParams.ChMask;
+ }
+ else if( linkAdrParams.ChMaskCtrl == 7 )
+ {
+ // Disable all 125 kHz channels
+ channelsMask[0] = 0x0000;
+ channelsMask[1] = 0x0000;
+ channelsMask[2] = 0x0000;
+ channelsMask[3] = 0x0000;
+ // Apply chMask to channels 64 to 71
+ channelsMask[4] = linkAdrParams.ChMask;
+ }
+ else if( linkAdrParams.ChMaskCtrl == 5 )
+ {
+ // RFU
+ status &= 0xFE; // Channel mask KO
+ }
+ else
+ {
+ channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask;
+ }
+ }
+
+ // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels
+ if( ( linkAdrParams.Datarate < DR_6 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) )
+ {
+ status &= 0xFE; // Channel mask KO
+ }
+
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+ phyParam = RegionAU915GetPhyParam( &getPhy );
+
+ linkAdrVerifyParams.Status = status;
+ linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+ linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+ linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+ linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+ linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+ linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+ linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+ linkAdrVerifyParams.NbChannels = AU915_MAX_NB_CHANNELS;
+ linkAdrVerifyParams.ChannelsMask = channelsMask;
+ linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+ linkAdrVerifyParams.MaxDatarate = AU915_TX_MAX_DATARATE;
+ linkAdrVerifyParams.Channels = Channels;
+ linkAdrVerifyParams.MinTxPower = AU915_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = AU915_MAX_TX_POWER;
+
+ // Verify the parameters and update, if necessary
+ status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+ // Update channelsMask if everything is correct
+ if( status == 0x07 )
+ {
+ // Copy Mask
+ RegionCommonChanMaskCopy( ChannelsMask, channelsMask, 6 );
+
+ ChannelsMaskRemaining[0] &= ChannelsMask[0];
+ ChannelsMaskRemaining[1] &= ChannelsMask[1];
+ ChannelsMaskRemaining[2] &= ChannelsMask[2];
+ ChannelsMaskRemaining[3] &= ChannelsMask[3];
+ ChannelsMaskRemaining[4] = ChannelsMask[4];
+ ChannelsMaskRemaining[5] = ChannelsMask[5];
+ }
+
+ // Update status variables
+ *drOut = linkAdrParams.Datarate;
+ *txPowOut = linkAdrParams.TxPower;
+ *nbRepOut = linkAdrParams.NbRep;
+ *nbBytesParsed = bytesProcessed;
+
+ return status;
+}
+
+uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ uint8_t status = 0x07;
+ uint32_t freq = rxParamSetupReq->Frequency;
+
+ // Verify radio frequency
+ if( ( Radio.CheckRfFrequency( freq ) == false ) ||
+ ( freq < AU915_FIRST_RX1_CHANNEL ) ||
+ ( freq > AU915_LAST_RX1_CHANNEL ) ||
+ ( ( ( freq - ( uint32_t ) AU915_FIRST_RX1_CHANNEL ) % ( uint32_t ) AU915_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ // Verify datarate
+ if( RegionCommonValueInRange( rxParamSetupReq->Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+ if( ( rxParamSetupReq->Datarate == DR_7 ) ||
+ ( rxParamSetupReq->Datarate > DR_13 ) )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify datarate offset
+ if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, AU915_MIN_RX1_DR_OFFSET, AU915_MAX_RX1_DR_OFFSET ) == false )
+ {
+ status &= 0xFB; // Rx1DrOffset range KO
+ }
+
+ return status;
+}
+
+uint8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+ // Datarate and frequency KO
+ return 0;
+}
+
+int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+ return -1;
+}
+
+uint8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ return 0;
+}
+
+int8_t RegionAU915AlternateDr( AlternateDrParams_t* alternateDr )
+{
+ int8_t datarate = 0;
+
+ // Re-enable 500 kHz default channels
+ ChannelsMask[4] = 0x00FF;
+
+ if( ( alternateDr->NbTrials & 0x01 ) == 0x01 )
+ {
+ datarate = DR_6;
+ }
+ else
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
+
+void RegionAU915CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = Channels;
+ calcBackOffParams.Bands = Bands;
+ calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+ calcBackOffParams.Joined = calcBackOff->Joined;
+ calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+ calcBackOffParams.Channel = calcBackOff->Channel;
+ calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+ calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+ RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTx = 0;
+ uint8_t enabledChannels[AU915_MAX_NB_CHANNELS] = { 0 };
+ TimerTime_t nextTxDelay = 0;
+
+ // Count 125kHz channels
+ if( RegionCommonCountChannels( ChannelsMaskRemaining, 0, 4 ) == 0 )
+ { // Reactivate default channels
+ RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 4 );
+ }
+ // Check other channels
+ if( nextChanParams->Datarate >= DR_6 )
+ {
+ if( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 )
+ {
+ ChannelsMaskRemaining[4] = ChannelsMask[4];
+ }
+ }
+
+ if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, AU915_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate,
+ ChannelsMaskRemaining, Channels,
+ Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+ // Disable the channel in the mask
+ RegionCommonChanDisable( ChannelsMaskRemaining, *channel, AU915_MAX_NB_CHANNELS - 8 );
+
+ *time = 0;
+ return true;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return true;
+ }
+ // Datarate not supported by any channel
+ *time = 0;
+ return false;
+ }
+}
+
+LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove )
+{
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+void RegionAU915SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+ Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ int8_t datarate = DatarateOffsetsAU915[dr][drOffset];
+
+ if( datarate < 0 )
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
+
+void RegionAU915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesAU915;
+ regionCommonRxBeaconSetup.ChannelPlanFrequency = ( 923.3e6 + ( BeaconChannel( 0, rxBeaconSetup->BeaconTime, rxBeaconSetup->BeaconInterval ) * 600e3 ) );
+ regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = ( 923.3e6 + ( rxBeaconSetup->BeaconTimingChannel * 600e3 ) );
+ regionCommonRxBeaconSetup.BeaconSize = AU915_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = AU915_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = AU915_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+ regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+ regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup, beaconChannelSet );
+
+ // Store downlink datarate
+ *outDr = AU915_BEACON_CHANNEL_DR;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionAU915.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,545 @@
+/*!
+ * \file RegionAU915.h
+ *
+ * \brief Region definition for AU915
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGIONAU915 Region AU915
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_AU915_H__
+#define __REGION_AU915_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define AU915_MAX_NB_CHANNELS 72
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define AU915_TX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define AU915_TX_MAX_DATARATE DR_6
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define AU915_RX_MIN_DATARATE DR_8
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define AU915_RX_MAX_DATARATE DR_13
+
+/*!
+ * Default datarate used by the node
+ */
+#define AU915_DEFAULT_DATARATE DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define AU915_MIN_RX1_DR_OFFSET 0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define AU915_MAX_RX1_DR_OFFSET 6
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define AU915_DEFAULT_RX1_DR_OFFSET 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define AU915_MIN_TX_POWER TX_POWER_10
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define AU915_MAX_TX_POWER TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define AU915_DEFAULT_TX_POWER TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define AU915_DEFAULT_MAX_EIRP 30.0f
+
+/*!
+ * Default antenna gain
+ */
+#define AU915_DEFAULT_ANTENNA_GAIN 2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define AU915_ADR_ACK_LIMIT 64
+
+/*!
+ * ADR Ack delay
+ */
+#define AU915_ADR_ACK_DELAY 32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define AU915_DUTY_CYCLE_ENABLED 0
+
+/*!
+ * Maximum RX window duration
+ */
+#define AU915_MAX_RX_WINDOW 3000
+
+/*!
+ * Receive delay 1
+ */
+#define AU915_RECEIVE_DELAY1 1000
+
+/*!
+ * Receive delay 2
+ */
+#define AU915_RECEIVE_DELAY2 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define AU915_JOIN_ACCEPT_DELAY1 5000
+
+/*!
+ * Join accept delay 2
+ */
+#define AU915_JOIN_ACCEPT_DELAY2 6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define AU915_MAX_FCNT_GAP 16384
+
+/*!
+ * Ack timeout
+ */
+#define AU915_ACKTIMEOUT 2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define AU915_ACK_TIMEOUT_RND 1000
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define AU915_RX_WND_2_FREQ 923300000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define AU915_RX_WND_2_DR DR_8
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define AU915_BEACON_INTERVAL 128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define AU915_BEACON_RESERVED 2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define AU915_BEACON_GUARD 3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define AU915_BEACON_WINDOW 122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define AU915_BEACON_WINDOW_SLOTS 4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define AU915_PING_SLOT_WINDOW 30
+
+/*!
+ * Beacon frequency
+ */
+#define AU915_BEACON_CHANNEL_FREQ 923300000
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define AU915_BEACON_SYMBOL_TO_DEFAULT 8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define AU915_BEACON_SYMBOL_TO_EXPANSION_MAX 400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define AU915_PING_SLOT_SYMBOL_TO_EXPANSION_MAX 40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define AU915_BEACON_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define AU915_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define AU915_MAX_BEACON_LESS_PERIOD 7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define AU915_BEACON_DELAY_BEACON_TIMING_ANS 30
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define AU915_BEACON_SIZE 19
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define AU915_BEACON_CHANNEL_DR DR_10
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define AU915_BEACON_CHANNEL_BW 2
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define AU915_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define AU915_BAND0 { 1, AU915_MAX_TX_POWER, 0, 0 } // 100.0 %
+
+/*!
+ * Defines the first channel for RX window 1 for US band
+ */
+#define AU915_FIRST_RX1_CHANNEL ( (uint32_t) 923300000 )
+
+/*!
+ * Defines the last channel for RX window 1 for US band
+ */
+#define AU915_LAST_RX1_CHANNEL ( (uint32_t) 927500000 )
+
+/*!
+ * Defines the step width of the channels for RX window 1
+ */
+#define AU915_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600000 )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesAU915[] = { 12, 11, 10, 9, 8, 7, 8, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsAU915[] = { 125000, 125000, 125000, 125000, 125000, 125000, 500000, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 };
+
+/*!
+ * Up/Down link data rates offset definition
+ */
+static const int8_t DatarateOffsetsAU915[7][6] =
+{
+ { DR_8 , DR_8 , DR_8 , DR_8 , DR_8 , DR_8 }, // DR_0
+ { DR_9 , DR_8 , DR_8 , DR_8 , DR_8 , DR_8 }, // DR_1
+ { DR_10, DR_9 , DR_8 , DR_8 , DR_8 , DR_8 }, // DR_2
+ { DR_11, DR_10, DR_9 , DR_8 , DR_8 , DR_8 }, // DR_3
+ { DR_12, DR_11, DR_10, DR_9 , DR_8 , DR_8 }, // DR_4
+ { DR_13, DR_12, DR_11, DR_10, DR_9 , DR_8 }, // DR_5
+ { DR_13, DR_13, DR_12, DR_11, DR_10, DR_9 }, // DR_6
+};
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateAU915[] = { 51, 51, 51, 115, 242, 242, 242, 0, 53, 129, 242, 242, 242, 242, 0, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterAU915[] = { 51, 51, 51, 115, 222, 222, 222, 0, 33, 109, 222, 222, 222, 222, 0, 0 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionAU915InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ * CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionAU915AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ * The receiver will turn on in a [-rxError : +rxError] ms
+ * interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ * Returns -1, if the functionality is not implemented. In this case, the end node
+ * shall not process the command.
+ */
+int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionAU915AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionAU915CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ * cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionAU915SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONAU915 */
+
+#endif // __REGION_AU915_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionCN470.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,918 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region CN470 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionCN470.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 6
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[CN470_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[CN470_MAX_NB_BANDS] =
+{
+ CN470_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+ uint8_t nextLowerDr = 0;
+
+ if( dr == minDr )
+ {
+ nextLowerDr = minDr;
+ }
+ else
+ {
+ nextLowerDr = dr - 1;
+ }
+ return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+ switch( BandwidthsCN470[drIndex] )
+ {
+ default:
+ case 125000:
+ return 0;
+ case 250000:
+ return 1;
+ case 500000:
+ return 2;
+ }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+ int8_t txPowerResult = txPower;
+
+ // Limit tx power to the band max
+ txPowerResult = MAX( txPower, maxBandTxPower );
+
+ return txPowerResult;
+}
+
+static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTransmission = 0;
+
+ for( uint8_t i = 0, k = 0; i < CN470_MAX_NB_CHANNELS; i += 16, k++ )
+ {
+ for( uint8_t j = 0; j < 16; j++ )
+ {
+ if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+ {
+ if( channels[i + j].Frequency == 0 )
+ { // Check if the channel is enabled
+ continue;
+ }
+ if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+ channels[i + j].DrRange.Fields.Max ) == false )
+ { // Check if the current channel selection supports the given datarate
+ continue;
+ }
+ if( bands[channels[i + j].Band].TimeOff > 0 )
+ { // Check if the band is available for transmission
+ delayTransmission++;
+ continue;
+ }
+ enabledChannels[nbEnabledChannels++] = i + j;
+ }
+ }
+ }
+
+ *delayTx = delayTransmission;
+ return nbEnabledChannels;
+}
+
+static uint8_t BeaconChannel( uint32_t devAddr, TimerTime_t beaconTime, TimerTime_t beaconInterval )
+{
+ uint32_t frequency = 0;
+
+ frequency = devAddr + ( beaconTime / ( beaconInterval / 1000 ) );
+
+ return ( ( uint8_t )( frequency % 8 ) );
+}
+
+PhyParam_t RegionCN470GetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ phyParam.Value = CN470_RX_MIN_DATARATE;
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ phyParam.Value = CN470_TX_MIN_DATARATE;
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = CN470_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, CN470_TX_MIN_DATARATE );
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = CN470_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ phyParam.Value = MaxPayloadOfDatarateCN470[getPhy->Datarate];
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterCN470[getPhy->Datarate];
+ break;
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ phyParam.Value = CN470_DUTY_CYCLE_ENABLED;
+ break;
+ }
+ case PHY_MAX_RX_WINDOW:
+ {
+ phyParam.Value = CN470_MAX_RX_WINDOW;
+ break;
+ }
+ case PHY_RECEIVE_DELAY1:
+ {
+ phyParam.Value = CN470_RECEIVE_DELAY1;
+ break;
+ }
+ case PHY_RECEIVE_DELAY2:
+ {
+ phyParam.Value = CN470_RECEIVE_DELAY2;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY1:
+ {
+ phyParam.Value = CN470_JOIN_ACCEPT_DELAY1;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY2:
+ {
+ phyParam.Value = CN470_JOIN_ACCEPT_DELAY2;
+ break;
+ }
+ case PHY_MAX_FCNT_GAP:
+ {
+ phyParam.Value = CN470_MAX_FCNT_GAP;
+ break;
+ }
+ case PHY_ACK_TIMEOUT:
+ {
+ phyParam.Value = ( CN470_ACKTIMEOUT + randr( -CN470_ACK_TIMEOUT_RND, CN470_ACK_TIMEOUT_RND ) );
+ break;
+ }
+ case PHY_DEF_DR1_OFFSET:
+ {
+ phyParam.Value = CN470_DEFAULT_RX1_DR_OFFSET;
+ break;
+ }
+ case PHY_DEF_RX2_FREQUENCY:
+ {
+ phyParam.Value = CN470_RX_WND_2_FREQ;
+ break;
+ }
+ case PHY_DEF_RX2_DR:
+ {
+ phyParam.Value = CN470_RX_WND_2_DR;
+ break;
+ }
+ case PHY_CHANNELS_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = CN470_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = Channels;
+ break;
+ }
+ case PHY_DEF_UPLINK_DWELL_TIME:
+ case PHY_DEF_DOWNLINK_DWELL_TIME:
+ {
+ phyParam.Value = 0;
+ break;
+ }
+ case PHY_DEF_MAX_EIRP:
+ {
+ phyParam.fValue = CN470_DEFAULT_MAX_EIRP;
+ break;
+ }
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = CN470_DEFAULT_ANTENNA_GAIN;
+ break;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ case PHY_DEF_NB_JOIN_TRIALS:
+ {
+ phyParam.Value = 48;
+ break;
+ }
+ case PHY_BEACON_INTERVAL:
+ {
+ phyParam.Value = CN470_BEACON_INTERVAL;
+ break;
+ }
+ case PHY_BEACON_RESERVED:
+ {
+ phyParam.Value = CN470_BEACON_RESERVED;
+ break;
+ }
+ case PHY_BEACON_GUARD:
+ {
+ phyParam.Value = CN470_BEACON_GUARD;
+ break;
+ }
+ case PHY_BEACON_WINDOW:
+ {
+ phyParam.Value = CN470_BEACON_WINDOW;
+ break;
+ }
+ case PHY_BEACON_WINDOW_SLOTS:
+ {
+ phyParam.Value = CN470_BEACON_WINDOW_SLOTS;
+ break;
+ }
+ case PHY_PING_SLOT_WINDOW:
+ {
+ phyParam.Value = CN470_PING_SLOT_WINDOW;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_DEFAULT:
+ {
+ phyParam.Value = CN470_BEACON_SYMBOL_TO_DEFAULT;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = CN470_BEACON_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = CN470_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = CN470_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = CN470_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_MAX_BEACON_LESS_PERIOD:
+ {
+ phyParam.Value = CN470_MAX_BEACON_LESS_PERIOD;
+ break;
+ }
+ case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+ {
+ phyParam.Value = CN470_BEACON_DELAY_BEACON_TIMING_ANS;
+ break;
+ }
+ case PHY_PINGSLOT_CHANNEL_FREQ:
+ {
+ phyParam.Value = CN470_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_SIZE:
+ {
+ phyParam.Value = CN470_BEACON_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = CN470_BEACON_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionCN470InitDefaults( InitType_t type )
+{
+ switch( type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Channels
+ // 125 kHz channels
+ for( uint8_t i = 0; i < CN470_MAX_NB_CHANNELS; i++ )
+ {
+ Channels[i].Frequency = 470300000 + i * 200000;
+ Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0;
+ Channels[i].Band = 0;
+ }
+
+ // Initialize the channels default mask
+ ChannelsDefaultMask[0] = 0xFFFF;
+ ChannelsDefaultMask[1] = 0xFFFF;
+ ChannelsDefaultMask[2] = 0xFFFF;
+ ChannelsDefaultMask[3] = 0xFFFF;
+ ChannelsDefaultMask[4] = 0xFFFF;
+ ChannelsDefaultMask[5] = 0xFFFF;
+
+ // Update the channels mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+ break;
+ }
+ case INIT_TYPE_RESTORE:
+ {
+ // Restore channels default mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_TX_DR:
+ case PHY_DEF_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470_TX_MIN_DATARATE, CN470_TX_MAX_DATARATE );
+ }
+ case PHY_RX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_POWER:
+ case PHY_TX_POWER:
+ {
+ // Remark: switched min and max!
+ return RegionCommonValueInRange( verify->TxPower, CN470_MAX_TX_POWER, CN470_MIN_TX_POWER );
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ return CN470_DUTY_CYCLE_ENABLED;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ {
+ if( verify->NbJoinTrials < 48 )
+ {
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ return;
+}
+
+bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegionCN470AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ bool adrAckReq = false;
+ int8_t datarate = adrNext->Datarate;
+ int8_t txPower = adrNext->TxPower;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Report back the adr ack counter
+ *adrAckCounter = adrNext->AdrAckCounter;
+
+ if( adrNext->AdrEnabled == true )
+ {
+ if( datarate == CN470_TX_MIN_DATARATE )
+ {
+ *adrAckCounter = 0;
+ adrAckReq = false;
+ }
+ else
+ {
+ if( adrNext->AdrAckCounter >= CN470_ADR_ACK_LIMIT )
+ {
+ adrAckReq = true;
+ txPower = CN470_MAX_TX_POWER;
+ }
+ else
+ {
+ adrAckReq = false;
+ }
+ if( adrNext->AdrAckCounter >= ( CN470_ADR_ACK_LIMIT + CN470_ADR_ACK_DELAY ) )
+ {
+ if( ( adrNext->AdrAckCounter % CN470_ADR_ACK_DELAY ) == 1 )
+ {
+ // Decrease the datarate
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.Datarate = datarate;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionCN470GetPhyParam( &getPhy );
+ datarate = phyParam.Value;
+
+ if( datarate == CN470_TX_MIN_DATARATE )
+ {
+ // We must set adrAckReq to false as soon as we reach the lowest datarate
+ adrAckReq = false;
+ if( adrNext->UpdateChanMask == true )
+ {
+ // Re-enable default channels
+ ChannelsMask[0] = 0xFFFF;
+ ChannelsMask[1] = 0xFFFF;
+ ChannelsMask[2] = 0xFFFF;
+ ChannelsMask[3] = 0xFFFF;
+ ChannelsMask[4] = 0xFFFF;
+ ChannelsMask[5] = 0xFFFF;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *drOut = datarate;
+ *txPowOut = txPower;
+ return adrAckReq;
+}
+
+void RegionCN470ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+ double tSymbol = 0.0;
+
+ // Get the datarate, perform a boundary check
+ rxConfigParams->Datarate = MIN( datarate, CN470_RX_MAX_DATARATE );
+ rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+ tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesCN470[rxConfigParams->Datarate], BandwidthsCN470[rxConfigParams->Datarate] );
+
+ RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+ int8_t dr = rxConfig->Datarate;
+ uint8_t maxPayload = 0;
+ int8_t phyDr = 0;
+ uint32_t frequency = rxConfig->Frequency;
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->Window == 0 )
+ {
+ // Apply window 1 frequency
+ frequency = CN470_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 48 ) * CN470_STEPWIDTH_RX1_CHANNEL;
+ }
+
+ // Read the physical datarate from the datarates table
+ phyDr = DataratesCN470[dr];
+
+ Radio.SetChannel( frequency );
+
+ // Radio configuration
+ Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+
+ if( rxConfig->RepeaterSupport == true )
+ {
+ maxPayload = MaxPayloadOfDatarateRepeaterCN470[dr];
+ }
+ else
+ {
+ maxPayload = MaxPayloadOfDatarateCN470[dr];
+ }
+ Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+ *datarate = (uint8_t) dr;
+ return true;
+}
+
+bool RegionCN470TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ int8_t phyDr = DataratesCN470[txConfig->Datarate];
+ int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+ int8_t phyTxPower = 0;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+ // Setup the radio frequency
+ Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+ Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, 0, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+ // Setup maximum payload lenght of the radio driver
+ Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen );
+ // Get the time-on-air of the next tx frame
+ *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen );
+ *txPower = txPowerLimited;
+
+ return true;
+}
+
+uint8_t RegionCN470LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+ uint8_t status = 0x07;
+ RegionCommonLinkAdrParams_t linkAdrParams;
+ uint8_t nextIndex = 0;
+ uint8_t bytesProcessed = 0;
+ uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 };
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+ // Initialize local copy of channels mask
+ RegionCommonChanMaskCopy( channelsMask, ChannelsMask, 6 );
+
+ while( bytesProcessed < linkAdrReq->PayloadSize )
+ {
+ // Get ADR request parameters
+ nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+ if( nextIndex == 0 )
+ break; // break loop, since no more request has been found
+
+ // Update bytes processed
+ bytesProcessed += nextIndex;
+
+ // Revert status, as we only check the last ADR request for the channel mask KO
+ status = 0x07;
+
+ if( linkAdrParams.ChMaskCtrl == 6 )
+ {
+ // Enable all 125 kHz channels
+ channelsMask[0] = 0xFFFF;
+ channelsMask[1] = 0xFFFF;
+ channelsMask[2] = 0xFFFF;
+ channelsMask[3] = 0xFFFF;
+ channelsMask[4] = 0xFFFF;
+ channelsMask[5] = 0xFFFF;
+ }
+ else if( linkAdrParams.ChMaskCtrl == 7 )
+ {
+ status &= 0xFE; // Channel mask KO
+ }
+ else
+ {
+ for( uint8_t i = 0; i < 16; i++ )
+ {
+ if( ( ( linkAdrParams.ChMask & ( 1 << i ) ) != 0 ) &&
+ ( Channels[linkAdrParams.ChMaskCtrl * 16 + i].Frequency == 0 ) )
+ {// Trying to enable an undefined channel
+ status &= 0xFE; // Channel mask KO
+ }
+ }
+ channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask;
+ }
+ }
+
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+ phyParam = RegionCN470GetPhyParam( &getPhy );
+
+ linkAdrVerifyParams.Status = status;
+ linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+ linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+ linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+ linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+ linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+ linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+ linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+ linkAdrVerifyParams.NbChannels = CN470_MAX_NB_CHANNELS;
+ linkAdrVerifyParams.ChannelsMask = channelsMask;
+ linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+ linkAdrVerifyParams.MaxDatarate = CN470_TX_MAX_DATARATE;
+ linkAdrVerifyParams.Channels = Channels;
+ linkAdrVerifyParams.MinTxPower = CN470_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = CN470_MAX_TX_POWER;
+
+ // Verify the parameters and update, if necessary
+ status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+ // Update channelsMask if everything is correct
+ if( status == 0x07 )
+ {
+ // Copy Mask
+ RegionCommonChanMaskCopy( ChannelsMask, channelsMask, 6 );
+ }
+
+ // Update status variables
+ *drOut = linkAdrParams.Datarate;
+ *txPowOut = linkAdrParams.TxPower;
+ *nbRepOut = linkAdrParams.NbRep;
+ *nbBytesParsed = bytesProcessed;
+
+ return status;
+}
+
+uint8_t RegionCN470RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ uint8_t status = 0x07;
+ uint32_t freq = rxParamSetupReq->Frequency;
+
+ // Verify radio frequency
+ if( ( Radio.CheckRfFrequency( freq ) == false ) ||
+ ( freq < CN470_FIRST_RX1_CHANNEL ) ||
+ ( freq > CN470_LAST_RX1_CHANNEL ) ||
+ ( ( ( freq - ( uint32_t ) CN470_FIRST_RX1_CHANNEL ) % ( uint32_t ) CN470_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ // Verify datarate
+ if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify datarate offset
+ if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN470_MIN_RX1_DR_OFFSET, CN470_MAX_RX1_DR_OFFSET ) == false )
+ {
+ status &= 0xFB; // Rx1DrOffset range KO
+ }
+
+ return status;
+}
+
+uint8_t RegionCN470NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+ // Datarate and frequency KO
+ return 0;
+}
+
+int8_t RegionCN470TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+ return -1;
+}
+
+uint8_t RegionCN470DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ return 0;
+}
+
+int8_t RegionCN470AlternateDr( AlternateDrParams_t* alternateDr )
+{
+ int8_t datarate = 0;
+
+ if( ( alternateDr->NbTrials % 48 ) == 0 )
+ {
+ datarate = DR_0;
+ }
+ else if( ( alternateDr->NbTrials % 32 ) == 0 )
+ {
+ datarate = DR_1;
+ }
+ else if( ( alternateDr->NbTrials % 24 ) == 0 )
+ {
+ datarate = DR_2;
+ }
+ else if( ( alternateDr->NbTrials % 16 ) == 0 )
+ {
+ datarate = DR_3;
+ }
+ else if( ( alternateDr->NbTrials % 8 ) == 0 )
+ {
+ datarate = DR_4;
+ }
+ else
+ {
+ datarate = DR_5;
+ }
+ return datarate;
+}
+
+void RegionCN470CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = Channels;
+ calcBackOffParams.Bands = Bands;
+ calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+ calcBackOffParams.Joined = calcBackOff->Joined;
+ calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+ calcBackOffParams.Channel = calcBackOff->Channel;
+ calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+ calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+ RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionCN470NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTx = 0;
+ uint8_t enabledChannels[CN470_MAX_NB_CHANNELS] = { 0 };
+ TimerTime_t nextTxDelay = 0;
+
+ // Count 125kHz channels
+ if( RegionCommonCountChannels( ChannelsMask, 0, 6 ) == 0 )
+ { // Reactivate default channels
+ ChannelsMask[0] = 0xFFFF;
+ ChannelsMask[1] = 0xFFFF;
+ ChannelsMask[2] = 0xFFFF;
+ ChannelsMask[3] = 0xFFFF;
+ ChannelsMask[4] = 0xFFFF;
+ ChannelsMask[5] = 0xFFFF;
+ }
+
+ if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, CN470_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate,
+ ChannelsMask, Channels,
+ Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+ *time = 0;
+ return true;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return true;
+ }
+ // Datarate not supported by any channel
+ *time = 0;
+ return false;
+ }
+}
+
+LoRaMacStatus_t RegionCN470ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove )
+{
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+void RegionCN470SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+ Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionCN470ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ int8_t datarate = dr - drOffset;
+
+ if( datarate < 0 )
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
+
+void RegionCN470RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesCN470;
+ regionCommonRxBeaconSetup.ChannelPlanFrequency = ( 508.3e6 + ( BeaconChannel( 0, rxBeaconSetup->BeaconTime, rxBeaconSetup->BeaconInterval ) * 200e3 ) );
+ regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = ( 508.3e6 + ( rxBeaconSetup->BeaconTimingChannel * 200e3 ) );
+ regionCommonRxBeaconSetup.BeaconSize = CN470_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = CN470_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = CN470_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+ regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+ regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup, beaconChannelSet );
+
+ // Store downlink datarate
+ *outDr = CN470_BEACON_CHANNEL_DR;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionCN470.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,531 @@
+/*!
+ * \file RegionCN470.h
+ *
+ * \brief Region definition for CN470
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGIONCN470 Region CN470
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_CN470_H__
+#define __REGION_CN470_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define CN470_MAX_NB_CHANNELS 96
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define CN470_TX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define CN470_TX_MAX_DATARATE DR_5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define CN470_RX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define CN470_RX_MAX_DATARATE DR_5
+
+/*!
+ * Default datarate used by the node
+ */
+#define CN470_DEFAULT_DATARATE DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define CN470_MIN_RX1_DR_OFFSET 0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define CN470_MAX_RX1_DR_OFFSET 3
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define CN470_DEFAULT_RX1_DR_OFFSET 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define CN470_MIN_TX_POWER TX_POWER_7
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define CN470_MAX_TX_POWER TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define CN470_DEFAULT_TX_POWER TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define CN470_DEFAULT_MAX_EIRP 19.15f
+
+/*!
+ * Default antenna gain
+ */
+#define CN470_DEFAULT_ANTENNA_GAIN 2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define CN470_ADR_ACK_LIMIT 64
+
+/*!
+ * ADR Ack delay
+ */
+#define CN470_ADR_ACK_DELAY 32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define CN470_DUTY_CYCLE_ENABLED 0
+
+/*!
+ * Maximum RX window duration
+ */
+#define CN470_MAX_RX_WINDOW 3000
+
+/*!
+ * Receive delay 1
+ */
+#define CN470_RECEIVE_DELAY1 1000
+
+/*!
+ * Receive delay 2
+ */
+#define CN470_RECEIVE_DELAY2 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define CN470_JOIN_ACCEPT_DELAY1 5000
+
+/*!
+ * Join accept delay 2
+ */
+#define CN470_JOIN_ACCEPT_DELAY2 6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define CN470_MAX_FCNT_GAP 16384
+
+/*!
+ * Ack timeout
+ */
+#define CN470_ACKTIMEOUT 2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define CN470_ACK_TIMEOUT_RND 1000
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define CN470_RX_WND_2_FREQ 505300000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define CN470_RX_WND_2_DR DR_0
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define CN470_BEACON_INTERVAL 128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define CN470_BEACON_RESERVED 2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define CN470_BEACON_GUARD 3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define CN470_BEACON_WINDOW 122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define CN470_BEACON_WINDOW_SLOTS 4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define CN470_PING_SLOT_WINDOW 30
+
+/*!
+ * Beacon frequency
+ */
+#define CN470_BEACON_CHANNEL_FREQ 508300000
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define CN470_BEACON_SYMBOL_TO_DEFAULT 8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define CN470_BEACON_SYMBOL_TO_EXPANSION_MAX 400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define CN470_PING_SLOT_SYMBOL_TO_EXPANSION_MAX 40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define CN470_BEACON_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define CN470_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define CN470_MAX_BEACON_LESS_PERIOD 7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define CN470_BEACON_DELAY_BEACON_TIMING_ANS 30
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define CN470_BEACON_SIZE 19
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define CN470_BEACON_CHANNEL_DR DR_2
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define CN470_BEACON_CHANNEL_BW 0
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define CN470_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define CN470_BAND0 { 1, CN470_MAX_TX_POWER, 0, 0 } // 100.0 %
+
+/*!
+ * Defines the first channel for RX window 1 for CN470 band
+ */
+#define CN470_FIRST_RX1_CHANNEL ( (uint32_t) 500300000 )
+
+/*!
+ * Defines the last channel for RX window 1 for CN470 band
+ */
+#define CN470_LAST_RX1_CHANNEL ( (uint32_t) 509700000 )
+
+/*!
+ * Defines the step width of the channels for RX window 1
+ */
+#define CN470_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 200000 )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesCN470[] = { 12, 11, 10, 9, 8, 7 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsCN470[] = { 125000, 125000, 125000, 125000, 125000, 125000 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateCN470[] = { 51, 51, 51, 115, 222, 222 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterCN470[] = { 51, 51, 51, 115, 222, 222 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionCN470GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionCN470InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ * CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionCN470AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ * The receiver will turn on in a [-rxError : +rxError] ms
+ * interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionCN470ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionCN470TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN470LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN470RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN470NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ * Returns -1, if the functionality is not implemented. In this case, the end node
+ * shall not process the command.
+ */
+int8_t RegionCN470TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN470DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionCN470AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionCN470CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ * cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionCN470NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionCN470ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionCN470SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionCN470ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONCN470 */
+
+#endif // __REGION_CN470_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionCN779.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1152 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region CN779 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionCN779.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[CN779_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[CN779_MAX_NB_BANDS] =
+{
+ CN779_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+ uint8_t nextLowerDr = 0;
+
+ if( dr == minDr )
+ {
+ nextLowerDr = minDr;
+ }
+ else
+ {
+ nextLowerDr = dr - 1;
+ }
+ return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+ switch( BandwidthsCN779[drIndex] )
+ {
+ default:
+ case 125000:
+ return 0;
+ case 250000:
+ return 1;
+ case 500000:
+ return 2;
+ }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+ int8_t txPowerResult = txPower;
+
+ // Limit tx power to the band max
+ txPowerResult = MAX( txPower, maxBandTxPower );
+
+ return txPowerResult;
+}
+
+static bool VerifyTxFreq( uint32_t freq )
+{
+ // Check radio driver support
+ if( Radio.CheckRfFrequency( freq ) == false )
+ {
+ return false;
+ }
+
+ if( ( freq < 779500000 ) || ( freq > 786500000 ) )
+ {
+ return false;
+ }
+ return true;
+}
+
+static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTransmission = 0;
+
+ for( uint8_t i = 0, k = 0; i < CN779_MAX_NB_CHANNELS; i += 16, k++ )
+ {
+ for( uint8_t j = 0; j < 16; j++ )
+ {
+ if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+ {
+ if( channels[i + j].Frequency == 0 )
+ { // Check if the channel is enabled
+ continue;
+ }
+ if( joined == false )
+ {
+ if( ( CN779_JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+ {
+ continue;
+ }
+ }
+ if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+ channels[i + j].DrRange.Fields.Max ) == false )
+ { // Check if the current channel selection supports the given datarate
+ continue;
+ }
+ if( bands[channels[i + j].Band].TimeOff > 0 )
+ { // Check if the band is available for transmission
+ delayTransmission++;
+ continue;
+ }
+ enabledChannels[nbEnabledChannels++] = i + j;
+ }
+ }
+ }
+
+ *delayTx = delayTransmission;
+ return nbEnabledChannels;
+}
+
+PhyParam_t RegionCN779GetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ phyParam.Value = CN779_RX_MIN_DATARATE;
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ phyParam.Value = CN779_TX_MIN_DATARATE;
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = CN779_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, CN779_TX_MIN_DATARATE );
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = CN779_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ phyParam.Value = MaxPayloadOfDatarateCN779[getPhy->Datarate];
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterCN779[getPhy->Datarate];
+ break;
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ phyParam.Value = CN779_DUTY_CYCLE_ENABLED;
+ break;
+ }
+ case PHY_MAX_RX_WINDOW:
+ {
+ phyParam.Value = CN779_MAX_RX_WINDOW;
+ break;
+ }
+ case PHY_RECEIVE_DELAY1:
+ {
+ phyParam.Value = CN779_RECEIVE_DELAY1;
+ break;
+ }
+ case PHY_RECEIVE_DELAY2:
+ {
+ phyParam.Value = CN779_RECEIVE_DELAY2;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY1:
+ {
+ phyParam.Value = CN779_JOIN_ACCEPT_DELAY1;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY2:
+ {
+ phyParam.Value = CN779_JOIN_ACCEPT_DELAY2;
+ break;
+ }
+ case PHY_MAX_FCNT_GAP:
+ {
+ phyParam.Value = CN779_MAX_FCNT_GAP;
+ break;
+ }
+ case PHY_ACK_TIMEOUT:
+ {
+ phyParam.Value = ( CN779_ACKTIMEOUT + randr( -CN779_ACK_TIMEOUT_RND, CN779_ACK_TIMEOUT_RND ) );
+ break;
+ }
+ case PHY_DEF_DR1_OFFSET:
+ {
+ phyParam.Value = CN779_DEFAULT_RX1_DR_OFFSET;
+ break;
+ }
+ case PHY_DEF_RX2_FREQUENCY:
+ {
+ phyParam.Value = CN779_RX_WND_2_FREQ;
+ break;
+ }
+ case PHY_DEF_RX2_DR:
+ {
+ phyParam.Value = CN779_RX_WND_2_DR;
+ break;
+ }
+ case PHY_CHANNELS_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = CN779_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = Channels;
+ break;
+ }
+ case PHY_DEF_UPLINK_DWELL_TIME:
+ case PHY_DEF_DOWNLINK_DWELL_TIME:
+ {
+ phyParam.Value = 0;
+ break;
+ }
+ case PHY_DEF_MAX_EIRP:
+ {
+ phyParam.fValue = CN779_DEFAULT_MAX_EIRP;
+ break;
+ }
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = CN779_DEFAULT_ANTENNA_GAIN;
+ break;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ case PHY_DEF_NB_JOIN_TRIALS:
+ {
+ phyParam.Value = 48;
+ break;
+ }
+ case PHY_BEACON_INTERVAL:
+ {
+ phyParam.Value = CN779_BEACON_INTERVAL;
+ break;
+ }
+ case PHY_BEACON_RESERVED:
+ {
+ phyParam.Value = CN779_BEACON_RESERVED;
+ break;
+ }
+ case PHY_BEACON_GUARD:
+ {
+ phyParam.Value = CN779_BEACON_GUARD;
+ break;
+ }
+ case PHY_BEACON_WINDOW:
+ {
+ phyParam.Value = CN779_BEACON_WINDOW;
+ break;
+ }
+ case PHY_BEACON_WINDOW_SLOTS:
+ {
+ phyParam.Value = CN779_BEACON_WINDOW_SLOTS;
+ break;
+ }
+ case PHY_PING_SLOT_WINDOW:
+ {
+ phyParam.Value = CN779_PING_SLOT_WINDOW;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_DEFAULT:
+ {
+ phyParam.Value = CN779_BEACON_SYMBOL_TO_DEFAULT;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = CN779_BEACON_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = CN779_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = CN779_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = CN779_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_MAX_BEACON_LESS_PERIOD:
+ {
+ phyParam.Value = CN779_MAX_BEACON_LESS_PERIOD;
+ break;
+ }
+ case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+ {
+ phyParam.Value = CN779_BEACON_DELAY_BEACON_TIMING_ANS;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = CN779_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_PINGSLOT_CHANNEL_FREQ:
+ {
+ phyParam.Value = CN779_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_SIZE:
+ {
+ phyParam.Value = CN779_BEACON_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = CN779_BEACON_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionCN779SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionCN779InitDefaults( InitType_t type )
+{
+ switch( type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Channels
+ Channels[0] = ( ChannelParams_t ) CN779_LC1;
+ Channels[1] = ( ChannelParams_t ) CN779_LC2;
+ Channels[2] = ( ChannelParams_t ) CN779_LC3;
+
+ // Initialize the channels default mask
+ ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE:
+ {
+ // Restore channels default mask
+ ChannelsMask[0] |= ChannelsDefaultMask[0];
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionCN779Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+ }
+ case PHY_RX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN779_RX_MIN_DATARATE, CN779_RX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_POWER:
+ case PHY_TX_POWER:
+ {
+ // Remark: switched min and max!
+ return RegionCommonValueInRange( verify->TxPower, CN779_MAX_TX_POWER, CN779_MIN_TX_POWER );
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ return CN779_DUTY_CYCLE_ENABLED;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ {
+ if( verify->NbJoinTrials < 48 )
+ {
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+void RegionCN779ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ ChannelParams_t newChannel;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ // Setup default datarate range
+ newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+ // Size of the optional CF list
+ if( applyCFList->Size != 16 )
+ {
+ return;
+ }
+
+ // Last byte is RFU, don't take it into account
+ for( uint8_t i = 0, chanIdx = CN779_NUMB_DEFAULT_CHANNELS; chanIdx < CN779_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+ {
+ if( chanIdx < ( CN779_NUMB_CHANNELS_CF_LIST + CN779_NUMB_DEFAULT_CHANNELS ) )
+ {
+ // Channel frequency
+ newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
+ newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
+ newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
+ newChannel.Frequency *= 100;
+
+ // Initialize alternative frequency to 0
+ newChannel.Rx1Frequency = 0;
+ }
+ else
+ {
+ newChannel.Frequency = 0;
+ newChannel.DrRange.Value = 0;
+ newChannel.Rx1Frequency = 0;
+ }
+
+ if( newChannel.Frequency != 0 )
+ {
+ channelAdd.NewChannel = &newChannel;
+ channelAdd.ChannelId = chanIdx;
+
+ // Try to add all channels
+ RegionCN779ChannelAdd( &channelAdd );
+ }
+ else
+ {
+ channelRemove.ChannelId = chanIdx;
+
+ RegionCN779ChannelsRemove( &channelRemove );
+ }
+ }
+}
+
+bool RegionCN779ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegionCN779AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ bool adrAckReq = false;
+ int8_t datarate = adrNext->Datarate;
+ int8_t txPower = adrNext->TxPower;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Report back the adr ack counter
+ *adrAckCounter = adrNext->AdrAckCounter;
+
+ if( adrNext->AdrEnabled == true )
+ {
+ if( datarate == CN779_TX_MIN_DATARATE )
+ {
+ *adrAckCounter = 0;
+ adrAckReq = false;
+ }
+ else
+ {
+ if( adrNext->AdrAckCounter >= CN779_ADR_ACK_LIMIT )
+ {
+ adrAckReq = true;
+ txPower = CN779_MAX_TX_POWER;
+ }
+ else
+ {
+ adrAckReq = false;
+ }
+ if( adrNext->AdrAckCounter >= ( CN779_ADR_ACK_LIMIT + CN779_ADR_ACK_DELAY ) )
+ {
+ if( ( adrNext->AdrAckCounter % CN779_ADR_ACK_DELAY ) == 1 )
+ {
+ // Decrease the datarate
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.Datarate = datarate;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionCN779GetPhyParam( &getPhy );
+ datarate = phyParam.Value;
+
+ if( datarate == CN779_TX_MIN_DATARATE )
+ {
+ // We must set adrAckReq to false as soon as we reach the lowest datarate
+ adrAckReq = false;
+ if( adrNext->UpdateChanMask == true )
+ {
+ // Re-enable default channels
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *drOut = datarate;
+ *txPowOut = txPower;
+ return adrAckReq;
+}
+
+void RegionCN779ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+ double tSymbol = 0.0;
+
+ // Get the datarate, perform a boundary check
+ rxConfigParams->Datarate = MIN( datarate, CN779_RX_MAX_DATARATE );
+ rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+ if( rxConfigParams->Datarate == DR_7 )
+ { // FSK
+ tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesCN779[rxConfigParams->Datarate] );
+ }
+ else
+ { // LoRa
+ tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesCN779[rxConfigParams->Datarate], BandwidthsCN779[rxConfigParams->Datarate] );
+ }
+
+ RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionCN779RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+ RadioModems_t modem;
+ int8_t dr = rxConfig->Datarate;
+ uint8_t maxPayload = 0;
+ int8_t phyDr = 0;
+ uint32_t frequency = rxConfig->Frequency;
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->Window == 0 )
+ {
+ // Apply window 1 frequency
+ frequency = Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = Channels[rxConfig->Channel].Rx1Frequency;
+ }
+ }
+
+ // Read the physical datarate from the datarates table
+ phyDr = DataratesCN779[dr];
+
+ Radio.SetChannel( frequency );
+
+ // Radio configuration
+ if( dr == DR_7 )
+ {
+ modem = MODEM_FSK;
+ Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+ }
+
+ if( rxConfig->RepeaterSupport == true )
+ {
+ maxPayload = MaxPayloadOfDatarateRepeaterCN779[dr];
+ }
+ else
+ {
+ maxPayload = MaxPayloadOfDatarateCN779[dr];
+ }
+ Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+ *datarate = (uint8_t) dr;
+ return true;
+}
+
+bool RegionCN779TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ RadioModems_t modem;
+ int8_t phyDr = DataratesCN779[txConfig->Datarate];
+ int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+ uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+ int8_t phyTxPower = 0;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+ // Setup the radio frequency
+ Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+ if( txConfig->Datarate == DR_7 )
+ { // High Speed FSK channel
+ modem = MODEM_FSK;
+ Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+ }
+
+ // Setup maximum payload lenght of the radio driver
+ Radio.SetMaxPayloadLength( modem, txConfig->PktLen );
+ // Get the time-on-air of the next tx frame
+ *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen );
+
+ *txPower = txPowerLimited;
+ return true;
+}
+
+uint8_t RegionCN779LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+ uint8_t status = 0x07;
+ RegionCommonLinkAdrParams_t linkAdrParams;
+ uint8_t nextIndex = 0;
+ uint8_t bytesProcessed = 0;
+ uint16_t chMask = 0;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+ while( bytesProcessed < linkAdrReq->PayloadSize )
+ {
+ // Get ADR request parameters
+ nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+ if( nextIndex == 0 )
+ break; // break loop, since no more request has been found
+
+ // Update bytes processed
+ bytesProcessed += nextIndex;
+
+ // Revert status, as we only check the last ADR request for the channel mask KO
+ status = 0x07;
+
+ // Setup temporary channels mask
+ chMask = linkAdrParams.ChMask;
+
+ // Verify channels mask
+ if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
+ {
+ status &= 0xFE; // Channel mask KO
+ }
+ else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
+ ( linkAdrParams.ChMaskCtrl >= 7 ) )
+ {
+ // RFU
+ status &= 0xFE; // Channel mask KO
+ }
+ else
+ {
+ for( uint8_t i = 0; i < CN779_MAX_NB_CHANNELS; i++ )
+ {
+ if( linkAdrParams.ChMaskCtrl == 6 )
+ {
+ if( Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( Channels[i].Frequency == 0 ) )
+ {// Trying to enable an undefined channel
+ status &= 0xFE; // Channel mask KO
+ }
+ }
+ }
+ }
+ }
+
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+ phyParam = RegionCN779GetPhyParam( &getPhy );
+
+ linkAdrVerifyParams.Status = status;
+ linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+ linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+ linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+ linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+ linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+ linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+ linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+ linkAdrVerifyParams.NbChannels = CN779_MAX_NB_CHANNELS;
+ linkAdrVerifyParams.ChannelsMask = &chMask;
+ linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+ linkAdrVerifyParams.MaxDatarate = CN779_TX_MAX_DATARATE;
+ linkAdrVerifyParams.Channels = Channels;
+ linkAdrVerifyParams.MinTxPower = CN779_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = CN779_MAX_TX_POWER;
+
+ // Verify the parameters and update, if necessary
+ status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+ // Update channelsMask if everything is correct
+ if( status == 0x07 )
+ {
+ // Set the channels mask to a default value
+ memset( ChannelsMask, 0, sizeof( ChannelsMask ) );
+ // Update the channels mask
+ ChannelsMask[0] = chMask;
+ }
+
+ // Update status variables
+ *drOut = linkAdrParams.Datarate;
+ *txPowOut = linkAdrParams.TxPower;
+ *nbRepOut = linkAdrParams.NbRep;
+ *nbBytesParsed = bytesProcessed;
+
+ return status;
+}
+
+uint8_t RegionCN779RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ uint8_t status = 0x07;
+
+ // Verify radio frequency
+ if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ // Verify datarate
+ if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN779_RX_MIN_DATARATE, CN779_RX_MAX_DATARATE ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify datarate offset
+ if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN779_MIN_RX1_DR_OFFSET, CN779_MAX_RX1_DR_OFFSET ) == false )
+ {
+ status &= 0xFB; // Rx1DrOffset range KO
+ }
+
+ return status;
+}
+
+uint8_t RegionCN779NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+ uint8_t status = 0x03;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ if( newChannelReq->NewChannel->Frequency == 0 )
+ {
+ channelRemove.ChannelId = newChannelReq->ChannelId;
+
+ // Remove
+ if( RegionCN779ChannelsRemove( &channelRemove ) == false )
+ {
+ status &= 0xFC;
+ }
+ }
+ else
+ {
+ channelAdd.NewChannel = newChannelReq->NewChannel;
+ channelAdd.ChannelId = newChannelReq->ChannelId;
+
+ switch( RegionCN779ChannelAdd( &channelAdd ) )
+ {
+ case LORAMAC_STATUS_OK:
+ {
+ break;
+ }
+ case LORAMAC_STATUS_FREQUENCY_INVALID:
+ {
+ status &= 0xFE;
+ break;
+ }
+ case LORAMAC_STATUS_DATARATE_INVALID:
+ {
+ status &= 0xFD;
+ break;
+ }
+ case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
+ {
+ status &= 0xFC;
+ break;
+ }
+ default:
+ {
+ status &= 0xFC;
+ break;
+ }
+ }
+ }
+
+ return status;
+}
+
+int8_t RegionCN779TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+ return -1;
+}
+
+uint8_t RegionCN779DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ uint8_t status = 0x03;
+
+ // Verify if the frequency is supported
+ if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionCN779AlternateDr( AlternateDrParams_t* alternateDr )
+{
+ int8_t datarate = 0;
+
+ if( ( alternateDr->NbTrials % 48 ) == 0 )
+ {
+ datarate = DR_0;
+ }
+ else if( ( alternateDr->NbTrials % 32 ) == 0 )
+ {
+ datarate = DR_1;
+ }
+ else if( ( alternateDr->NbTrials % 24 ) == 0 )
+ {
+ datarate = DR_2;
+ }
+ else if( ( alternateDr->NbTrials % 16 ) == 0 )
+ {
+ datarate = DR_3;
+ }
+ else if( ( alternateDr->NbTrials % 8 ) == 0 )
+ {
+ datarate = DR_4;
+ }
+ else
+ {
+ datarate = DR_5;
+ }
+ return datarate;
+}
+
+void RegionCN779CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = Channels;
+ calcBackOffParams.Bands = Bands;
+ calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+ calcBackOffParams.Joined = calcBackOff->Joined;
+ calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+ calcBackOffParams.Channel = calcBackOff->Channel;
+ calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+ calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+ RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionCN779NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTx = 0;
+ uint8_t enabledChannels[CN779_MAX_NB_CHANNELS] = { 0 };
+ TimerTime_t nextTxDelay = 0;
+
+ if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+
+ if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, CN779_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ ChannelsMask, Channels,
+ Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+ *time = 0;
+ return true;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return true;
+ }
+ // Datarate not supported by any channel, restore defaults
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ *time = 0;
+ return false;
+ }
+}
+
+LoRaMacStatus_t RegionCN779ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ uint8_t band = 0;
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id >= CN779_MAX_NB_CHANNELS )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ // Validate the datarate range
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+ {
+ drInvalid = true;
+ }
+
+ // Default channels don't accept all values
+ if( id < CN779_NUMB_DEFAULT_CHANNELS )
+ {
+ // Validate the datarate range for min: must be DR_0
+ if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 )
+ {
+ drInvalid = true;
+ }
+ // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, CN779_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ // We are not allowed to change the frequency
+ if( channelAdd->NewChannel->Frequency != Channels[id].Frequency )
+ {
+ freqInvalid = true;
+ }
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false )
+ {
+ freqInvalid = true;
+ }
+ }
+
+ // Check status
+ if( ( drInvalid == true ) && ( freqInvalid == true ) )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+ if( drInvalid == true )
+ {
+ return LORAMAC_STATUS_DATARATE_INVALID;
+ }
+ if( freqInvalid == true )
+ {
+ return LORAMAC_STATUS_FREQUENCY_INVALID;
+ }
+
+ memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) );
+ Channels[id].Band = band;
+ ChannelsMask[0] |= ( 1 << id );
+ return LORAMAC_STATUS_OK;
+}
+
+bool RegionCN779ChannelsRemove( ChannelRemoveParams_t* channelRemove )
+{
+ uint8_t id = channelRemove->ChannelId;
+
+ if( id < CN779_NUMB_DEFAULT_CHANNELS )
+ {
+ return false;
+ }
+
+ // Remove the channel from the list of channels
+ Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( ChannelsMask, id, CN779_MAX_NB_CHANNELS );
+}
+
+void RegionCN779SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+ Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionCN779ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ int8_t datarate = dr - drOffset;
+
+ if( datarate < 0 )
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
+
+void RegionCN779RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesCN779;
+ regionCommonRxBeaconSetup.ChannelPlanFrequency = CN779_BEACON_CHANNEL_FREQ;
+ regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = CN779_BEACON_CHANNEL_FREQ;
+ regionCommonRxBeaconSetup.BeaconSize = CN779_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = CN779_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = CN779_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+ regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+ regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup, beaconChannelSet );
+
+ // Store downlink datarate
+ *outDr = CN779_BEACON_CHANNEL_DR;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionCN779.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,555 @@
+/*!
+ * \file RegionCN779.h
+ *
+ * \brief Region definition for CN779
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGIONCN779 Region CN779
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_CN779_H__
+#define __REGION_CN779_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define CN779_MAX_NB_CHANNELS 16
+
+/*!
+ * Number of default channels
+ */
+#define CN779_NUMB_DEFAULT_CHANNELS 3
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define CN779_NUMB_CHANNELS_CF_LIST 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define CN779_TX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define CN779_TX_MAX_DATARATE DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define CN779_RX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define CN779_RX_MAX_DATARATE DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define CN779_DEFAULT_DATARATE DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define CN779_MIN_RX1_DR_OFFSET 0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define CN779_MAX_RX1_DR_OFFSET 5
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define CN779_DEFAULT_RX1_DR_OFFSET 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define CN779_MIN_TX_POWER TX_POWER_5
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define CN779_MAX_TX_POWER TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define CN779_DEFAULT_TX_POWER TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define CN779_DEFAULT_MAX_EIRP 12.15f
+
+/*!
+ * Default antenna gain
+ */
+#define CN779_DEFAULT_ANTENNA_GAIN 2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define CN779_ADR_ACK_LIMIT 64
+
+/*!
+ * ADR Ack delay
+ */
+#define CN779_ADR_ACK_DELAY 32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define CN779_DUTY_CYCLE_ENABLED 1
+
+/*!
+ * Maximum RX window duration
+ */
+#define CN779_MAX_RX_WINDOW 3000
+
+/*!
+ * Receive delay 1
+ */
+#define CN779_RECEIVE_DELAY1 1000
+
+/*!
+ * Receive delay 2
+ */
+#define CN779_RECEIVE_DELAY2 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define CN779_JOIN_ACCEPT_DELAY1 5000
+
+/*!
+ * Join accept delay 2
+ */
+#define CN779_JOIN_ACCEPT_DELAY2 6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define CN779_MAX_FCNT_GAP 16384
+
+/*!
+ * Ack timeout
+ */
+#define CN779_ACKTIMEOUT 2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define CN779_ACK_TIMEOUT_RND 1000
+
+/*!
+ * Verification of default datarate
+ */
+#if ( CN779_DEFAULT_DATARATE > DR_5 )
+#error "A default DR higher than DR_5 may lead to connectivity loss."
+#endif
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define CN779_RX_WND_2_FREQ 786000000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define CN779_RX_WND_2_DR DR_0
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define CN779_BEACON_INTERVAL 128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define CN779_BEACON_RESERVED 2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define CN779_BEACON_GUARD 3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define CN779_BEACON_WINDOW 122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define CN779_BEACON_WINDOW_SLOTS 4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define CN779_PING_SLOT_WINDOW 30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define CN779_BEACON_SYMBOL_TO_DEFAULT 8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define CN779_BEACON_SYMBOL_TO_EXPANSION_MAX 400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define CN779_PING_SLOT_SYMBOL_TO_EXPANSION_MAX 40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define CN779_BEACON_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define CN779_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define CN779_MAX_BEACON_LESS_PERIOD 7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define CN779_BEACON_DELAY_BEACON_TIMING_ANS 30
+
+/*!
+ * Beacon frequency
+ */
+#define CN779_BEACON_CHANNEL_FREQ 785000000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define CN779_BEACON_SIZE 17
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define CN779_BEACON_CHANNEL_DR DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define CN779_BEACON_CHANNEL_BW 0
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define CN779_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define CN779_BAND0 { 100, CN779_MAX_TX_POWER, 0, 0 } // 1.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define CN779_LC1 { 779500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define CN779_LC2 { 779700000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 3
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define CN779_LC3 { 779900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define CN779_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesCN779[] = { 12, 11, 10, 9, 8, 7, 7, 50 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsCN779[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateCN779[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterCN779[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionCN779GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionCN779SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionCN779InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionCN779Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ * CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionCN779ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionCN779ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionCN779AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ * The receiver will turn on in a [-rxError : +rxError] ms
+ * interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionCN779ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionCN779RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionCN779TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN779LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN779RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN779NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ * Returns -1, if the functionality is not implemented. In this case, the end node
+ * shall not process the command.
+ */
+int8_t RegionCN779TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN779DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionCN779AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionCN779CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ * cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionCN779NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionCN779ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionCN779ChannelsRemove( ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionCN779SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionCN779ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONCN779 */
+
+#endif // __REGION_CN779_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionCommon.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,409 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC common region implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "radio.h"
+#include "timer.h"
+#include "utilities.h"
+#include "LoRaMac.h"
+#include "RegionCommon.h"
+
+
+
+#define BACKOFF_DC_1_HOUR 100
+#define BACKOFF_DC_10_HOURS 1000
+#define BACKOFF_DC_24_HOURS 10000
+
+
+
+static uint8_t CountChannels( uint16_t mask, uint8_t nbBits )
+{
+ uint8_t nbActiveBits = 0;
+
+ for( uint8_t j = 0; j < nbBits; j++ )
+ {
+ if( ( mask & ( 1 << j ) ) == ( 1 << j ) )
+ {
+ nbActiveBits++;
+ }
+ }
+ return nbActiveBits;
+}
+
+
+
+uint16_t RegionCommonGetJoinDc( TimerTime_t elapsedTime )
+{
+ uint16_t dutyCycle = 0;
+
+ if( elapsedTime < 3600000 )
+ {
+ dutyCycle = BACKOFF_DC_1_HOUR;
+ }
+ else if( elapsedTime < ( 3600000 + 36000000 ) )
+ {
+ dutyCycle = BACKOFF_DC_10_HOURS;
+ }
+ else
+ {
+ dutyCycle = BACKOFF_DC_24_HOURS;
+ }
+ return dutyCycle;
+}
+
+bool RegionCommonChanVerifyDr( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, int8_t minDr, int8_t maxDr, ChannelParams_t* channels )
+{
+ if( RegionCommonValueInRange( dr, minDr, maxDr ) == 0 )
+ {
+ return false;
+ }
+
+ for( uint8_t i = 0, k = 0; i < nbChannels; i += 16, k++ )
+ {
+ for( uint8_t j = 0; j < 16; j++ )
+ {
+ if( ( ( channelsMask[k] & ( 1 << j ) ) != 0 ) )
+ {// Check datarate validity for enabled channels
+ if( RegionCommonValueInRange( dr, ( channels[i + j].DrRange.Fields.Min & 0x0F ),
+ ( channels[i + j].DrRange.Fields.Max & 0x0F ) ) == 1 )
+ {
+ // At least 1 channel has been found we can return OK.
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+uint8_t RegionCommonValueInRange( int8_t value, int8_t min, int8_t max )
+{
+ if( ( value >= min ) && ( value <= max ) )
+ {
+ return 1;
+ }
+ return 0;
+}
+
+bool RegionCommonChanDisable( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels )
+{
+ uint8_t index = id / 16;
+
+ if( ( index > ( maxChannels / 16 ) ) || ( id >= maxChannels ) )
+ {
+ return false;
+ }
+
+ // Deactivate channel
+ channelsMask[index] &= ~( 1 << ( id % 16 ) );
+
+ return true;
+}
+
+uint8_t RegionCommonCountChannels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx )
+{
+ uint8_t nbChannels = 0;
+
+ if( channelsMask == NULL )
+ {
+ return 0;
+ }
+
+ for( uint8_t i = startIdx; i < stopIdx; i++ )
+ {
+ nbChannels += CountChannels( channelsMask[i], 16 );
+ }
+
+ return nbChannels;
+}
+
+void RegionCommonChanMaskCopy( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len )
+{
+ if( ( channelsMaskDest != NULL ) && ( channelsMaskSrc != NULL ) )
+ {
+ for( uint8_t i = 0; i < len; i++ )
+ {
+ channelsMaskDest[i] = channelsMaskSrc[i];
+ }
+ }
+}
+
+void RegionCommonSetBandTxDone( bool joined, Band_t* band, TimerTime_t lastTxDone )
+{
+ if( joined == true )
+ {
+ band->LastTxDoneTime = lastTxDone;
+ }
+ else
+ {
+ band->LastTxDoneTime = lastTxDone;
+ band->LastJoinTxDoneTime = lastTxDone;
+ }
+}
+
+TimerTime_t RegionCommonUpdateBandTimeOff( bool joined, bool dutyCycle, Band_t* bands, uint8_t nbBands )
+{
+ TimerTime_t nextTxDelay = ( TimerTime_t )( -1 );
+
+ // Update bands Time OFF
+ for( uint8_t i = 0; i < nbBands; i++ )
+ {
+ if( joined == false )
+ {
+ uint32_t txDoneTime = MAX( TimerGetElapsedTime( bands[i].LastJoinTxDoneTime ),
+ ( dutyCycle == true ) ? TimerGetElapsedTime( bands[i].LastTxDoneTime ) : 0 );
+
+ if( bands[i].TimeOff <= txDoneTime )
+ {
+ bands[i].TimeOff = 0;
+ }
+ if( bands[i].TimeOff != 0 )
+ {
+ nextTxDelay = MIN( bands[i].TimeOff - txDoneTime, nextTxDelay );
+ }
+ }
+ else
+ {
+ if( dutyCycle == true )
+ {
+ if( bands[i].TimeOff <= TimerGetElapsedTime( bands[i].LastTxDoneTime ) )
+ {
+ bands[i].TimeOff = 0;
+ }
+ if( bands[i].TimeOff != 0 )
+ {
+ nextTxDelay = MIN( bands[i].TimeOff - TimerGetElapsedTime( bands[i].LastTxDoneTime ),
+ nextTxDelay );
+ }
+ }
+ else
+ {
+ nextTxDelay = 0;
+ bands[i].TimeOff = 0;
+ }
+ }
+ }
+ return nextTxDelay;
+}
+
+uint8_t RegionCommonParseLinkAdrReq( uint8_t* payload, RegionCommonLinkAdrParams_t* linkAdrParams )
+{
+ uint8_t retIndex = 0;
+
+ if( payload[0] == SRV_MAC_LINK_ADR_REQ )
+ {
+ // Parse datarate and tx power
+ linkAdrParams->Datarate = payload[1];
+ linkAdrParams->TxPower = linkAdrParams->Datarate & 0x0F;
+ linkAdrParams->Datarate = ( linkAdrParams->Datarate >> 4 ) & 0x0F;
+ // Parse ChMask
+ linkAdrParams->ChMask = ( uint16_t )payload[2];
+ linkAdrParams->ChMask |= ( uint16_t )payload[3] << 8;
+ // Parse ChMaskCtrl and nbRep
+ linkAdrParams->NbRep = payload[4];
+ linkAdrParams->ChMaskCtrl = ( linkAdrParams->NbRep >> 4 ) & 0x07;
+ linkAdrParams->NbRep &= 0x0F;
+
+ // LinkAdrReq has 4 bytes length + 1 byte CMD
+ retIndex = 5;
+ }
+ return retIndex;
+}
+
+uint8_t RegionCommonLinkAdrReqVerifyParams( RegionCommonLinkAdrReqVerifyParams_t* verifyParams, int8_t* dr, int8_t* txPow, uint8_t* nbRep )
+{
+ uint8_t status = verifyParams->Status;
+ int8_t datarate = verifyParams->Datarate;
+ int8_t txPower = verifyParams->TxPower;
+ int8_t nbRepetitions = verifyParams->NbRep;
+
+ // Handle the case when ADR is off.
+ if( verifyParams->AdrEnabled == false )
+ {
+ // When ADR is off, we are allowed to change the channels mask and the NbRep,
+ // if the datarate and the TX power of the LinkAdrReq are set to 0x0F.
+ if( ( verifyParams->Datarate != 0x0F ) || ( verifyParams->TxPower != 0x0F ) )
+ {
+ status = 0;
+ nbRepetitions = verifyParams->CurrentNbRep;
+ }
+ // Get the current datarate and tx power
+ datarate = verifyParams->CurrentDatarate;
+ txPower = verifyParams->CurrentTxPower;
+ }
+
+ if( status != 0 )
+ {
+ // Verify datarate. The variable phyParam. Value contains the minimum allowed datarate.
+ if( RegionCommonChanVerifyDr( verifyParams->NbChannels, verifyParams->ChannelsMask, datarate,
+ verifyParams->MinDatarate, verifyParams->MaxDatarate, verifyParams->Channels ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify tx power
+ if( RegionCommonValueInRange( txPower, verifyParams->MaxTxPower, verifyParams->MinTxPower ) == 0 )
+ {
+ // Verify if the maximum TX power is exceeded
+ if( verifyParams->MaxTxPower > txPower )
+ { // Apply maximum TX power. Accept TX power.
+ txPower = verifyParams->MaxTxPower;
+ }
+ else
+ {
+ status &= 0xFB; // TxPower KO
+ }
+ }
+ }
+
+ // If the status is ok, verify the NbRep
+ if( status == 0x07 )
+ {
+ if( nbRepetitions == 0 )
+ { // Keep the current one
+ nbRepetitions = verifyParams->CurrentNbRep;
+ }
+ }
+
+ // Apply changes
+ *dr = datarate;
+ *txPow = txPower;
+ *nbRep = nbRepetitions;
+
+ return status;
+}
+
+double RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidth )
+{
+ return ( ( double )( 1 << phyDr ) / ( double )bandwidth ) * 1000;
+}
+
+double RegionCommonComputeSymbolTimeFsk( uint8_t phyDr )
+{
+ return ( 8.0 / ( double )phyDr ); // 1 symbol equals 1 byte
+}
+
+void RegionCommonComputeRxWindowParameters( double tSymbol, uint8_t minRxSymbols, uint32_t rxError, uint32_t wakeUpTime, uint32_t* windowTimeout, int32_t* windowOffset )
+{
+ *windowTimeout = MAX( ( uint32_t )ceil( ( ( 2 * minRxSymbols - 8 ) * tSymbol + 2 * rxError ) / tSymbol ), minRxSymbols ); // Computed number of symbols
+ *windowOffset = ( int32_t )ceil( ( 4.0 * tSymbol ) - ( ( *windowTimeout * tSymbol ) / 2.0 ) - wakeUpTime );
+}
+
+int8_t RegionCommonComputeTxPower( int8_t txPowerIndex, float maxEirp, float antennaGain )
+{
+ int8_t phyTxPower = 0;
+
+ phyTxPower = ( int8_t )floor( ( maxEirp - ( txPowerIndex * 2U ) ) - antennaGain );
+
+ return phyTxPower;
+}
+
+void RegionCommonCalcBackOff( RegionCommonCalcBackOffParams_t* calcBackOffParams )
+{
+ uint8_t bandIdx = calcBackOffParams->Channels[calcBackOffParams->Channel].Band;
+ uint16_t dutyCycle = calcBackOffParams->Bands[bandIdx].DCycle;
+ uint16_t joinDutyCycle = 0;
+
+ // Reset time-off to initial value.
+ calcBackOffParams->Bands[bandIdx].TimeOff = 0;
+
+ if( calcBackOffParams->Joined == false )
+ {
+ // Get the join duty cycle
+ joinDutyCycle = RegionCommonGetJoinDc( calcBackOffParams->ElapsedTime );
+ // Apply the most restricting duty cycle
+ dutyCycle = MAX( dutyCycle, joinDutyCycle );
+ // Reset the timeoff if the last frame was not a join request and when the duty cycle is not enabled
+ if( ( calcBackOffParams->DutyCycleEnabled == false ) && ( calcBackOffParams->LastTxIsJoinRequest == false ) )
+ {
+ // This is the case when the duty cycle is off and the last uplink frame was not a join.
+ // This could happen in case of a rejoin, e.g. in compliance test mode.
+ // In this special case we have to set the time off to 0, since the join duty cycle shall only
+ // be applied after the first join request.
+ calcBackOffParams->Bands[bandIdx].TimeOff = 0;
+ }
+ else
+ {
+ // Apply band time-off.
+ calcBackOffParams->Bands[bandIdx].TimeOff = calcBackOffParams->TxTimeOnAir * dutyCycle - calcBackOffParams->TxTimeOnAir;
+ }
+ }
+ else
+ {
+ if( calcBackOffParams->DutyCycleEnabled == true )
+ {
+ calcBackOffParams->Bands[bandIdx].TimeOff = calcBackOffParams->TxTimeOnAir * dutyCycle - calcBackOffParams->TxTimeOnAir;
+ }
+ else
+ {
+ calcBackOffParams->Bands[bandIdx].TimeOff = 0;
+ }
+ }
+}
+
+
+void RegionCommonRxBeaconSetup( RegionCommonRxBeaconSetupParams_t* rxBeaconSetupParams, bool *beaconChannelSet )
+{
+ bool rxContinuous = true;
+ uint32_t frequency = rxBeaconSetupParams->CustomFrequency;
+ uint8_t datarate;
+
+ // Set the radio into sleep mode
+ Radio.Sleep( );
+
+ // If no custom frequency is enabled, use the default floor plan frequency
+ if( rxBeaconSetupParams->CustomFrequencyEnabled == false )
+ {
+ // Restore floor plan
+ frequency = rxBeaconSetupParams->ChannelPlanFrequency;
+ }
+
+ // Set the frequency which was provided by BeaconTimingAns MAC command
+ if( rxBeaconSetupParams->BeaconChannelSet == true )
+ {
+ // Take the frequency of the next beacon
+ *beaconChannelSet = false;
+ frequency = rxBeaconSetupParams->BeaconTimingAnsFrequency;
+ }
+
+ // Setup frequency and payload length
+ Radio.SetChannel( frequency );
+ Radio.SetMaxPayloadLength( MODEM_LORA, rxBeaconSetupParams->BeaconSize );
+
+ // Check the RX continuous mode
+ if( rxBeaconSetupParams->RxTime != 0 )
+ {
+ rxContinuous = false;
+ }
+
+ // Get region specific datarate
+ datarate = rxBeaconSetupParams->Datarates[rxBeaconSetupParams->BeaconDatarate];
+
+ // Setup radio
+ Radio.SetRxConfig( MODEM_LORA, rxBeaconSetupParams->BeaconChannelBW, datarate,
+ 1, 0, 10, rxBeaconSetupParams->SymbolTimeout, true, rxBeaconSetupParams->BeaconSize, false, 0, 0, false, rxContinuous );
+
+ Radio.Rx( rxBeaconSetupParams->RxTime );
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionCommon.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,424 @@
+/*!
+ * \file RegionCommon.h
+ *
+ * \brief Region independent implementations which are common to all regions.
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGIONCOMMON Common region implementation
+ * Region independent implementations which are common to all regions.
+ * \{
+ */
+#ifndef __REGIONCOMMON_H__
+#define __REGIONCOMMON_H__
+
+typedef struct sRegionCommonLinkAdrParams
+{
+ /*!
+ * Number of repetitions.
+ */
+ uint8_t NbRep;
+ /*!
+ * Datarate.
+ */
+ int8_t Datarate;
+ /*!
+ * Tx power.
+ */
+ int8_t TxPower;
+ /*!
+ * Channels mask control field.
+ */
+ uint8_t ChMaskCtrl;
+ /*!
+ * Channels mask field.
+ */
+ uint16_t ChMask;
+}RegionCommonLinkAdrParams_t;
+
+typedef struct sRegionCommonLinkAdrReqVerifyParams
+{
+ /*!
+ * The current status of the AdrLinkRequest.
+ */
+ uint8_t Status;
+ /*!
+ * Set to true, if ADR is enabled.
+ */
+ bool AdrEnabled;
+ /*!
+ * The datarate the AdrLinkRequest wants to set.
+ */
+ int8_t Datarate;
+ /*!
+ * The TX power the AdrLinkRequest wants to set.
+ */
+ int8_t TxPower;
+ /*!
+ * The number of repetitions the AdrLinkRequest wants to set.
+ */
+ uint8_t NbRep;
+ /*!
+ * The current datarate the node is using.
+ */
+ int8_t CurrentDatarate;
+ /*!
+ * The current TX power the node is using.
+ */
+ int8_t CurrentTxPower;
+ /*!
+ * The current number of repetitions the node is using.
+ */
+ int8_t CurrentNbRep;
+ /*!
+ * The number of channels.
+ */
+ uint8_t NbChannels;
+ /*!
+ * Pointer to the first element of the channels mask.
+ */
+ uint16_t* ChannelsMask;
+ /*!
+ * The minimum possible datarate.
+ */
+ int8_t MinDatarate;
+ /*!
+ * The maximum possible datarate.
+ */
+ int8_t MaxDatarate;
+ /*!
+ * Pointer to the channels.
+ */
+ ChannelParams_t* Channels;
+ /*!
+ * The minimum possible TX power.
+ */
+ int8_t MinTxPower;
+ /*!
+ * The maximum possible TX power.
+ */
+ int8_t MaxTxPower;
+}RegionCommonLinkAdrReqVerifyParams_t;
+
+typedef struct sRegionCommonCalcBackOffParams
+{
+ /*!
+ * A pointer to region specific channels.
+ */
+ ChannelParams_t* Channels;
+ /*!
+ * A pointer to region specific bands.
+ */
+ Band_t* Bands;
+ /*!
+ * Set to true, if the last uplink was a join request.
+ */
+ bool LastTxIsJoinRequest;
+ /*!
+ * Set to true, if the node is joined.
+ */
+ bool Joined;
+ /*!
+ * Set to true, if the duty cycle is enabled.
+ */
+ bool DutyCycleEnabled;
+ /*!
+ * The current channel.
+ */
+ uint8_t Channel;
+ /*!
+ * The elapsed time since initialization.
+ */
+ TimerTime_t ElapsedTime;
+ /*!
+ * The time on air of the last Tx frame.
+ */
+ TimerTime_t TxTimeOnAir;
+}RegionCommonCalcBackOffParams_t;
+
+typedef struct sRegionCommonRxBeaconSetupParams
+{
+ /*!
+ * A pointer to the available datarates.
+ */
+ const uint8_t* Datarates;
+ /*!
+ * The default channel floor plan frequency.
+ */
+ uint32_t ChannelPlanFrequency;
+ /*!
+ * The frequency to use when a BeaconTimingAns was performed.
+ */
+ uint32_t BeaconTimingAnsFrequency;
+ /*!
+ * The size of the beacon frame.
+ */
+ uint8_t BeaconSize;
+ /*!
+ * The datarate of the beacon.
+ */
+ uint8_t BeaconDatarate;
+ /*!
+ * The channel bandwidth of the beacon.
+ */
+ uint8_t BeaconChannelBW;
+ /*!
+ * A custom frequency.
+ */
+ uint32_t CustomFrequency;
+ /*!
+ * Set to true, if a custom frequency shall be used.
+ */
+ bool CustomFrequencyEnabled;
+ /*!
+ * Set to true, if a BeaconTimingAns MAC command was performed.
+ */
+ bool BeaconChannelSet;
+ /*!
+ * The RX time.
+ */
+ uint32_t RxTime;
+ /*!
+ * The symbol timeout of the RX procedure.
+ */
+ uint16_t SymbolTimeout;
+}RegionCommonRxBeaconSetupParams_t;
+
+/*!
+ * \brief Calculates the join duty cycle.
+ * This is a generic function and valid for all regions.
+ *
+ * \param [IN] elapsedTime Elapsed time since the start of the device.
+ *
+ * \retval Duty cycle restriction.
+ */
+uint16_t RegionCommonGetJoinDc( TimerTime_t elapsedTime );
+
+/*!
+ * \brief Verifies, if a value is in a given range.
+ * This is a generic function and valid for all regions.
+ *
+ * \param [IN] value Value to verify, if it is in range.
+ *
+ * \param [IN] min Minimum possible value.
+ *
+ * \param [IN] max Maximum possible value.
+ *
+ * \retval Returns 1 if the value is in range, otherwise 0.
+ */
+uint8_t RegionCommonValueInRange( int8_t value, int8_t min, int8_t max );
+
+/*!
+ * \brief Verifies, if a datarate is available on an active channel.
+ * This is a generic function and valid for all regions.
+ *
+ * \param [IN] nbChannels Number of channels.
+ *
+ * \param [IN] channelsMask The channels mask of the region.
+ *
+ * \param [IN] dr The datarate to verify.
+ *
+ * \param [IN] minDr Minimum datarate.
+ *
+ * \param [IN] maxDr Maximum datarate.
+ *
+ * \param [IN] channels The channels of the region.
+ *
+ * \retval Returns true if the datarate is supported, false if not.
+ */
+bool RegionCommonChanVerifyDr( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr,
+ int8_t minDr, int8_t maxDr, ChannelParams_t* channels );
+
+/*!
+ * \brief Disables a channel in a given channels mask.
+ * This is a generic function and valid for all regions.
+ *
+ * \param [IN] channelsMask The channels mask of the region.
+ *
+ * \param [IN] id The id of the channels mask to disable.
+ *
+ * \param [IN] maxChannels Maximum number of channels.
+ *
+ * \retval Returns true if the channel could be disabled, false if not.
+ */
+bool RegionCommonChanDisable( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels );
+
+/*!
+ * \brief Counts the number of active channels in a given channels mask.
+ * This is a generic function and valid for all regions.
+ *
+ * \param [IN] channelsMask The channels mask of the region.
+ *
+ * \param [IN] startIdx Start index.
+ *
+ * \param [IN] stopIdx Stop index ( the channels of this index will not be counted ).
+ *
+ * \retval Returns the number of active channels.
+ */
+uint8_t RegionCommonCountChannels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx );
+
+/*!
+ * \brief Copy a channels mask.
+ * This is a generic function and valid for all regions.
+ *
+ * \param [IN] channelsMaskDest The destination channels mask.
+ *
+ * \param [IN] channelsMaskSrc The source channels mask.
+ *
+ * \param [IN] len The index length to copy.
+ */
+void RegionCommonChanMaskCopy( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len );
+
+/*!
+ * \brief Sets the last tx done property.
+ * This is a generic function and valid for all regions.
+ *
+ * \param [IN] joined Set to true, if the node has joined the network
+ *
+ * \param [IN] band The band to be updated.
+ *
+ * \param [IN] lastTxDone The time of the last TX done.
+ */
+void RegionCommonSetBandTxDone( bool joined, Band_t* band, TimerTime_t lastTxDone );
+
+/*!
+ * \brief Updates the time-offs of the bands.
+ * This is a generic function and valid for all regions.
+ *
+ * \param [IN] joined Set to true, if the node has joined the network
+ *
+ * \param [IN] dutyCycle Set to true, if the duty cycle is enabled.
+ *
+ * \param [IN] bands A pointer to the bands.
+ *
+ * \param [IN] nbBands The number of bands available.
+ *
+ * \retval Returns the time which must be waited to perform the next uplink.
+ */
+TimerTime_t RegionCommonUpdateBandTimeOff( bool joined, bool dutyCycle, Band_t* bands, uint8_t nbBands );
+
+/*!
+ * \brief Parses the parameter of an LinkAdrRequest.
+ * This is a generic function and valid for all regions.
+ *
+ * \param [IN] payload Pointer to the payload containing the MAC commands. The payload
+ * must contain the CMD identifier, following by the parameters.
+ *
+ * \param [OUT] parseLinkAdr The function fills the structure with the ADR parameters.
+ *
+ * \retval Returns the length of the ADR request, if a request was found. Otherwise, the
+ * function returns 0.
+ */
+uint8_t RegionCommonParseLinkAdrReq( uint8_t* payload, RegionCommonLinkAdrParams_t* parseLinkAdr );
+
+/*!
+ * \brief Verifies and updates the datarate, the TX power and the number of repetitions
+ * of a LinkAdrRequest. This depends on the configuration of ADR also.
+ *
+ * \param [IN] verifyParams Pointer to a structure containing input parameters.
+ *
+ * \param [OUT] dr The updated datarate.
+ *
+ * \param [OUT] txPow The updated TX power.
+ *
+ * \param [OUT] nbRep The updated number of repetitions.
+ *
+ * \retval Returns the status according to the LinkAdrRequest definition.
+ */
+uint8_t RegionCommonLinkAdrReqVerifyParams( RegionCommonLinkAdrReqVerifyParams_t* verifyParams, int8_t* dr, int8_t* txPow, uint8_t* nbRep );
+
+/*!
+ * \brief Computes the symbol time for LoRa modulation.
+ *
+ * \param [IN] phyDr Physical datarate to use.
+ *
+ * \param [IN] bandwidth Bandwidth to use.
+ *
+ * \retval Returns the symbol time.
+ */
+double RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidth );
+
+/*!
+ * \brief Computes the symbol time for FSK modulation.
+ *
+ * \param [IN] phyDr Physical datarate to use.
+ *
+ * \param [IN] bandwidth Bandwidth to use.
+ *
+ * \retval Returns the symbol time.
+ */
+double RegionCommonComputeSymbolTimeFsk( uint8_t phyDr );
+
+/*!
+ * \brief Computes the RX window timeout and the RX window offset.
+ *
+ * \param [IN] tSymbol Symbol timeout.
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ * The receiver will turn on in a [-rxError : +rxError] ms interval around RxOffset.
+ *
+ * \param [IN] wakeUpTime Wakeup time of the system.
+ *
+ * \param [OUT] windowTimeout RX window timeout.
+ *
+ * \param [OUT] windowOffset RX window time offset to be applied to the RX delay.
+ */
+void RegionCommonComputeRxWindowParameters( double tSymbol, uint8_t minRxSymbols, uint32_t rxError, uint32_t wakeUpTime, uint32_t* windowTimeout, int32_t* windowOffset );
+
+/*!
+ * \brief Computes the txPower, based on the max EIRP and the antenna gain.
+ *
+ * \param [IN] txPower TX power index.
+ *
+ * \param [IN] maxEirp Maximum EIRP.
+ *
+ * \param [IN] antennaGain Antenna gain.
+ *
+ * \retval Returns the physical TX power.
+ */
+int8_t RegionCommonComputeTxPower( int8_t txPowerIndex, float maxEirp, float antennaGain );
+
+/*!
+ * \brief Calculates the duty cycle for the current band.
+ *
+ * \param [IN] calcBackOffParams A pointer to the input parameters.
+ */
+void RegionCommonCalcBackOff( RegionCommonCalcBackOffParams_t* calcBackOffParams );
+
+/*!
+ * \brief Sets up the radio into RX beacon mode.
+ *
+ * \param [IN] rxBeaconSetupParams A pointer to the input parameters.
+ *
+ * \param [OUT] beaconChannelSet A pointer ot the beaconChannelSet variable.
+ */
+void RegionCommonRxBeaconSetup( RegionCommonRxBeaconSetupParams_t* rxBeaconSetupParams, bool *beaconChannelSet );
+
+/*! \} defgroup REGIONCOMMON */
+
+#endif // __REGIONCOMMON_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionEU433.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1152 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region EU433 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionEU433.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[EU433_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[EU433_MAX_NB_BANDS] =
+{
+ EU433_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+ uint8_t nextLowerDr = 0;
+
+ if( dr == minDr )
+ {
+ nextLowerDr = minDr;
+ }
+ else
+ {
+ nextLowerDr = dr - 1;
+ }
+ return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+ switch( BandwidthsEU433[drIndex] )
+ {
+ default:
+ case 125000:
+ return 0;
+ case 250000:
+ return 1;
+ case 500000:
+ return 2;
+ }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+ int8_t txPowerResult = txPower;
+
+ // Limit tx power to the band max
+ txPowerResult = MAX( txPower, maxBandTxPower );
+
+ return txPowerResult;
+}
+
+static bool VerifyTxFreq( uint32_t freq )
+{
+ // Check radio driver support
+ if( Radio.CheckRfFrequency( freq ) == false )
+ {
+ return false;
+ }
+
+ if( ( freq < 433175000 ) || ( freq > 434665000 ) )
+ {
+ return false;
+ }
+ return true;
+}
+
+static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTransmission = 0;
+
+ for( uint8_t i = 0, k = 0; i < EU433_MAX_NB_CHANNELS; i += 16, k++ )
+ {
+ for( uint8_t j = 0; j < 16; j++ )
+ {
+ if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+ {
+ if( channels[i + j].Frequency == 0 )
+ { // Check if the channel is enabled
+ continue;
+ }
+ if( joined == false )
+ {
+ if( ( EU433_JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+ {
+ continue;
+ }
+ }
+ if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+ channels[i + j].DrRange.Fields.Max ) == false )
+ { // Check if the current channel selection supports the given datarate
+ continue;
+ }
+ if( bands[channels[i + j].Band].TimeOff > 0 )
+ { // Check if the band is available for transmission
+ delayTransmission++;
+ continue;
+ }
+ enabledChannels[nbEnabledChannels++] = i + j;
+ }
+ }
+ }
+
+ *delayTx = delayTransmission;
+ return nbEnabledChannels;
+}
+
+PhyParam_t RegionEU433GetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ phyParam.Value = EU433_RX_MIN_DATARATE;
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ phyParam.Value = EU433_TX_MIN_DATARATE;
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = EU433_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, EU433_TX_MIN_DATARATE );
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = EU433_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ phyParam.Value = MaxPayloadOfDatarateEU433[getPhy->Datarate];
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterEU433[getPhy->Datarate];
+ break;
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ phyParam.Value = EU433_DUTY_CYCLE_ENABLED;
+ break;
+ }
+ case PHY_MAX_RX_WINDOW:
+ {
+ phyParam.Value = EU433_MAX_RX_WINDOW;
+ break;
+ }
+ case PHY_RECEIVE_DELAY1:
+ {
+ phyParam.Value = EU433_RECEIVE_DELAY1;
+ break;
+ }
+ case PHY_RECEIVE_DELAY2:
+ {
+ phyParam.Value = EU433_RECEIVE_DELAY2;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY1:
+ {
+ phyParam.Value = EU433_JOIN_ACCEPT_DELAY1;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY2:
+ {
+ phyParam.Value = EU433_JOIN_ACCEPT_DELAY2;
+ break;
+ }
+ case PHY_MAX_FCNT_GAP:
+ {
+ phyParam.Value = EU433_MAX_FCNT_GAP;
+ break;
+ }
+ case PHY_ACK_TIMEOUT:
+ {
+ phyParam.Value = ( EU433_ACKTIMEOUT + randr( -EU433_ACK_TIMEOUT_RND, EU433_ACK_TIMEOUT_RND ) );
+ break;
+ }
+ case PHY_DEF_DR1_OFFSET:
+ {
+ phyParam.Value = EU433_DEFAULT_RX1_DR_OFFSET;
+ break;
+ }
+ case PHY_DEF_RX2_FREQUENCY:
+ {
+ phyParam.Value = EU433_RX_WND_2_FREQ;
+ break;
+ }
+ case PHY_DEF_RX2_DR:
+ {
+ phyParam.Value = EU433_RX_WND_2_DR;
+ break;
+ }
+ case PHY_CHANNELS_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = EU433_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = Channels;
+ break;
+ }
+ case PHY_DEF_UPLINK_DWELL_TIME:
+ case PHY_DEF_DOWNLINK_DWELL_TIME:
+ {
+ phyParam.Value = 0;
+ break;
+ }
+ case PHY_DEF_MAX_EIRP:
+ {
+ phyParam.fValue = EU433_DEFAULT_MAX_EIRP;
+ break;
+ }
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = EU433_DEFAULT_ANTENNA_GAIN;
+ break;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ case PHY_DEF_NB_JOIN_TRIALS:
+ {
+ phyParam.Value = 48;
+ break;
+ }
+ case PHY_BEACON_INTERVAL:
+ {
+ phyParam.Value = EU433_BEACON_INTERVAL;
+ break;
+ }
+ case PHY_BEACON_RESERVED:
+ {
+ phyParam.Value = EU433_BEACON_RESERVED;
+ break;
+ }
+ case PHY_BEACON_GUARD:
+ {
+ phyParam.Value = EU433_BEACON_GUARD;
+ break;
+ }
+ case PHY_BEACON_WINDOW:
+ {
+ phyParam.Value = EU433_BEACON_WINDOW;
+ break;
+ }
+ case PHY_BEACON_WINDOW_SLOTS:
+ {
+ phyParam.Value = EU433_BEACON_WINDOW_SLOTS;
+ break;
+ }
+ case PHY_PING_SLOT_WINDOW:
+ {
+ phyParam.Value = EU433_PING_SLOT_WINDOW;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_DEFAULT:
+ {
+ phyParam.Value = EU433_BEACON_SYMBOL_TO_DEFAULT;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = EU433_BEACON_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = EU433_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = EU433_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = EU433_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_MAX_BEACON_LESS_PERIOD:
+ {
+ phyParam.Value = EU433_MAX_BEACON_LESS_PERIOD;
+ break;
+ }
+ case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+ {
+ phyParam.Value = EU433_BEACON_DELAY_BEACON_TIMING_ANS;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = EU433_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_PINGSLOT_CHANNEL_FREQ:
+ {
+ phyParam.Value = EU433_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_SIZE:
+ {
+ phyParam.Value = EU433_BEACON_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = EU433_BEACON_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionEU433SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionEU433InitDefaults( InitType_t type )
+{
+ switch( type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Channels
+ Channels[0] = ( ChannelParams_t ) EU433_LC1;
+ Channels[1] = ( ChannelParams_t ) EU433_LC2;
+ Channels[2] = ( ChannelParams_t ) EU433_LC3;
+
+ // Initialize the channels default mask
+ ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE:
+ {
+ // Restore channels default mask
+ ChannelsMask[0] |= ChannelsDefaultMask[0];
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionEU433Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+ }
+ case PHY_RX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU433_RX_MIN_DATARATE, EU433_RX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_POWER:
+ case PHY_TX_POWER:
+ {
+ // Remark: switched min and max!
+ return RegionCommonValueInRange( verify->TxPower, EU433_MAX_TX_POWER, EU433_MIN_TX_POWER );
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ return EU433_DUTY_CYCLE_ENABLED;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ {
+ if( verify->NbJoinTrials < 48 )
+ {
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+void RegionEU433ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ ChannelParams_t newChannel;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ // Setup default datarate range
+ newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+ // Size of the optional CF list
+ if( applyCFList->Size != 16 )
+ {
+ return;
+ }
+
+ // Last byte is RFU, don't take it into account
+ for( uint8_t i = 0, chanIdx = EU433_NUMB_DEFAULT_CHANNELS; chanIdx < EU433_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+ {
+ if( chanIdx < ( EU433_NUMB_CHANNELS_CF_LIST + EU433_NUMB_DEFAULT_CHANNELS ) )
+ {
+ // Channel frequency
+ newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
+ newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
+ newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
+ newChannel.Frequency *= 100;
+
+ // Initialize alternative frequency to 0
+ newChannel.Rx1Frequency = 0;
+ }
+ else
+ {
+ newChannel.Frequency = 0;
+ newChannel.DrRange.Value = 0;
+ newChannel.Rx1Frequency = 0;
+ }
+
+ if( newChannel.Frequency != 0 )
+ {
+ channelAdd.NewChannel = &newChannel;
+ channelAdd.ChannelId = chanIdx;
+
+ // Try to add all channels
+ RegionEU433ChannelAdd( &channelAdd );
+ }
+ else
+ {
+ channelRemove.ChannelId = chanIdx;
+
+ RegionEU433ChannelsRemove( &channelRemove );
+ }
+ }
+}
+
+bool RegionEU433ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegionEU433AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ bool adrAckReq = false;
+ int8_t datarate = adrNext->Datarate;
+ int8_t txPower = adrNext->TxPower;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Report back the adr ack counter
+ *adrAckCounter = adrNext->AdrAckCounter;
+
+ if( adrNext->AdrEnabled == true )
+ {
+ if( datarate == EU433_TX_MIN_DATARATE )
+ {
+ *adrAckCounter = 0;
+ adrAckReq = false;
+ }
+ else
+ {
+ if( adrNext->AdrAckCounter >= EU433_ADR_ACK_LIMIT )
+ {
+ adrAckReq = true;
+ txPower = EU433_MAX_TX_POWER;
+ }
+ else
+ {
+ adrAckReq = false;
+ }
+ if( adrNext->AdrAckCounter >= ( EU433_ADR_ACK_LIMIT + EU433_ADR_ACK_DELAY ) )
+ {
+ if( ( adrNext->AdrAckCounter % EU433_ADR_ACK_DELAY ) == 1 )
+ {
+ // Decrease the datarate
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.Datarate = datarate;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionEU433GetPhyParam( &getPhy );
+ datarate = phyParam.Value;
+
+ if( datarate == EU433_TX_MIN_DATARATE )
+ {
+ // We must set adrAckReq to false as soon as we reach the lowest datarate
+ adrAckReq = false;
+ if( adrNext->UpdateChanMask == true )
+ {
+ // Re-enable default channels
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *drOut = datarate;
+ *txPowOut = txPower;
+ return adrAckReq;
+}
+
+void RegionEU433ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+ double tSymbol = 0.0;
+
+ // Get the datarate, perform a boundary check
+ rxConfigParams->Datarate = MIN( datarate, EU433_RX_MAX_DATARATE );
+ rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+ if( rxConfigParams->Datarate == DR_7 )
+ { // FSK
+ tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesEU433[rxConfigParams->Datarate] );
+ }
+ else
+ { // LoRa
+ tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesEU433[rxConfigParams->Datarate], BandwidthsEU433[rxConfigParams->Datarate] );
+ }
+
+ RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionEU433RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+ RadioModems_t modem;
+ int8_t dr = rxConfig->Datarate;
+ uint8_t maxPayload = 0;
+ int8_t phyDr = 0;
+ uint32_t frequency = rxConfig->Frequency;
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->Window == 0 )
+ {
+ // Apply window 1 frequency
+ frequency = Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = Channels[rxConfig->Channel].Rx1Frequency;
+ }
+ }
+
+ // Read the physical datarate from the datarates table
+ phyDr = DataratesEU433[dr];
+
+ Radio.SetChannel( frequency );
+
+ // Radio configuration
+ if( dr == DR_7 )
+ {
+ modem = MODEM_FSK;
+ Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+ }
+
+ if( rxConfig->RepeaterSupport == true )
+ {
+ maxPayload = MaxPayloadOfDatarateRepeaterEU433[dr];
+ }
+ else
+ {
+ maxPayload = MaxPayloadOfDatarateEU433[dr];
+ }
+ Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+ *datarate = (uint8_t) dr;
+ return true;
+}
+
+bool RegionEU433TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ RadioModems_t modem;
+ int8_t phyDr = DataratesEU433[txConfig->Datarate];
+ int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+ uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+ int8_t phyTxPower = 0;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+ // Setup the radio frequency
+ Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+ if( txConfig->Datarate == DR_7 )
+ { // High Speed FSK channel
+ modem = MODEM_FSK;
+ Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+ }
+
+ // Setup maximum payload lenght of the radio driver
+ Radio.SetMaxPayloadLength( modem, txConfig->PktLen );
+ // Get the time-on-air of the next tx frame
+ *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen );
+
+ *txPower = txPowerLimited;
+ return true;
+}
+
+uint8_t RegionEU433LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+ uint8_t status = 0x07;
+ RegionCommonLinkAdrParams_t linkAdrParams;
+ uint8_t nextIndex = 0;
+ uint8_t bytesProcessed = 0;
+ uint16_t chMask = 0;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+ while( bytesProcessed < linkAdrReq->PayloadSize )
+ {
+ // Get ADR request parameters
+ nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+ if( nextIndex == 0 )
+ break; // break loop, since no more request has been found
+
+ // Update bytes processed
+ bytesProcessed += nextIndex;
+
+ // Revert status, as we only check the last ADR request for the channel mask KO
+ status = 0x07;
+
+ // Setup temporary channels mask
+ chMask = linkAdrParams.ChMask;
+
+ // Verify channels mask
+ if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
+ {
+ status &= 0xFE; // Channel mask KO
+ }
+ else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
+ ( linkAdrParams.ChMaskCtrl >= 7 ) )
+ {
+ // RFU
+ status &= 0xFE; // Channel mask KO
+ }
+ else
+ {
+ for( uint8_t i = 0; i < EU433_MAX_NB_CHANNELS; i++ )
+ {
+ if( linkAdrParams.ChMaskCtrl == 6 )
+ {
+ if( Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( Channels[i].Frequency == 0 ) )
+ {// Trying to enable an undefined channel
+ status &= 0xFE; // Channel mask KO
+ }
+ }
+ }
+ }
+ }
+
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+ phyParam = RegionEU433GetPhyParam( &getPhy );
+
+ linkAdrVerifyParams.Status = status;
+ linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+ linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+ linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+ linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+ linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+ linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+ linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+ linkAdrVerifyParams.NbChannels = EU433_MAX_NB_CHANNELS;
+ linkAdrVerifyParams.ChannelsMask = &chMask;
+ linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+ linkAdrVerifyParams.MaxDatarate = EU433_TX_MAX_DATARATE;
+ linkAdrVerifyParams.Channels = Channels;
+ linkAdrVerifyParams.MinTxPower = EU433_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = EU433_MAX_TX_POWER;
+
+ // Verify the parameters and update, if necessary
+ status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+ // Update channelsMask if everything is correct
+ if( status == 0x07 )
+ {
+ // Set the channels mask to a default value
+ memset( ChannelsMask, 0, sizeof( ChannelsMask ) );
+ // Update the channels mask
+ ChannelsMask[0] = chMask;
+ }
+
+ // Update status variables
+ *drOut = linkAdrParams.Datarate;
+ *txPowOut = linkAdrParams.TxPower;
+ *nbRepOut = linkAdrParams.NbRep;
+ *nbBytesParsed = bytesProcessed;
+
+ return status;
+}
+
+uint8_t RegionEU433RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ uint8_t status = 0x07;
+
+ // Verify radio frequency
+ if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ // Verify datarate
+ if( RegionCommonValueInRange( rxParamSetupReq->Datarate, EU433_RX_MIN_DATARATE, EU433_RX_MAX_DATARATE ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify datarate offset
+ if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, EU433_MIN_RX1_DR_OFFSET, EU433_MAX_RX1_DR_OFFSET ) == false )
+ {
+ status &= 0xFB; // Rx1DrOffset range KO
+ }
+
+ return status;
+}
+
+uint8_t RegionEU433NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+ uint8_t status = 0x03;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ if( newChannelReq->NewChannel->Frequency == 0 )
+ {
+ channelRemove.ChannelId = newChannelReq->ChannelId;
+
+ // Remove
+ if( RegionEU433ChannelsRemove( &channelRemove ) == false )
+ {
+ status &= 0xFC;
+ }
+ }
+ else
+ {
+ channelAdd.NewChannel = newChannelReq->NewChannel;
+ channelAdd.ChannelId = newChannelReq->ChannelId;
+
+ switch( RegionEU433ChannelAdd( &channelAdd ) )
+ {
+ case LORAMAC_STATUS_OK:
+ {
+ break;
+ }
+ case LORAMAC_STATUS_FREQUENCY_INVALID:
+ {
+ status &= 0xFE;
+ break;
+ }
+ case LORAMAC_STATUS_DATARATE_INVALID:
+ {
+ status &= 0xFD;
+ break;
+ }
+ case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
+ {
+ status &= 0xFC;
+ break;
+ }
+ default:
+ {
+ status &= 0xFC;
+ break;
+ }
+ }
+ }
+
+ return status;
+}
+
+int8_t RegionEU433TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+ return -1;
+}
+
+uint8_t RegionEU433DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ uint8_t status = 0x03;
+
+ // Verify if the frequency is supported
+ if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionEU433AlternateDr( AlternateDrParams_t* alternateDr )
+{
+ int8_t datarate = 0;
+
+ if( ( alternateDr->NbTrials % 48 ) == 0 )
+ {
+ datarate = DR_0;
+ }
+ else if( ( alternateDr->NbTrials % 32 ) == 0 )
+ {
+ datarate = DR_1;
+ }
+ else if( ( alternateDr->NbTrials % 24 ) == 0 )
+ {
+ datarate = DR_2;
+ }
+ else if( ( alternateDr->NbTrials % 16 ) == 0 )
+ {
+ datarate = DR_3;
+ }
+ else if( ( alternateDr->NbTrials % 8 ) == 0 )
+ {
+ datarate = DR_4;
+ }
+ else
+ {
+ datarate = DR_5;
+ }
+ return datarate;
+}
+
+void RegionEU433CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = Channels;
+ calcBackOffParams.Bands = Bands;
+ calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+ calcBackOffParams.Joined = calcBackOff->Joined;
+ calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+ calcBackOffParams.Channel = calcBackOff->Channel;
+ calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+ calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+ RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionEU433NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTx = 0;
+ uint8_t enabledChannels[EU433_MAX_NB_CHANNELS] = { 0 };
+ TimerTime_t nextTxDelay = 0;
+
+ if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+
+ if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, EU433_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ ChannelsMask, Channels,
+ Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+ *time = 0;
+ return true;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return true;
+ }
+ // Datarate not supported by any channel, restore defaults
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ *time = 0;
+ return false;
+ }
+}
+
+LoRaMacStatus_t RegionEU433ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ uint8_t band = 0;
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id >= EU433_MAX_NB_CHANNELS )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ // Validate the datarate range
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+ {
+ drInvalid = true;
+ }
+
+ // Default channels don't accept all values
+ if( id < EU433_NUMB_DEFAULT_CHANNELS )
+ {
+ // Validate the datarate range for min: must be DR_0
+ if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 )
+ {
+ drInvalid = true;
+ }
+ // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, EU433_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ // We are not allowed to change the frequency
+ if( channelAdd->NewChannel->Frequency != Channels[id].Frequency )
+ {
+ freqInvalid = true;
+ }
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false )
+ {
+ freqInvalid = true;
+ }
+ }
+
+ // Check status
+ if( ( drInvalid == true ) && ( freqInvalid == true ) )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+ if( drInvalid == true )
+ {
+ return LORAMAC_STATUS_DATARATE_INVALID;
+ }
+ if( freqInvalid == true )
+ {
+ return LORAMAC_STATUS_FREQUENCY_INVALID;
+ }
+
+ memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) );
+ Channels[id].Band = band;
+ ChannelsMask[0] |= ( 1 << id );
+ return LORAMAC_STATUS_OK;
+}
+
+bool RegionEU433ChannelsRemove( ChannelRemoveParams_t* channelRemove )
+{
+ uint8_t id = channelRemove->ChannelId;
+
+ if( id < EU433_NUMB_DEFAULT_CHANNELS )
+ {
+ return false;
+ }
+
+ // Remove the channel from the list of channels
+ Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( ChannelsMask, id, EU433_MAX_NB_CHANNELS );
+}
+
+void RegionEU433SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+ Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionEU433ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ int8_t datarate = dr - drOffset;
+
+ if( datarate < 0 )
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
+
+void RegionEU433RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesEU433;
+ regionCommonRxBeaconSetup.ChannelPlanFrequency = EU433_BEACON_CHANNEL_FREQ;
+ regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = EU433_BEACON_CHANNEL_FREQ;
+ regionCommonRxBeaconSetup.BeaconSize = EU433_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = EU433_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = EU433_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+ regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+ regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup, beaconChannelSet );
+
+ // Store downlink datarate
+ *outDr = EU433_BEACON_CHANNEL_DR;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionEU433.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,556 @@
+/*!
+ * \file RegionEU433.h
+ *
+ * \brief Region definition for EU433
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGIONEU433 Region EU433
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_EU433_H__
+#define __REGION_EU433_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define EU433_MAX_NB_CHANNELS 16
+
+/*!
+ * Number of default channels
+ */
+#define EU433_NUMB_DEFAULT_CHANNELS 3
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define EU433_NUMB_CHANNELS_CF_LIST 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define EU433_TX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define EU433_TX_MAX_DATARATE DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define EU433_RX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define EU433_RX_MAX_DATARATE DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define EU433_DEFAULT_DATARATE DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define EU433_MIN_RX1_DR_OFFSET 0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define EU433_MAX_RX1_DR_OFFSET 5
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define EU433_DEFAULT_RX1_DR_OFFSET 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define EU433_MIN_TX_POWER TX_POWER_5
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define EU433_MAX_TX_POWER TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define EU433_DEFAULT_TX_POWER TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define EU433_DEFAULT_MAX_EIRP 12.15f
+
+/*!
+ * Default antenna gain
+ */
+#define EU433_DEFAULT_ANTENNA_GAIN 2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define EU433_ADR_ACK_LIMIT 64
+
+/*!
+ * ADR Ack delay
+ */
+#define EU433_ADR_ACK_DELAY 32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define EU433_DUTY_CYCLE_ENABLED 1
+
+/*!
+ * Maximum RX window duration
+ */
+#define EU433_MAX_RX_WINDOW 3000
+
+/*!
+ * Receive delay 1
+ */
+#define EU433_RECEIVE_DELAY1 1000
+
+/*!
+ * Receive delay 2
+ */
+#define EU433_RECEIVE_DELAY2 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define EU433_JOIN_ACCEPT_DELAY1 5000
+
+/*!
+ * Join accept delay 2
+ */
+#define EU433_JOIN_ACCEPT_DELAY2 6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define EU433_MAX_FCNT_GAP 16384
+
+/*!
+ * Ack timeout
+ */
+#define EU433_ACKTIMEOUT 2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define EU433_ACK_TIMEOUT_RND 1000
+
+/*!
+ * Verification of default datarate
+ */
+#if ( EU433_DEFAULT_DATARATE > DR_5 )
+#error "A default DR higher than DR_5 may lead to connectivity loss."
+#endif
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define EU433_RX_WND_2_FREQ 434665000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define EU433_RX_WND_2_DR DR_0
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define EU433_MAX_NB_BANDS 1
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define EU433_BEACON_INTERVAL 128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define EU433_BEACON_RESERVED 2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define EU433_BEACON_GUARD 3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define EU433_BEACON_WINDOW 122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define EU433_BEACON_WINDOW_SLOTS 4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define EU433_PING_SLOT_WINDOW 30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define EU433_BEACON_SYMBOL_TO_DEFAULT 8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define EU433_BEACON_SYMBOL_TO_EXPANSION_MAX 400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define EU433_PING_SLOT_SYMBOL_TO_EXPANSION_MAX 40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define EU433_BEACON_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define EU433_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define EU433_MAX_BEACON_LESS_PERIOD 7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define EU433_BEACON_DELAY_BEACON_TIMING_ANS 30
+
+/*!
+ * Beacon frequency
+ */
+#define EU433_BEACON_CHANNEL_FREQ 434665000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define EU433_BEACON_SIZE 17
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define EU433_BEACON_CHANNEL_DR DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define EU433_BEACON_CHANNEL_BW 0
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define EU433_BAND0 { 100, EU433_MAX_TX_POWER, 0, 0 } // 1.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define EU433_LC1 { 433175000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define EU433_LC2 { 433375000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 3
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define EU433_LC3 { 433575000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define EU433_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesEU433[] = { 12, 11, 10, 9, 8, 7, 7, 50 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsEU433[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateEU433[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterEU433[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionEU433GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionEU433SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionEU433InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionEU433Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ * CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionEU433ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionEU433ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionEU433AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ * The receiver will turn on in a [-rxError : +rxError] ms
+ * interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionEU433ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionEU433RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionEU433TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU433LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU433RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU433NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ * Returns -1, if the functionality is not implemented. In this case, the end node
+ * shall not process the command.
+ */
+int8_t RegionEU433TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU433DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionEU433AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionEU433CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ * cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionEU433NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionEU433ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionEU433ChannelsRemove( ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionEU433SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionEU433ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONEU433 */
+
+#endif // __REGION_EU433_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionEU868.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1183 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region EU868 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionEU868.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[EU868_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[EU868_MAX_NB_BANDS] =
+{
+ EU868_BAND0,
+ EU868_BAND1,
+ EU868_BAND2,
+ EU868_BAND3,
+ EU868_BAND4,
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+ uint8_t nextLowerDr = 0;
+
+ if( dr == minDr )
+ {
+ nextLowerDr = minDr;
+ }
+ else
+ {
+ nextLowerDr = dr - 1;
+ }
+ return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+ switch( BandwidthsEU868[drIndex] )
+ {
+ default:
+ case 125000:
+ return 0;
+ case 250000:
+ return 1;
+ case 500000:
+ return 2;
+ }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+ int8_t txPowerResult = txPower;
+
+ // Limit tx power to the band max
+ txPowerResult = MAX( txPower, maxBandTxPower );
+
+ return txPowerResult;
+}
+
+static bool VerifyTxFreq( uint32_t freq, uint8_t *band )
+{
+ // Check radio driver support
+ if( Radio.CheckRfFrequency( freq ) == false )
+ {
+ return false;
+ }
+
+ // Check frequency bands
+ if( ( freq >= 863000000 ) && ( freq < 865000000 ) )
+ {
+ *band = 2;
+ }
+ else if( ( freq >= 865000000 ) && ( freq <= 868000000 ) )
+ {
+ *band = 0;
+ }
+ else if( ( freq > 868000000 ) && ( freq <= 868600000 ) )
+ {
+ *band = 1;
+ }
+ else if( ( freq >= 868700000 ) && ( freq <= 869200000 ) )
+ {
+ *band = 2;
+ }
+ else if( ( freq >= 869400000 ) && ( freq <= 869650000 ) )
+ {
+ *band = 3;
+ }
+ else if( ( freq >= 869700000 ) && ( freq <= 870000000 ) )
+ {
+ *band = 4;
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+}
+
+static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTransmission = 0;
+
+ for( uint8_t i = 0, k = 0; i < EU868_MAX_NB_CHANNELS; i += 16, k++ )
+ {
+ for( uint8_t j = 0; j < 16; j++ )
+ {
+ if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+ {
+ if( channels[i + j].Frequency == 0 )
+ { // Check if the channel is enabled
+ continue;
+ }
+ if( joined == false )
+ {
+ if( ( EU868_JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+ {
+ continue;
+ }
+ }
+ if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+ channels[i + j].DrRange.Fields.Max ) == false )
+ { // Check if the current channel selection supports the given datarate
+ continue;
+ }
+ if( bands[channels[i + j].Band].TimeOff > 0 )
+ { // Check if the band is available for transmission
+ delayTransmission++;
+ continue;
+ }
+ enabledChannels[nbEnabledChannels++] = i + j;
+ }
+ }
+ }
+
+ *delayTx = delayTransmission;
+ return nbEnabledChannels;
+}
+
+PhyParam_t RegionEU868GetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ phyParam.Value = EU868_RX_MIN_DATARATE;
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ phyParam.Value = EU868_TX_MIN_DATARATE;
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = EU868_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, EU868_TX_MIN_DATARATE );
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = EU868_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ phyParam.Value = MaxPayloadOfDatarateEU868[getPhy->Datarate];
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterEU868[getPhy->Datarate];
+ break;
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ phyParam.Value = EU868_DUTY_CYCLE_ENABLED;
+ break;
+ }
+ case PHY_MAX_RX_WINDOW:
+ {
+ phyParam.Value = EU868_MAX_RX_WINDOW;
+ break;
+ }
+ case PHY_RECEIVE_DELAY1:
+ {
+ phyParam.Value = EU868_RECEIVE_DELAY1;
+ break;
+ }
+ case PHY_RECEIVE_DELAY2:
+ {
+ phyParam.Value = EU868_RECEIVE_DELAY2;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY1:
+ {
+ phyParam.Value = EU868_JOIN_ACCEPT_DELAY1;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY2:
+ {
+ phyParam.Value = EU868_JOIN_ACCEPT_DELAY2;
+ break;
+ }
+ case PHY_MAX_FCNT_GAP:
+ {
+ phyParam.Value = EU868_MAX_FCNT_GAP;
+ break;
+ }
+ case PHY_ACK_TIMEOUT:
+ {
+ phyParam.Value = ( EU868_ACKTIMEOUT + randr( -EU868_ACK_TIMEOUT_RND, EU868_ACK_TIMEOUT_RND ) );
+ break;
+ }
+ case PHY_DEF_DR1_OFFSET:
+ {
+ phyParam.Value = EU868_DEFAULT_RX1_DR_OFFSET;
+ break;
+ }
+ case PHY_DEF_RX2_FREQUENCY:
+ {
+ phyParam.Value = EU868_RX_WND_2_FREQ;
+ break;
+ }
+ case PHY_DEF_RX2_DR:
+ {
+ phyParam.Value = EU868_RX_WND_2_DR;
+ break;
+ }
+ case PHY_CHANNELS_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = EU868_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = Channels;
+ break;
+ }
+ case PHY_DEF_UPLINK_DWELL_TIME:
+ case PHY_DEF_DOWNLINK_DWELL_TIME:
+ {
+ phyParam.Value = 0;
+ break;
+ }
+ case PHY_DEF_MAX_EIRP:
+ {
+ phyParam.fValue = EU868_DEFAULT_MAX_EIRP;
+ break;
+ }
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = EU868_DEFAULT_ANTENNA_GAIN;
+ break;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ case PHY_DEF_NB_JOIN_TRIALS:
+ {
+ phyParam.Value = 48;
+ break;
+ }
+ case PHY_BEACON_INTERVAL:
+ {
+ phyParam.Value = EU868_BEACON_INTERVAL;
+ break;
+ }
+ case PHY_BEACON_RESERVED:
+ {
+ phyParam.Value = EU868_BEACON_RESERVED;
+ break;
+ }
+ case PHY_BEACON_GUARD:
+ {
+ phyParam.Value = EU868_BEACON_GUARD;
+ break;
+ }
+ case PHY_BEACON_WINDOW:
+ {
+ phyParam.Value = EU868_BEACON_WINDOW;
+ break;
+ }
+ case PHY_BEACON_WINDOW_SLOTS:
+ {
+ phyParam.Value = EU868_BEACON_WINDOW_SLOTS;
+ break;
+ }
+ case PHY_PING_SLOT_WINDOW:
+ {
+ phyParam.Value = EU868_PING_SLOT_WINDOW;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_DEFAULT:
+ {
+ phyParam.Value = EU868_BEACON_SYMBOL_TO_DEFAULT;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = EU868_BEACON_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = EU868_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = EU868_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = EU868_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_MAX_BEACON_LESS_PERIOD:
+ {
+ phyParam.Value = EU868_MAX_BEACON_LESS_PERIOD;
+ break;
+ }
+ case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+ {
+ phyParam.Value = EU868_BEACON_DELAY_BEACON_TIMING_ANS;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = EU868_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_PINGSLOT_CHANNEL_FREQ:
+ {
+ phyParam.Value = EU868_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_SIZE:
+ {
+ phyParam.Value = EU868_BEACON_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = EU868_BEACON_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionEU868SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionEU868InitDefaults( InitType_t type )
+{
+ switch( type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Channels
+ Channels[0] = ( ChannelParams_t ) EU868_LC1;
+ Channels[1] = ( ChannelParams_t ) EU868_LC2;
+ Channels[2] = ( ChannelParams_t ) EU868_LC3;
+
+ // Initialize the channels default mask
+ ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE:
+ {
+ // Restore channels default mask
+ ChannelsMask[0] |= ChannelsDefaultMask[0];
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionEU868Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+ }
+ case PHY_RX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU868_RX_MIN_DATARATE, EU868_RX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_POWER:
+ case PHY_TX_POWER:
+ {
+ // Remark: switched min and max!
+ return RegionCommonValueInRange( verify->TxPower, EU868_MAX_TX_POWER, EU868_MIN_TX_POWER );
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ return EU868_DUTY_CYCLE_ENABLED;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ {
+ if( verify->NbJoinTrials < 48 )
+ {
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+void RegionEU868ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ ChannelParams_t newChannel;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ // Setup default datarate range
+ newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+ // Size of the optional CF list
+ if( applyCFList->Size != 16 )
+ {
+ return;
+ }
+
+ // Last byte is RFU, don't take it into account
+ for( uint8_t i = 0, chanIdx = EU868_NUMB_DEFAULT_CHANNELS; chanIdx < EU868_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+ {
+ if( chanIdx < ( EU868_NUMB_CHANNELS_CF_LIST + EU868_NUMB_DEFAULT_CHANNELS ) )
+ {
+ // Channel frequency
+ newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
+ newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
+ newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
+ newChannel.Frequency *= 100;
+
+ // Initialize alternative frequency to 0
+ newChannel.Rx1Frequency = 0;
+ }
+ else
+ {
+ newChannel.Frequency = 0;
+ newChannel.DrRange.Value = 0;
+ newChannel.Rx1Frequency = 0;
+ }
+
+ if( newChannel.Frequency != 0 )
+ {
+ channelAdd.NewChannel = &newChannel;
+ channelAdd.ChannelId = chanIdx;
+
+ // Try to add all channels
+ RegionEU868ChannelAdd( &channelAdd );
+ }
+ else
+ {
+ channelRemove.ChannelId = chanIdx;
+
+ RegionEU868ChannelsRemove( &channelRemove );
+ }
+ }
+}
+
+bool RegionEU868ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegionEU868AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ bool adrAckReq = false;
+ int8_t datarate = adrNext->Datarate;
+ int8_t txPower = adrNext->TxPower;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Report back the adr ack counter
+ *adrAckCounter = adrNext->AdrAckCounter;
+
+ if( adrNext->AdrEnabled == true )
+ {
+ if( datarate == EU868_TX_MIN_DATARATE )
+ {
+ *adrAckCounter = 0;
+ adrAckReq = false;
+ }
+ else
+ {
+ if( adrNext->AdrAckCounter >= EU868_ADR_ACK_LIMIT )
+ {
+ adrAckReq = true;
+ txPower = EU868_MAX_TX_POWER;
+ }
+ else
+ {
+ adrAckReq = false;
+ }
+ if( adrNext->AdrAckCounter >= ( EU868_ADR_ACK_LIMIT + EU868_ADR_ACK_DELAY ) )
+ {
+ if( ( adrNext->AdrAckCounter % EU868_ADR_ACK_DELAY ) == 1 )
+ {
+ // Decrease the datarate
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.Datarate = datarate;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionEU868GetPhyParam( &getPhy );
+ datarate = phyParam.Value;
+
+ if( datarate == EU868_TX_MIN_DATARATE )
+ {
+ // We must set adrAckReq to false as soon as we reach the lowest datarate
+ adrAckReq = false;
+ if( adrNext->UpdateChanMask == true )
+ {
+ // Re-enable default channels
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *drOut = datarate;
+ *txPowOut = txPower;
+ return adrAckReq;
+}
+
+void RegionEU868ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+ double tSymbol = 0.0;
+
+ // Get the datarate, perform a boundary check
+ rxConfigParams->Datarate = MIN( datarate, EU868_RX_MAX_DATARATE );
+ rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+ if( rxConfigParams->Datarate == DR_7 )
+ { // FSK
+ tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesEU868[rxConfigParams->Datarate] );
+ }
+ else
+ { // LoRa
+ tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesEU868[rxConfigParams->Datarate], BandwidthsEU868[rxConfigParams->Datarate] );
+ }
+
+ RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+ RadioModems_t modem;
+ int8_t dr = rxConfig->Datarate;
+ uint8_t maxPayload = 0;
+ int8_t phyDr = 0;
+ uint32_t frequency = rxConfig->Frequency;
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->Window == 0 )
+ {
+ // Apply window 1 frequency
+ frequency = Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = Channels[rxConfig->Channel].Rx1Frequency;
+ }
+ }
+
+ // Read the physical datarate from the datarates table
+ phyDr = DataratesEU868[dr];
+
+ Radio.SetChannel( frequency );
+
+ // Radio configuration
+ if( dr == DR_7 )
+ {
+ modem = MODEM_FSK;
+ Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+ }
+
+ if( rxConfig->RepeaterSupport == true )
+ {
+ maxPayload = MaxPayloadOfDatarateRepeaterEU868[dr];
+ }
+ else
+ {
+ maxPayload = MaxPayloadOfDatarateEU868[dr];
+ }
+
+ Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+ *datarate = (uint8_t) dr;
+ return true;
+}
+
+bool RegionEU868TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ RadioModems_t modem;
+ int8_t phyDr = DataratesEU868[txConfig->Datarate];
+ int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+ uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+ int8_t phyTxPower = 0;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+ // Setup the radio frequency
+ Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+ if( txConfig->Datarate == DR_7 )
+ { // High Speed FSK channel
+ modem = MODEM_FSK;
+ Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+ }
+
+ // Setup maximum payload lenght of the radio driver
+ Radio.SetMaxPayloadLength( modem, txConfig->PktLen );
+ // Get the time-on-air of the next tx frame
+ *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen );
+
+ *txPower = txPowerLimited;
+ return true;
+}
+
+uint8_t RegionEU868LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+ uint8_t status = 0x07;
+ RegionCommonLinkAdrParams_t linkAdrParams;
+ uint8_t nextIndex = 0;
+ uint8_t bytesProcessed = 0;
+ uint16_t chMask = 0;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+ while( bytesProcessed < linkAdrReq->PayloadSize )
+ {
+ // Get ADR request parameters
+ nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+ if( nextIndex == 0 )
+ break; // break loop, since no more request has been found
+
+ // Update bytes processed
+ bytesProcessed += nextIndex;
+
+ // Revert status, as we only check the last ADR request for the channel mask KO
+ status = 0x07;
+
+ // Setup temporary channels mask
+ chMask = linkAdrParams.ChMask;
+
+ // Verify channels mask
+ if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
+ {
+ status &= 0xFE; // Channel mask KO
+ }
+ else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
+ ( linkAdrParams.ChMaskCtrl >= 7 ) )
+ {
+ // RFU
+ status &= 0xFE; // Channel mask KO
+ }
+ else
+ {
+ for( uint8_t i = 0; i < EU868_MAX_NB_CHANNELS; i++ )
+ {
+ if( linkAdrParams.ChMaskCtrl == 6 )
+ {
+ if( Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( Channels[i].Frequency == 0 ) )
+ {// Trying to enable an undefined channel
+ status &= 0xFE; // Channel mask KO
+ }
+ }
+ }
+ }
+ }
+
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+ phyParam = RegionEU868GetPhyParam( &getPhy );
+
+ linkAdrVerifyParams.Status = status;
+ linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+ linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+ linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+ linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+ linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+ linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+ linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+ linkAdrVerifyParams.NbChannels = EU868_MAX_NB_CHANNELS;
+ linkAdrVerifyParams.ChannelsMask = &chMask;
+ linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+ linkAdrVerifyParams.MaxDatarate = EU868_TX_MAX_DATARATE;
+ linkAdrVerifyParams.Channels = Channels;
+ linkAdrVerifyParams.MinTxPower = EU868_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = EU868_MAX_TX_POWER;
+
+ // Verify the parameters and update, if necessary
+ status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+ // Update channelsMask if everything is correct
+ if( status == 0x07 )
+ {
+ // Set the channels mask to a default value
+ memset( ChannelsMask, 0, sizeof( ChannelsMask ) );
+ // Update the channels mask
+ ChannelsMask[0] = chMask;
+ }
+
+ // Update status variables
+ *drOut = linkAdrParams.Datarate;
+ *txPowOut = linkAdrParams.TxPower;
+ *nbRepOut = linkAdrParams.NbRep;
+ *nbBytesParsed = bytesProcessed;
+
+ return status;
+}
+
+uint8_t RegionEU868RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ uint8_t status = 0x07;
+
+ // Verify radio frequency
+ if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ // Verify datarate
+ if( RegionCommonValueInRange( rxParamSetupReq->Datarate, EU868_RX_MIN_DATARATE, EU868_RX_MAX_DATARATE ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify datarate offset
+ if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, EU868_MIN_RX1_DR_OFFSET, EU868_MAX_RX1_DR_OFFSET ) == false )
+ {
+ status &= 0xFB; // Rx1DrOffset range KO
+ }
+
+ return status;
+}
+
+uint8_t RegionEU868NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+ uint8_t status = 0x03;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ if( newChannelReq->NewChannel->Frequency == 0 )
+ {
+ channelRemove.ChannelId = newChannelReq->ChannelId;
+
+ // Remove
+ if( RegionEU868ChannelsRemove( &channelRemove ) == false )
+ {
+ status &= 0xFC;
+ }
+ }
+ else
+ {
+ channelAdd.NewChannel = newChannelReq->NewChannel;
+ channelAdd.ChannelId = newChannelReq->ChannelId;
+
+ switch( RegionEU868ChannelAdd( &channelAdd ) )
+ {
+ case LORAMAC_STATUS_OK:
+ {
+ break;
+ }
+ case LORAMAC_STATUS_FREQUENCY_INVALID:
+ {
+ status &= 0xFE;
+ break;
+ }
+ case LORAMAC_STATUS_DATARATE_INVALID:
+ {
+ status &= 0xFD;
+ break;
+ }
+ case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
+ {
+ status &= 0xFC;
+ break;
+ }
+ default:
+ {
+ status &= 0xFC;
+ break;
+ }
+ }
+ }
+
+ return status;
+}
+
+int8_t RegionEU868TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+ return -1;
+}
+
+uint8_t RegionEU868DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ uint8_t status = 0x03;
+ uint8_t band = 0;
+
+ // Verify if the frequency is supported
+ if( VerifyTxFreq( dlChannelReq->Rx1Frequency, &band ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionEU868AlternateDr( AlternateDrParams_t* alternateDr )
+{
+ int8_t datarate = 0;
+
+ if( ( alternateDr->NbTrials % 48 ) == 0 )
+ {
+ datarate = DR_0;
+ }
+ else if( ( alternateDr->NbTrials % 32 ) == 0 )
+ {
+ datarate = DR_1;
+ }
+ else if( ( alternateDr->NbTrials % 24 ) == 0 )
+ {
+ datarate = DR_2;
+ }
+ else if( ( alternateDr->NbTrials % 16 ) == 0 )
+ {
+ datarate = DR_3;
+ }
+ else if( ( alternateDr->NbTrials % 8 ) == 0 )
+ {
+ datarate = DR_4;
+ }
+ else
+ {
+ datarate = DR_5;
+ }
+ return datarate;
+}
+
+void RegionEU868CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = Channels;
+ calcBackOffParams.Bands = Bands;
+ calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+ calcBackOffParams.Joined = calcBackOff->Joined;
+ calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+ calcBackOffParams.Channel = calcBackOff->Channel;
+ calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+ calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+ RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTx = 0;
+ uint8_t enabledChannels[EU868_MAX_NB_CHANNELS] = { 0 };
+ TimerTime_t nextTxDelay = 0;
+
+ if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+
+ if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, EU868_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ ChannelsMask, Channels,
+ Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+ *time = 0;
+ return true;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return true;
+ }
+ // Datarate not supported by any channel, restore defaults
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ *time = 0;
+ return false;
+ }
+}
+
+LoRaMacStatus_t RegionEU868ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ uint8_t band = 0;
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id >= EU868_MAX_NB_CHANNELS )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ // Validate the datarate range
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+ {
+ drInvalid = true;
+ }
+
+ // Default channels don't accept all values
+ if( id < EU868_NUMB_DEFAULT_CHANNELS )
+ {
+ // Validate the datarate range for min: must be DR_0
+ if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 )
+ {
+ drInvalid = true;
+ }
+ // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, EU868_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ // We are not allowed to change the frequency
+ if( channelAdd->NewChannel->Frequency != Channels[id].Frequency )
+ {
+ freqInvalid = true;
+ }
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyTxFreq( channelAdd->NewChannel->Frequency, &band ) == false )
+ {
+ freqInvalid = true;
+ }
+ }
+
+ // Check status
+ if( ( drInvalid == true ) && ( freqInvalid == true ) )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+ if( drInvalid == true )
+ {
+ return LORAMAC_STATUS_DATARATE_INVALID;
+ }
+ if( freqInvalid == true )
+ {
+ return LORAMAC_STATUS_FREQUENCY_INVALID;
+ }
+
+ memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) );
+ Channels[id].Band = band;
+ ChannelsMask[0] |= ( 1 << id );
+ return LORAMAC_STATUS_OK;
+}
+
+bool RegionEU868ChannelsRemove( ChannelRemoveParams_t* channelRemove )
+{
+ uint8_t id = channelRemove->ChannelId;
+
+ if( id < EU868_NUMB_DEFAULT_CHANNELS )
+ {
+ return false;
+ }
+
+ // Remove the channel from the list of channels
+ Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( ChannelsMask, id, EU868_MAX_NB_CHANNELS );
+}
+
+void RegionEU868SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+ Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionEU868ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ int8_t datarate = dr - drOffset;
+
+ if( datarate < 0 )
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
+
+void RegionEU868RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesEU868;
+ regionCommonRxBeaconSetup.ChannelPlanFrequency = EU868_BEACON_CHANNEL_FREQ;
+ regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = EU868_BEACON_CHANNEL_FREQ;
+ regionCommonRxBeaconSetup.BeaconSize = EU868_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = EU868_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = EU868_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+ regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+ regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup, beaconChannelSet );
+
+ // Store downlink datarate
+ *outDr = EU868_BEACON_CHANNEL_DR;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionEU868.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,579 @@
+/*!
+ * \file RegionEU868.h
+ *
+ * \brief Region definition for EU868
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGIONEU868 Region EU868
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_EU868_H__
+#define __REGION_EU868_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define EU868_MAX_NB_CHANNELS 16
+
+/*!
+ * Number of default channels
+ */
+#define EU868_NUMB_DEFAULT_CHANNELS 3
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define EU868_NUMB_CHANNELS_CF_LIST 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define EU868_TX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define EU868_TX_MAX_DATARATE DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define EU868_RX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define EU868_RX_MAX_DATARATE DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define EU868_DEFAULT_DATARATE DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define EU868_MIN_RX1_DR_OFFSET 0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define EU868_MAX_RX1_DR_OFFSET 5
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define EU868_DEFAULT_RX1_DR_OFFSET 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define EU868_MIN_TX_POWER TX_POWER_7
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define EU868_MAX_TX_POWER TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define EU868_DEFAULT_TX_POWER TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define EU868_DEFAULT_MAX_EIRP 16.0f
+
+/*!
+ * Default antenna gain
+ */
+#define EU868_DEFAULT_ANTENNA_GAIN 2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define EU868_ADR_ACK_LIMIT 64
+
+/*!
+ * ADR Ack delay
+ */
+#define EU868_ADR_ACK_DELAY 32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define EU868_DUTY_CYCLE_ENABLED 1
+
+/*!
+ * Maximum RX window duration
+ */
+#define EU868_MAX_RX_WINDOW 3000
+
+/*!
+ * Receive delay 1
+ */
+#define EU868_RECEIVE_DELAY1 1000
+
+/*!
+ * Receive delay 2
+ */
+#define EU868_RECEIVE_DELAY2 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define EU868_JOIN_ACCEPT_DELAY1 5000
+
+/*!
+ * Join accept delay 2
+ */
+#define EU868_JOIN_ACCEPT_DELAY2 6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define EU868_MAX_FCNT_GAP 16384
+
+/*!
+ * Ack timeout
+ */
+#define EU868_ACKTIMEOUT 2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define EU868_ACK_TIMEOUT_RND 1000
+
+#if ( EU868_DEFAULT_DATARATE > DR_5 )
+#error "A default DR higher than DR_5 may lead to connectivity loss."
+#endif
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define EU868_RX_WND_2_FREQ 869525000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define EU868_RX_WND_2_DR DR_0
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define EU868_BEACON_INTERVAL 128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define EU868_BEACON_RESERVED 2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define EU868_BEACON_GUARD 3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define EU868_BEACON_WINDOW 122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define EU868_BEACON_WINDOW_SLOTS 4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define EU868_PING_SLOT_WINDOW 30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define EU868_BEACON_SYMBOL_TO_DEFAULT 8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define EU868_BEACON_SYMBOL_TO_EXPANSION_MAX 400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define EU868_PING_SLOT_SYMBOL_TO_EXPANSION_MAX 40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define EU868_BEACON_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define EU868_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define EU868_MAX_BEACON_LESS_PERIOD 7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define EU868_BEACON_DELAY_BEACON_TIMING_ANS 30
+
+/*!
+ * Beacon frequency
+ */
+#define EU868_BEACON_CHANNEL_FREQ 869525000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define EU868_BEACON_SIZE 17
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define EU868_BEACON_CHANNEL_DR DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define EU868_BEACON_CHANNEL_BW 0
+
+/*!
+ * Maximum number of bands
+ */
+#define EU868_MAX_NB_BANDS 5
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND0 { 100 , EU868_MAX_TX_POWER, 0, 0 } // 1.0 %
+
+/*!
+ * Band 1 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND1 { 100 , EU868_MAX_TX_POWER, 0, 0 } // 1.0 %
+
+/*!
+ * Band 2 definition
+ * Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND2 { 1000, EU868_MAX_TX_POWER, 0, 0 } // 0.1 %
+
+/*!
+ * Band 2 definition
+ * Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND3 { 10 , EU868_MAX_TX_POWER, 0, 0 } // 10.0 %
+
+/*!
+ * Band 2 definition
+ * Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND4 { 100 , EU868_MAX_TX_POWER, 0, 0 } // 1.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define EU868_LC1 { 868100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
+
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define EU868_LC2 { 868300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
+
+/*!
+ * LoRaMac default channel 3
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define EU868_LC3 { 868500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define EU868_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesEU868[] = { 12, 11, 10, 9, 8, 7, 7, 50 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsEU868[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateEU868[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterEU868[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionEU868GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionEU868SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionEU868InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionEU868Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ * CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionEU868ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionEU868ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionEU868AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ * The receiver will turn on in a [-rxError : +rxError] ms
+ * interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionEU868ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionEU868TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU868LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU868RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU868NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ * Returns -1, if the functionality is not implemented. In this case, the end node
+ * shall not process the command.
+ */
+int8_t RegionEU868TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU868DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionEU868AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionEU868CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ * cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionEU868ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionEU868ChannelsRemove( ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionEU868SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionEU868ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+void RegionEU868RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet );
+
+/*! \} defgroup REGIONEU868 */
+
+#endif // __REGION_EU868_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionIN865.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1152 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region IN865 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionIN865.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[IN865_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[IN865_MAX_NB_BANDS] =
+{
+ IN865_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+ uint8_t nextLowerDr = 0;
+
+ if( dr == minDr )
+ {
+ nextLowerDr = minDr;
+ }
+ else if( dr == DR_7 )
+ {
+ nextLowerDr = DR_5;
+ }
+ else
+ {
+ nextLowerDr = dr - 1;
+ }
+ return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+ switch( BandwidthsIN865[drIndex] )
+ {
+ default:
+ case 125000:
+ return 0;
+ case 250000:
+ return 1;
+ case 500000:
+ return 2;
+ }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+ int8_t txPowerResult = txPower;
+
+ // Limit tx power to the band max
+ txPowerResult = MAX( txPower, maxBandTxPower );
+
+ return txPowerResult;
+}
+
+static bool VerifyTxFreq( uint32_t freq, uint8_t *band )
+{
+ // Check radio driver support
+ if( Radio.CheckRfFrequency( freq ) == false )
+ {
+ return false;
+ }
+
+ if( ( freq < 865000000 ) || ( freq > 867000000 ) )
+ {
+ return false;
+ }
+ return true;
+}
+
+static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTransmission = 0;
+
+ for( uint8_t i = 0, k = 0; i < IN865_MAX_NB_CHANNELS; i += 16, k++ )
+ {
+ for( uint8_t j = 0; j < 16; j++ )
+ {
+ if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+ {
+ if( channels[i + j].Frequency == 0 )
+ { // Check if the channel is enabled
+ continue;
+ }
+ if( joined == false )
+ {
+ if( ( IN865_JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+ {
+ continue;
+ }
+ }
+ if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+ channels[i + j].DrRange.Fields.Max ) == false )
+ { // Check if the current channel selection supports the given datarate
+ continue;
+ }
+ if( bands[channels[i + j].Band].TimeOff > 0 )
+ { // Check if the band is available for transmission
+ delayTransmission++;
+ continue;
+ }
+ enabledChannels[nbEnabledChannels++] = i + j;
+ }
+ }
+ }
+
+ *delayTx = delayTransmission;
+ return nbEnabledChannels;
+}
+
+PhyParam_t RegionIN865GetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ phyParam.Value = IN865_RX_MIN_DATARATE;
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ phyParam.Value = IN865_TX_MIN_DATARATE;
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = IN865_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, IN865_TX_MIN_DATARATE );
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = IN865_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ phyParam.Value = MaxPayloadOfDatarateIN865[getPhy->Datarate];
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterIN865[getPhy->Datarate];
+ break;
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ phyParam.Value = IN865_DUTY_CYCLE_ENABLED;
+ break;
+ }
+ case PHY_MAX_RX_WINDOW:
+ {
+ phyParam.Value = IN865_MAX_RX_WINDOW;
+ break;
+ }
+ case PHY_RECEIVE_DELAY1:
+ {
+ phyParam.Value = IN865_RECEIVE_DELAY1;
+ break;
+ }
+ case PHY_RECEIVE_DELAY2:
+ {
+ phyParam.Value = IN865_RECEIVE_DELAY2;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY1:
+ {
+ phyParam.Value = IN865_JOIN_ACCEPT_DELAY1;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY2:
+ {
+ phyParam.Value = IN865_JOIN_ACCEPT_DELAY2;
+ break;
+ }
+ case PHY_MAX_FCNT_GAP:
+ {
+ phyParam.Value = IN865_MAX_FCNT_GAP;
+ break;
+ }
+ case PHY_ACK_TIMEOUT:
+ {
+ phyParam.Value = ( IN865_ACKTIMEOUT + randr( -IN865_ACK_TIMEOUT_RND, IN865_ACK_TIMEOUT_RND ) );
+ break;
+ }
+ case PHY_DEF_DR1_OFFSET:
+ {
+ phyParam.Value = IN865_DEFAULT_RX1_DR_OFFSET;
+ break;
+ }
+ case PHY_DEF_RX2_FREQUENCY:
+ {
+ phyParam.Value = IN865_RX_WND_2_FREQ;
+ break;
+ }
+ case PHY_DEF_RX2_DR:
+ {
+ phyParam.Value = IN865_RX_WND_2_DR;
+ break;
+ }
+ case PHY_CHANNELS_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = IN865_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = Channels;
+ break;
+ }
+ case PHY_DEF_UPLINK_DWELL_TIME:
+ case PHY_DEF_DOWNLINK_DWELL_TIME:
+ {
+ phyParam.Value = 0;
+ break;
+ }
+ case PHY_DEF_MAX_EIRP:
+ {
+ phyParam.fValue = IN865_DEFAULT_MAX_EIRP;
+ break;
+ }
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = IN865_DEFAULT_ANTENNA_GAIN;
+ break;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ case PHY_DEF_NB_JOIN_TRIALS:
+ {
+ phyParam.Value = 48;
+ break;
+ }
+ case PHY_BEACON_INTERVAL:
+ {
+ phyParam.Value = IN865_BEACON_INTERVAL;
+ break;
+ }
+ case PHY_BEACON_RESERVED:
+ {
+ phyParam.Value = IN865_BEACON_RESERVED;
+ break;
+ }
+ case PHY_BEACON_GUARD:
+ {
+ phyParam.Value = IN865_BEACON_GUARD;
+ break;
+ }
+ case PHY_BEACON_WINDOW:
+ {
+ phyParam.Value = IN865_BEACON_WINDOW;
+ break;
+ }
+ case PHY_BEACON_WINDOW_SLOTS:
+ {
+ phyParam.Value = IN865_BEACON_WINDOW_SLOTS;
+ break;
+ }
+ case PHY_PING_SLOT_WINDOW:
+ {
+ phyParam.Value = IN865_PING_SLOT_WINDOW;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_DEFAULT:
+ {
+ phyParam.Value = IN865_BEACON_SYMBOL_TO_DEFAULT;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = IN865_BEACON_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = IN865_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = IN865_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = IN865_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_MAX_BEACON_LESS_PERIOD:
+ {
+ phyParam.Value = IN865_MAX_BEACON_LESS_PERIOD;
+ break;
+ }
+ case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+ {
+ phyParam.Value = IN865_BEACON_DELAY_BEACON_TIMING_ANS;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = IN865_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_PINGSLOT_CHANNEL_FREQ:
+ {
+ phyParam.Value = IN865_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_SIZE:
+ {
+ phyParam.Value = IN865_BEACON_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = IN865_BEACON_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionIN865InitDefaults( InitType_t type )
+{
+ switch( type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Channels
+ Channels[0] = ( ChannelParams_t ) IN865_LC1;
+ Channels[1] = ( ChannelParams_t ) IN865_LC2;
+ Channels[2] = ( ChannelParams_t ) IN865_LC3;
+
+ // Initialize the channels default mask
+ ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE:
+ {
+ // Restore channels default mask
+ ChannelsMask[0] |= ChannelsDefaultMask[0];
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+ }
+ case PHY_RX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_POWER:
+ case PHY_TX_POWER:
+ {
+ // Remark: switched min and max!
+ return RegionCommonValueInRange( verify->TxPower, IN865_MAX_TX_POWER, IN865_MIN_TX_POWER );
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ return IN865_DUTY_CYCLE_ENABLED;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ {
+ if( verify->NbJoinTrials < 48 )
+ {
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+void RegionIN865ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ ChannelParams_t newChannel;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ // Setup default datarate range
+ newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+ // Size of the optional CF list
+ if( applyCFList->Size != 16 )
+ {
+ return;
+ }
+
+ // Last byte is RFU, don't take it into account
+ for( uint8_t i = 0, chanIdx = IN865_NUMB_DEFAULT_CHANNELS; chanIdx < IN865_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+ {
+ if( chanIdx < ( IN865_NUMB_CHANNELS_CF_LIST + IN865_NUMB_DEFAULT_CHANNELS ) )
+ {
+ // Channel frequency
+ newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
+ newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
+ newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
+ newChannel.Frequency *= 100;
+
+ // Initialize alternative frequency to 0
+ newChannel.Rx1Frequency = 0;
+ }
+ else
+ {
+ newChannel.Frequency = 0;
+ newChannel.DrRange.Value = 0;
+ newChannel.Rx1Frequency = 0;
+ }
+
+ if( newChannel.Frequency != 0 )
+ {
+ channelAdd.NewChannel = &newChannel;
+ channelAdd.ChannelId = chanIdx;
+
+ // Try to add all channels
+ RegionIN865ChannelAdd( &channelAdd );
+ }
+ else
+ {
+ channelRemove.ChannelId = chanIdx;
+
+ RegionIN865ChannelsRemove( &channelRemove );
+ }
+ }
+}
+
+bool RegionIN865ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegionIN865AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ bool adrAckReq = false;
+ int8_t datarate = adrNext->Datarate;
+ int8_t txPower = adrNext->TxPower;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Report back the adr ack counter
+ *adrAckCounter = adrNext->AdrAckCounter;
+
+ if( adrNext->AdrEnabled == true )
+ {
+ if( datarate == IN865_TX_MIN_DATARATE )
+ {
+ *adrAckCounter = 0;
+ adrAckReq = false;
+ }
+ else
+ {
+ if( adrNext->AdrAckCounter >= IN865_ADR_ACK_LIMIT )
+ {
+ adrAckReq = true;
+ txPower = IN865_MAX_TX_POWER;
+ }
+ else
+ {
+ adrAckReq = false;
+ }
+ if( adrNext->AdrAckCounter >= ( IN865_ADR_ACK_LIMIT + IN865_ADR_ACK_DELAY ) )
+ {
+ if( ( adrNext->AdrAckCounter % IN865_ADR_ACK_DELAY ) == 1 )
+ {
+ // Decrease the datarate
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.Datarate = datarate;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionIN865GetPhyParam( &getPhy );
+ datarate = phyParam.Value;
+
+ if( datarate == IN865_TX_MIN_DATARATE )
+ {
+ // We must set adrAckReq to false as soon as we reach the lowest datarate
+ adrAckReq = false;
+ if( adrNext->UpdateChanMask == true )
+ {
+ // Re-enable default channels
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *drOut = datarate;
+ *txPowOut = txPower;
+ return adrAckReq;
+}
+
+void RegionIN865ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+ double tSymbol = 0.0;
+
+ // Get the datarate, perform a boundary check
+ rxConfigParams->Datarate = MIN( datarate, IN865_RX_MAX_DATARATE );
+ rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+ if( rxConfigParams->Datarate == DR_7 )
+ { // FSK
+ tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesIN865[rxConfigParams->Datarate] );
+ }
+ else
+ { // LoRa
+ tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesIN865[rxConfigParams->Datarate], BandwidthsIN865[rxConfigParams->Datarate] );
+ }
+
+ RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+ RadioModems_t modem;
+ int8_t dr = rxConfig->Datarate;
+ uint8_t maxPayload = 0;
+ int8_t phyDr = 0;
+ uint32_t frequency = rxConfig->Frequency;
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->Window == 0 )
+ {
+ // Apply window 1 frequency
+ frequency = Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = Channels[rxConfig->Channel].Rx1Frequency;
+ }
+ }
+
+ // Read the physical datarate from the datarates table
+ phyDr = DataratesIN865[dr];
+
+ Radio.SetChannel( frequency );
+
+ // Radio configuration
+ if( dr == DR_7 )
+ {
+ modem = MODEM_FSK;
+ Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+ }
+
+ if( rxConfig->RepeaterSupport == true )
+ {
+ maxPayload = MaxPayloadOfDatarateRepeaterIN865[dr];
+ }
+ else
+ {
+ maxPayload = MaxPayloadOfDatarateIN865[dr];
+ }
+ Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+ *datarate = (uint8_t) dr;
+ return true;
+}
+
+bool RegionIN865TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ RadioModems_t modem;
+ int8_t phyDr = DataratesIN865[txConfig->Datarate];
+ int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+ uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+ int8_t phyTxPower = 0;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+ // Setup the radio frequency
+ Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+ if( txConfig->Datarate == DR_7 )
+ { // High Speed FSK channel
+ modem = MODEM_FSK;
+ Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+ }
+
+ // Setup maximum payload lenght of the radio driver
+ Radio.SetMaxPayloadLength( modem, txConfig->PktLen );
+ // Get the time-on-air of the next tx frame
+ *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen );
+
+ *txPower = txPowerLimited;
+ return true;
+}
+
+uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+ uint8_t status = 0x07;
+ RegionCommonLinkAdrParams_t linkAdrParams;
+ uint8_t nextIndex = 0;
+ uint8_t bytesProcessed = 0;
+ uint16_t chMask = 0;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+ while( bytesProcessed < linkAdrReq->PayloadSize )
+ {
+ // Get ADR request parameters
+ nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+ if( nextIndex == 0 )
+ break; // break loop, since no more request has been found
+
+ // Update bytes processed
+ bytesProcessed += nextIndex;
+
+ // Revert status, as we only check the last ADR request for the channel mask KO
+ status = 0x07;
+
+ // Setup temporary channels mask
+ chMask = linkAdrParams.ChMask;
+
+ // Verify channels mask
+ if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
+ {
+ status &= 0xFE; // Channel mask KO
+ }
+ else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
+ ( linkAdrParams.ChMaskCtrl >= 7 ) )
+ {
+ // RFU
+ status &= 0xFE; // Channel mask KO
+ }
+ else
+ {
+ for( uint8_t i = 0; i < IN865_MAX_NB_CHANNELS; i++ )
+ {
+ if( linkAdrParams.ChMaskCtrl == 6 )
+ {
+ if( Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( Channels[i].Frequency == 0 ) )
+ {// Trying to enable an undefined channel
+ status &= 0xFE; // Channel mask KO
+ }
+ }
+ }
+ }
+ }
+
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+ phyParam = RegionIN865GetPhyParam( &getPhy );
+
+ linkAdrVerifyParams.Status = status;
+ linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+ linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+ linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+ linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+ linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+ linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+ linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+ linkAdrVerifyParams.NbChannels = IN865_MAX_NB_CHANNELS;
+ linkAdrVerifyParams.ChannelsMask = &chMask;
+ linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+ linkAdrVerifyParams.MaxDatarate = IN865_TX_MAX_DATARATE;
+ linkAdrVerifyParams.Channels = Channels;
+ linkAdrVerifyParams.MinTxPower = IN865_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = IN865_MAX_TX_POWER;
+
+ // Verify the parameters and update, if necessary
+ status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+ // Update channelsMask if everything is correct
+ if( status == 0x07 )
+ {
+ // Set the channels mask to a default value
+ memset( ChannelsMask, 0, sizeof( ChannelsMask ) );
+ // Update the channels mask
+ ChannelsMask[0] = chMask;
+ }
+
+ // Update status variables
+ *drOut = linkAdrParams.Datarate;
+ *txPowOut = linkAdrParams.TxPower;
+ *nbRepOut = linkAdrParams.NbRep;
+ *nbBytesParsed = bytesProcessed;
+
+ return status;
+}
+
+uint8_t RegionIN865RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ uint8_t status = 0x07;
+
+ // Verify radio frequency
+ if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ // Verify datarate
+ if( RegionCommonValueInRange( rxParamSetupReq->Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify datarate offset
+ if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, IN865_MIN_RX1_DR_OFFSET, IN865_MAX_RX1_DR_OFFSET ) == false )
+ {
+ status &= 0xFB; // Rx1DrOffset range KO
+ }
+
+ return status;
+}
+
+uint8_t RegionIN865NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+ uint8_t status = 0x03;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ if( newChannelReq->NewChannel->Frequency == 0 )
+ {
+ channelRemove.ChannelId = newChannelReq->ChannelId;
+
+ // Remove
+ if( RegionIN865ChannelsRemove( &channelRemove ) == false )
+ {
+ status &= 0xFC;
+ }
+ }
+ else
+ {
+ channelAdd.NewChannel = newChannelReq->NewChannel;
+ channelAdd.ChannelId = newChannelReq->ChannelId;
+
+ switch( RegionIN865ChannelAdd( &channelAdd ) )
+ {
+ case LORAMAC_STATUS_OK:
+ {
+ break;
+ }
+ case LORAMAC_STATUS_FREQUENCY_INVALID:
+ {
+ status &= 0xFE;
+ break;
+ }
+ case LORAMAC_STATUS_DATARATE_INVALID:
+ {
+ status &= 0xFD;
+ break;
+ }
+ case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
+ {
+ status &= 0xFC;
+ break;
+ }
+ default:
+ {
+ status &= 0xFC;
+ break;
+ }
+ }
+ }
+
+ return status;
+}
+
+int8_t RegionIN865TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+ return -1;
+}
+
+uint8_t RegionIN865DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ uint8_t status = 0x03;
+ uint8_t band = 0;
+
+ // Verify if the frequency is supported
+ if( VerifyTxFreq( dlChannelReq->Rx1Frequency, &band ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionIN865AlternateDr( AlternateDrParams_t* alternateDr )
+{
+ int8_t datarate = 0;
+
+ if( ( alternateDr->NbTrials % 48 ) == 0 )
+ {
+ datarate = DR_0;
+ }
+ else if( ( alternateDr->NbTrials % 32 ) == 0 )
+ {
+ datarate = DR_1;
+ }
+ else if( ( alternateDr->NbTrials % 24 ) == 0 )
+ {
+ datarate = DR_2;
+ }
+ else if( ( alternateDr->NbTrials % 16 ) == 0 )
+ {
+ datarate = DR_3;
+ }
+ else if( ( alternateDr->NbTrials % 8 ) == 0 )
+ {
+ datarate = DR_4;
+ }
+ else
+ {
+ datarate = DR_5;
+ }
+ return datarate;
+}
+
+void RegionIN865CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = Channels;
+ calcBackOffParams.Bands = Bands;
+ calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+ calcBackOffParams.Joined = calcBackOff->Joined;
+ calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+ calcBackOffParams.Channel = calcBackOff->Channel;
+ calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+ calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+ RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionIN865NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTx = 0;
+ uint8_t enabledChannels[IN865_MAX_NB_CHANNELS] = { 0 };
+ TimerTime_t nextTxDelay = 0;
+
+ if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+
+ if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, IN865_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ ChannelsMask, Channels,
+ Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+ *time = 0;
+ return true;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return true;
+ }
+ // Datarate not supported by any channel, restore defaults
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ *time = 0;
+ return false;
+ }
+}
+
+LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ uint8_t band = 0;
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id >= IN865_MAX_NB_CHANNELS )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ // Validate the datarate range
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+ {
+ drInvalid = true;
+ }
+
+ // Default channels don't accept all values
+ if( id < IN865_NUMB_DEFAULT_CHANNELS )
+ {
+ // Validate the datarate range for min: must be DR_0
+ if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 )
+ {
+ drInvalid = true;
+ }
+ // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, IN865_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ // We are not allowed to change the frequency
+ if( channelAdd->NewChannel->Frequency != Channels[id].Frequency )
+ {
+ freqInvalid = true;
+ }
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyTxFreq( channelAdd->NewChannel->Frequency, &band ) == false )
+ {
+ freqInvalid = true;
+ }
+ }
+
+ // Check status
+ if( ( drInvalid == true ) && ( freqInvalid == true ) )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+ if( drInvalid == true )
+ {
+ return LORAMAC_STATUS_DATARATE_INVALID;
+ }
+ if( freqInvalid == true )
+ {
+ return LORAMAC_STATUS_FREQUENCY_INVALID;
+ }
+
+ memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) );
+ Channels[id].Band = band;
+ ChannelsMask[0] |= ( 1 << id );
+ return LORAMAC_STATUS_OK;
+}
+
+bool RegionIN865ChannelsRemove( ChannelRemoveParams_t* channelRemove )
+{
+ uint8_t id = channelRemove->ChannelId;
+
+ if( id < IN865_NUMB_DEFAULT_CHANNELS )
+ {
+ return false;
+ }
+
+ // Remove the channel from the list of channels
+ Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( ChannelsMask, id, IN865_MAX_NB_CHANNELS );
+}
+
+void RegionIN865SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+ Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ // Apply offset formula
+ return MIN( DR_5, MAX( DR_0, dr - EffectiveRx1DrOffsetIN865[drOffset] ) );
+}
+
+void RegionIN865RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesIN865;
+ regionCommonRxBeaconSetup.ChannelPlanFrequency = IN865_BEACON_CHANNEL_FREQ;
+ regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = IN865_BEACON_CHANNEL_FREQ;
+ regionCommonRxBeaconSetup.BeaconSize = IN865_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = IN865_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = IN865_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+ regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+ regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup, beaconChannelSet );
+
+ // Store downlink datarate
+ *outDr = IN865_BEACON_CHANNEL_DR;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionIN865.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,558 @@
+/*!
+ * \file RegionIN865.h
+ *
+ * \brief Region definition for IN865
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGIONIN865 Region IN865
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_IN865_H__
+#define __REGION_IN865_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define IN865_MAX_NB_CHANNELS 16
+
+/*!
+ * Number of default channels
+ */
+#define IN865_NUMB_DEFAULT_CHANNELS 3
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define IN865_NUMB_CHANNELS_CF_LIST 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define IN865_TX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define IN865_TX_MAX_DATARATE DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define IN865_RX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define IN865_RX_MAX_DATARATE DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define IN865_DEFAULT_DATARATE DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define IN865_MIN_RX1_DR_OFFSET 0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define IN865_MAX_RX1_DR_OFFSET 7
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define IN865_DEFAULT_RX1_DR_OFFSET 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define IN865_MIN_TX_POWER TX_POWER_10
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define IN865_MAX_TX_POWER TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define IN865_DEFAULT_TX_POWER TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define IN865_DEFAULT_MAX_EIRP 30.0f
+
+/*!
+ * Default antenna gain
+ */
+#define IN865_DEFAULT_ANTENNA_GAIN 2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define IN865_ADR_ACK_LIMIT 64
+
+/*!
+ * ADR Ack delay
+ */
+#define IN865_ADR_ACK_DELAY 32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define IN865_DUTY_CYCLE_ENABLED 1
+
+/*!
+ * Maximum RX window duration
+ */
+#define IN865_MAX_RX_WINDOW 3000
+
+/*!
+ * Receive delay 1
+ */
+#define IN865_RECEIVE_DELAY1 1000
+
+/*!
+ * Receive delay 2
+ */
+#define IN865_RECEIVE_DELAY2 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define IN865_JOIN_ACCEPT_DELAY1 5000
+
+/*!
+ * Join accept delay 2
+ */
+#define IN865_JOIN_ACCEPT_DELAY2 6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define IN865_MAX_FCNT_GAP 16384
+
+/*!
+ * Ack timeout
+ */
+#define IN865_ACKTIMEOUT 2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define IN865_ACK_TIMEOUT_RND 1000
+
+#if ( IN865_DEFAULT_DATARATE > DR_5 )
+#error "A default DR higher than DR_5 may lead to connectivity loss."
+#endif
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define IN865_RX_WND_2_FREQ 866550000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define IN865_RX_WND_2_DR DR_2
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define IN865_BEACON_INTERVAL 128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define IN865_BEACON_RESERVED 2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define IN865_BEACON_GUARD 3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define IN865_BEACON_WINDOW 122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define IN865_BEACON_WINDOW_SLOTS 4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define IN865_PING_SLOT_WINDOW 30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define IN865_BEACON_SYMBOL_TO_DEFAULT 8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define IN865_BEACON_SYMBOL_TO_EXPANSION_MAX 400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define IN865_PING_SLOT_SYMBOL_TO_EXPANSION_MAX 40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define IN865_BEACON_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define IN865_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define IN865_MAX_BEACON_LESS_PERIOD 7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define IN865_BEACON_DELAY_BEACON_TIMING_ANS 30
+
+/*!
+ * Beacon frequency
+ */
+#define IN865_BEACON_CHANNEL_FREQ 866550000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define IN865_BEACON_SIZE 19
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define IN865_BEACON_CHANNEL_DR DR_4
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define IN865_BEACON_CHANNEL_BW 0
+
+/*!
+ * Maximum number of bands
+ */
+#define IN865_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define IN865_BAND0 { 1 , IN865_MAX_TX_POWER, 0, 0 } // 100.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define IN865_LC1 { 865062500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define IN865_LC2 { 865402500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 3
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define IN865_LC3 { 865985000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define IN865_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesIN865[] = { 12, 11, 10, 9, 8, 7, 7, 50 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsIN865[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateIN865[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterIN865[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
+
+/*!
+ * Effective datarate offsets for receive window 1.
+ */
+static const int8_t EffectiveRx1DrOffsetIN865[] = { 0, 1, 2, 3, 4, 5, -1, -2 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionIN865GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionIN865InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ * CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionIN865ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionIN865ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionIN865AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ * The receiver will turn on in a [-rxError : +rxError] ms
+ * interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionIN865ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionIN865TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionIN865RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionIN865NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ * Returns -1, if the functionality is not implemented. In this case, the end node
+ * shall not process the command.
+ */
+int8_t RegionIN865TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionIN865DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionIN865AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionIN865CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ * cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionIN865NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionIN865ChannelsRemove( ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionIN865SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONIN865 */
+
+#endif // __REGION_IN865_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionKR920.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1153 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region KR920 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionKR920.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[KR920_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[KR920_MAX_NB_BANDS] =
+{
+ KR920_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+ uint8_t nextLowerDr = 0;
+
+ if( dr == minDr )
+ {
+ nextLowerDr = minDr;
+ }
+ else
+ {
+ nextLowerDr = dr - 1;
+ }
+ return nextLowerDr;
+}
+
+static int8_t GetMaxEIRP( uint32_t freq )
+{
+ if( freq >= 922100000 )
+ {// Limit to 14dBm
+ return KR920_DEFAULT_MAX_EIRP_HIGH;
+ }
+ // Limit to 10dBm
+ return KR920_DEFAULT_MAX_EIRP_LOW;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+ switch( BandwidthsKR920[drIndex] )
+ {
+ default:
+ case 125000:
+ return 0;
+ case 250000:
+ return 1;
+ case 500000:
+ return 2;
+ }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+ int8_t txPowerResult = txPower;
+
+ // Limit tx power to the band max
+ txPowerResult = MAX( txPower, maxBandTxPower );
+
+ return txPowerResult;
+}
+
+static bool VerifyTxFreq( uint32_t freq )
+{
+ uint32_t tmpFreq = freq;
+
+ // Check radio driver support
+ if( Radio.CheckRfFrequency( tmpFreq ) == false )
+ {
+ return false;
+ }
+
+ // Verify if the frequency is valid. The frequency must be in a specified
+ // range and can be set to specific values.
+ if( ( tmpFreq >= 920900000 ) && ( tmpFreq <=923300000 ) )
+ {
+ // Range ok, check for specific value
+ tmpFreq -= 920900000;
+ if( ( tmpFreq % 200000 ) == 0 )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTransmission = 0;
+
+ for( uint8_t i = 0, k = 0; i < KR920_MAX_NB_CHANNELS; i += 16, k++ )
+ {
+ for( uint8_t j = 0; j < 16; j++ )
+ {
+ if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+ {
+ if( channels[i + j].Frequency == 0 )
+ { // Check if the channel is enabled
+ continue;
+ }
+ if( joined == false )
+ {
+ if( ( KR920_JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+ {
+ continue;
+ }
+ }
+ if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+ channels[i + j].DrRange.Fields.Max ) == false )
+ { // Check if the current channel selection supports the given datarate
+ continue;
+ }
+ if( bands[channels[i + j].Band].TimeOff > 0 )
+ { // Check if the band is available for transmission
+ delayTransmission++;
+ continue;
+ }
+ enabledChannels[nbEnabledChannels++] = i + j;
+ }
+ }
+ }
+
+ *delayTx = delayTransmission;
+ return nbEnabledChannels;
+}
+
+PhyParam_t RegionKR920GetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ phyParam.Value = KR920_RX_MIN_DATARATE;
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ phyParam.Value = KR920_TX_MIN_DATARATE;
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = KR920_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, KR920_TX_MIN_DATARATE );
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = KR920_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ phyParam.Value = MaxPayloadOfDatarateKR920[getPhy->Datarate];
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterKR920[getPhy->Datarate];
+ break;
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ phyParam.Value = KR920_DUTY_CYCLE_ENABLED;
+ break;
+ }
+ case PHY_MAX_RX_WINDOW:
+ {
+ phyParam.Value = KR920_MAX_RX_WINDOW;
+ break;
+ }
+ case PHY_RECEIVE_DELAY1:
+ {
+ phyParam.Value = KR920_RECEIVE_DELAY1;
+ break;
+ }
+ case PHY_RECEIVE_DELAY2:
+ {
+ phyParam.Value = KR920_RECEIVE_DELAY2;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY1:
+ {
+ phyParam.Value = KR920_JOIN_ACCEPT_DELAY1;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY2:
+ {
+ phyParam.Value = KR920_JOIN_ACCEPT_DELAY2;
+ break;
+ }
+ case PHY_MAX_FCNT_GAP:
+ {
+ phyParam.Value = KR920_MAX_FCNT_GAP;
+ break;
+ }
+ case PHY_ACK_TIMEOUT:
+ {
+ phyParam.Value = ( KR920_ACKTIMEOUT + randr( -KR920_ACK_TIMEOUT_RND, KR920_ACK_TIMEOUT_RND ) );
+ break;
+ }
+ case PHY_DEF_DR1_OFFSET:
+ {
+ phyParam.Value = KR920_DEFAULT_RX1_DR_OFFSET;
+ break;
+ }
+ case PHY_DEF_RX2_FREQUENCY:
+ {
+ phyParam.Value = KR920_RX_WND_2_FREQ;
+ break;
+ }
+ case PHY_DEF_RX2_DR:
+ {
+ phyParam.Value = KR920_RX_WND_2_DR;
+ break;
+ }
+ case PHY_CHANNELS_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = KR920_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = Channels;
+ break;
+ }
+ case PHY_DEF_UPLINK_DWELL_TIME:
+ case PHY_DEF_DOWNLINK_DWELL_TIME:
+ {
+ phyParam.Value = 0;
+ break;
+ }
+ case PHY_DEF_MAX_EIRP:
+ {
+ // We set the higher maximum EIRP as default value.
+ // The reason for this is, that the frequency may
+ // change during a channel selection for the next uplink.
+ // The value has to be recalculated in the TX configuration.
+ phyParam.fValue = KR920_DEFAULT_MAX_EIRP_HIGH;
+ break;
+ }
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = KR920_DEFAULT_ANTENNA_GAIN;
+ break;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ case PHY_DEF_NB_JOIN_TRIALS:
+ {
+ phyParam.Value = 48;
+ break;
+ }
+ case PHY_BEACON_INTERVAL:
+ {
+ phyParam.Value = KR920_BEACON_INTERVAL;
+ break;
+ }
+ case PHY_BEACON_RESERVED:
+ {
+ phyParam.Value = KR920_BEACON_RESERVED;
+ break;
+ }
+ case PHY_BEACON_GUARD:
+ {
+ phyParam.Value = KR920_BEACON_GUARD;
+ break;
+ }
+ case PHY_BEACON_WINDOW:
+ {
+ phyParam.Value = KR920_BEACON_WINDOW;
+ break;
+ }
+ case PHY_BEACON_WINDOW_SLOTS:
+ {
+ phyParam.Value = KR920_BEACON_WINDOW_SLOTS;
+ break;
+ }
+ case PHY_PING_SLOT_WINDOW:
+ {
+ phyParam.Value = KR920_PING_SLOT_WINDOW;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_DEFAULT:
+ {
+ phyParam.Value = KR920_BEACON_SYMBOL_TO_DEFAULT;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = KR920_BEACON_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = KR920_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = KR920_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = KR920_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_MAX_BEACON_LESS_PERIOD:
+ {
+ phyParam.Value = KR920_MAX_BEACON_LESS_PERIOD;
+ break;
+ }
+ case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+ {
+ phyParam.Value = KR920_BEACON_DELAY_BEACON_TIMING_ANS;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = KR920_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_PINGSLOT_CHANNEL_FREQ:
+ {
+ phyParam.Value = KR920_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_SIZE:
+ {
+ phyParam.Value = KR920_BEACON_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = KR920_BEACON_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionKR920SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionKR920InitDefaults( InitType_t type )
+{
+ switch( type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Channels
+ Channels[0] = ( ChannelParams_t ) KR920_LC1;
+ Channels[1] = ( ChannelParams_t ) KR920_LC2;
+ Channels[2] = ( ChannelParams_t ) KR920_LC3;
+
+ // Initialize the channels default mask
+ ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE:
+ {
+ // Restore channels default mask
+ ChannelsMask[0] |= ChannelsDefaultMask[0];
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionKR920Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+ }
+ case PHY_RX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, KR920_RX_MIN_DATARATE, KR920_RX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_POWER:
+ case PHY_TX_POWER:
+ {
+ // Remark: switched min and max!
+ return RegionCommonValueInRange( verify->TxPower, KR920_MAX_TX_POWER, KR920_MIN_TX_POWER );
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ return KR920_DUTY_CYCLE_ENABLED;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ {
+ if( verify->NbJoinTrials < 48 )
+ {
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+void RegionKR920ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ ChannelParams_t newChannel;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ // Setup default datarate range
+ newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+ // Size of the optional CF list
+ if( applyCFList->Size != 16 )
+ {
+ return;
+ }
+
+ // Last byte is RFU, don't take it into account
+ for( uint8_t i = 0, chanIdx = KR920_NUMB_DEFAULT_CHANNELS; chanIdx < KR920_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+ {
+ if( chanIdx < ( KR920_NUMB_CHANNELS_CF_LIST + KR920_NUMB_DEFAULT_CHANNELS ) )
+ {
+ // Channel frequency
+ newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
+ newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
+ newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
+ newChannel.Frequency *= 100;
+
+ // Initialize alternative frequency to 0
+ newChannel.Rx1Frequency = 0;
+ }
+ else
+ {
+ newChannel.Frequency = 0;
+ newChannel.DrRange.Value = 0;
+ newChannel.Rx1Frequency = 0;
+ }
+
+ if( newChannel.Frequency != 0 )
+ {
+ channelAdd.NewChannel = &newChannel;
+ channelAdd.ChannelId = chanIdx;
+
+ // Try to add all channels
+ RegionKR920ChannelAdd( &channelAdd );
+ }
+ else
+ {
+ channelRemove.ChannelId = chanIdx;
+
+ RegionKR920ChannelsRemove( &channelRemove );
+ }
+ }
+}
+
+bool RegionKR920ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegionKR920AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ bool adrAckReq = false;
+ int8_t datarate = adrNext->Datarate;
+ int8_t txPower = adrNext->TxPower;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Report back the adr ack counter
+ *adrAckCounter = adrNext->AdrAckCounter;
+
+ if( adrNext->AdrEnabled == true )
+ {
+ if( datarate == KR920_TX_MIN_DATARATE )
+ {
+ *adrAckCounter = 0;
+ adrAckReq = false;
+ }
+ else
+ {
+ if( adrNext->AdrAckCounter >= KR920_ADR_ACK_LIMIT )
+ {
+ adrAckReq = true;
+ txPower = KR920_MAX_TX_POWER;
+ }
+ else
+ {
+ adrAckReq = false;
+ }
+ if( adrNext->AdrAckCounter >= ( KR920_ADR_ACK_LIMIT + KR920_ADR_ACK_DELAY ) )
+ {
+ if( ( adrNext->AdrAckCounter % KR920_ADR_ACK_DELAY ) == 1 )
+ {
+ // Decrease the datarate
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.Datarate = datarate;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionKR920GetPhyParam( &getPhy );
+ datarate = phyParam.Value;
+
+ if( datarate == KR920_TX_MIN_DATARATE )
+ {
+ // We must set adrAckReq to false as soon as we reach the lowest datarate
+ adrAckReq = false;
+ if( adrNext->UpdateChanMask == true )
+ {
+ // Re-enable default channels
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *drOut = datarate;
+ *txPowOut = txPower;
+ return adrAckReq;
+}
+
+void RegionKR920ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+ double tSymbol = 0.0;
+
+ // Get the datarate, perform a boundary check
+ rxConfigParams->Datarate = MIN( datarate, KR920_RX_MAX_DATARATE );
+ rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+ tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesKR920[rxConfigParams->Datarate], BandwidthsKR920[rxConfigParams->Datarate] );
+
+ RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+ int8_t dr = rxConfig->Datarate;
+ uint8_t maxPayload = 0;
+ int8_t phyDr = 0;
+ uint32_t frequency = rxConfig->Frequency;
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->Window == 0 )
+ {
+ // Apply window 1 frequency
+ frequency = Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = Channels[rxConfig->Channel].Rx1Frequency;
+ }
+ }
+
+ // Read the physical datarate from the datarates table
+ phyDr = DataratesKR920[dr];
+
+ Radio.SetChannel( frequency );
+
+ // Radio configuration
+ Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+ maxPayload = MaxPayloadOfDatarateKR920[dr];
+ Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+ *datarate = (uint8_t) dr;
+ return true;
+}
+
+bool RegionKR920TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ int8_t phyDr = DataratesKR920[txConfig->Datarate];
+ int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+ uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+ float maxEIRP = GetMaxEIRP( Channels[txConfig->Channel].Frequency );
+ int8_t phyTxPower = 0;
+
+ // Take the minimum between the maxEIRP and txConfig->MaxEirp.
+ // The value of txConfig->MaxEirp could have changed during runtime, e.g. due to a MAC command.
+ maxEIRP = MIN( txConfig->MaxEirp, maxEIRP );
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, maxEIRP, txConfig->AntennaGain );
+
+ // Setup the radio frequency
+ Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+ Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+
+ // Setup maximum payload lenght of the radio driver
+ Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen );
+ // Get the time-on-air of the next tx frame
+ *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen );
+
+ *txPower = txPowerLimited;
+ return true;
+}
+
+uint8_t RegionKR920LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+ uint8_t status = 0x07;
+ RegionCommonLinkAdrParams_t linkAdrParams;
+ uint8_t nextIndex = 0;
+ uint8_t bytesProcessed = 0;
+ uint16_t chMask = 0;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+ while( bytesProcessed < linkAdrReq->PayloadSize )
+ {
+ // Get ADR request parameters
+ nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+ if( nextIndex == 0 )
+ break; // break loop, since no more request has been found
+
+ // Update bytes processed
+ bytesProcessed += nextIndex;
+
+ // Revert status, as we only check the last ADR request for the channel mask KO
+ status = 0x07;
+
+ // Setup temporary channels mask
+ chMask = linkAdrParams.ChMask;
+
+ // Verify channels mask
+ if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
+ {
+ status &= 0xFE; // Channel mask KO
+ }
+ else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
+ ( linkAdrParams.ChMaskCtrl >= 7 ) )
+ {
+ // RFU
+ status &= 0xFE; // Channel mask KO
+ }
+ else
+ {
+ for( uint8_t i = 0; i < KR920_MAX_NB_CHANNELS; i++ )
+ {
+ if( linkAdrParams.ChMaskCtrl == 6 )
+ {
+ if( Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( Channels[i].Frequency == 0 ) )
+ {// Trying to enable an undefined channel
+ status &= 0xFE; // Channel mask KO
+ }
+ }
+ }
+ }
+ }
+
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+ phyParam = RegionKR920GetPhyParam( &getPhy );
+
+ linkAdrVerifyParams.Status = status;
+ linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+ linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+ linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+ linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+ linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+ linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+ linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+ linkAdrVerifyParams.NbChannels = KR920_MAX_NB_CHANNELS;
+ linkAdrVerifyParams.ChannelsMask = &chMask;
+ linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+ linkAdrVerifyParams.MaxDatarate = KR920_TX_MAX_DATARATE;
+ linkAdrVerifyParams.Channels = Channels;
+ linkAdrVerifyParams.MinTxPower = KR920_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = KR920_MAX_TX_POWER;
+
+ // Verify the parameters and update, if necessary
+ status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+ // Update channelsMask if everything is correct
+ if( status == 0x07 )
+ {
+ // Set the channels mask to a default value
+ memset( ChannelsMask, 0, sizeof( ChannelsMask ) );
+ // Update the channels mask
+ ChannelsMask[0] = chMask;
+ }
+
+ // Update status variables
+ *drOut = linkAdrParams.Datarate;
+ *txPowOut = linkAdrParams.TxPower;
+ *nbRepOut = linkAdrParams.NbRep;
+ *nbBytesParsed = bytesProcessed;
+
+ return status;
+}
+
+uint8_t RegionKR920RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ uint8_t status = 0x07;
+
+ // Verify radio frequency
+ if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ // Verify datarate
+ if( RegionCommonValueInRange( rxParamSetupReq->Datarate, KR920_RX_MIN_DATARATE, KR920_RX_MAX_DATARATE ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify datarate offset
+ if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, KR920_MIN_RX1_DR_OFFSET, KR920_MAX_RX1_DR_OFFSET ) == false )
+ {
+ status &= 0xFB; // Rx1DrOffset range KO
+ }
+
+ return status;
+}
+
+uint8_t RegionKR920NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+ uint8_t status = 0x03;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ if( newChannelReq->NewChannel->Frequency == 0 )
+ {
+ channelRemove.ChannelId = newChannelReq->ChannelId;
+
+ // Remove
+ if( RegionKR920ChannelsRemove( &channelRemove ) == false )
+ {
+ status &= 0xFC;
+ }
+ }
+ else
+ {
+ channelAdd.NewChannel = newChannelReq->NewChannel;
+ channelAdd.ChannelId = newChannelReq->ChannelId;
+
+ switch( RegionKR920ChannelAdd( &channelAdd ) )
+ {
+ case LORAMAC_STATUS_OK:
+ {
+ break;
+ }
+ case LORAMAC_STATUS_FREQUENCY_INVALID:
+ {
+ status &= 0xFE;
+ break;
+ }
+ case LORAMAC_STATUS_DATARATE_INVALID:
+ {
+ status &= 0xFD;
+ break;
+ }
+ case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
+ {
+ status &= 0xFC;
+ break;
+ }
+ default:
+ {
+ status &= 0xFC;
+ break;
+ }
+ }
+ }
+
+ return status;
+}
+
+int8_t RegionKR920TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+ return -1;
+}
+
+uint8_t RegionKR920DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ uint8_t status = 0x03;
+
+ // Verify if the frequency is supported
+ if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionKR920AlternateDr( AlternateDrParams_t* alternateDr )
+{
+ int8_t datarate = 0;
+
+ if( ( alternateDr->NbTrials % 48 ) == 0 )
+ {
+ datarate = DR_0;
+ }
+ else if( ( alternateDr->NbTrials % 32 ) == 0 )
+ {
+ datarate = DR_1;
+ }
+ else if( ( alternateDr->NbTrials % 24 ) == 0 )
+ {
+ datarate = DR_2;
+ }
+ else if( ( alternateDr->NbTrials % 16 ) == 0 )
+ {
+ datarate = DR_3;
+ }
+ else if( ( alternateDr->NbTrials % 8 ) == 0 )
+ {
+ datarate = DR_4;
+ }
+ else
+ {
+ datarate = DR_5;
+ }
+ return datarate;
+}
+
+void RegionKR920CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = Channels;
+ calcBackOffParams.Bands = Bands;
+ calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+ calcBackOffParams.Joined = calcBackOff->Joined;
+ calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+ calcBackOffParams.Channel = calcBackOff->Channel;
+ calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+ calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+ RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionKR920NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ uint8_t channelNext = 0;
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTx = 0;
+ uint8_t enabledChannels[KR920_MAX_NB_CHANNELS] = { 0 };
+ TimerTime_t nextTxDelay = 0;
+
+ if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+
+ if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, KR920_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ ChannelsMask, Channels,
+ Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ for( uint8_t i = 0, j = randr( 0, nbEnabledChannels - 1 ); i < KR920_MAX_NB_CHANNELS; i++ )
+ {
+ channelNext = enabledChannels[j];
+ j = ( j + 1 ) % nbEnabledChannels;
+
+ // Perform carrier sense for KR920_CARRIER_SENSE_TIME
+ // If the channel is free, we can stop the LBT mechanism
+ if( Radio.IsChannelFree( MODEM_LORA, Channels[channelNext].Frequency, KR920_RSSI_FREE_TH, KR920_CARRIER_SENSE_TIME ) == true )
+ {
+ // Free channel found
+ *channel = channelNext;
+ *time = 0;
+ return true;
+ }
+ }
+ return false;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return true;
+ }
+ // Datarate not supported by any channel, restore defaults
+ ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ *time = 0;
+ return false;
+ }
+}
+
+LoRaMacStatus_t RegionKR920ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ uint8_t band = 0;
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id >= KR920_MAX_NB_CHANNELS )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ // Validate the datarate range
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+ {
+ drInvalid = true;
+ }
+
+ // Default channels don't accept all values
+ if( id < KR920_NUMB_DEFAULT_CHANNELS )
+ {
+ // All datarates are supported
+ // We are not allowed to change the frequency
+ if( channelAdd->NewChannel->Frequency != Channels[id].Frequency )
+ {
+ freqInvalid = true;
+ }
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false )
+ {
+ freqInvalid = true;
+ }
+ }
+
+ // Check status
+ if( ( drInvalid == true ) && ( freqInvalid == true ) )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+ if( drInvalid == true )
+ {
+ return LORAMAC_STATUS_DATARATE_INVALID;
+ }
+ if( freqInvalid == true )
+ {
+ return LORAMAC_STATUS_FREQUENCY_INVALID;
+ }
+
+ memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) );
+ Channels[id].Band = band;
+ ChannelsMask[0] |= ( 1 << id );
+ return LORAMAC_STATUS_OK;
+}
+
+bool RegionKR920ChannelsRemove( ChannelRemoveParams_t* channelRemove )
+{
+ uint8_t id = channelRemove->ChannelId;
+
+ if( id < KR920_NUMB_DEFAULT_CHANNELS )
+ {
+ return false;
+ }
+
+ // Remove the channel from the list of channels
+ Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( ChannelsMask, id, KR920_MAX_NB_CHANNELS );
+}
+
+void RegionKR920SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+ float maxEIRP = GetMaxEIRP( Channels[continuousWave->Channel].Frequency );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+ // Take the minimum between the maxEIRP and continuousWave->MaxEirp.
+ // The value of continuousWave->MaxEirp could have changed during runtime, e.g. due to a MAC command.
+ maxEIRP = MIN( continuousWave->MaxEirp, maxEIRP );
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, maxEIRP, continuousWave->AntennaGain );
+
+ Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionKR920ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ int8_t datarate = dr - drOffset;
+
+ if( datarate < 0 )
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
+
+void RegionKR920RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesKR920;
+ regionCommonRxBeaconSetup.ChannelPlanFrequency = KR920_BEACON_CHANNEL_FREQ;
+ regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = KR920_BEACON_CHANNEL_FREQ;
+ regionCommonRxBeaconSetup.BeaconSize = KR920_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = KR920_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = KR920_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+ regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+ regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup, beaconChannelSet );
+
+ // Store downlink datarate
+ *outDr = KR920_BEACON_CHANNEL_DR;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionKR920.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,568 @@
+/*!
+ * \file RegionKR920.h
+ *
+ * \brief Region definition for KR920
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGIONKR920 Region KR920
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_KR920_H__
+#define __REGION_KR920_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define KR920_MAX_NB_CHANNELS 16
+
+/*!
+ * Number of default channels
+ */
+#define KR920_NUMB_DEFAULT_CHANNELS 3
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define KR920_NUMB_CHANNELS_CF_LIST 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define KR920_TX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define KR920_TX_MAX_DATARATE DR_5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define KR920_RX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define KR920_RX_MAX_DATARATE DR_5
+
+/*!
+ * Default datarate used by the node
+ */
+#define KR920_DEFAULT_DATARATE DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define KR920_MIN_RX1_DR_OFFSET 0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define KR920_MAX_RX1_DR_OFFSET 5
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define KR920_DEFAULT_RX1_DR_OFFSET 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define KR920_MIN_TX_POWER TX_POWER_7
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define KR920_MAX_TX_POWER TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define KR920_DEFAULT_TX_POWER TX_POWER_0
+
+/*!
+ * Default Max EIRP for frequency 920.9 MHz - 921.9 MHz
+ */
+#define KR920_DEFAULT_MAX_EIRP_LOW 10.0f
+
+/*!
+ * Default Max EIRP for frequency 922.1 MHz - 923.3 MHz
+ */
+#define KR920_DEFAULT_MAX_EIRP_HIGH 14.0f
+
+/*!
+ * Default antenna gain
+ */
+#define KR920_DEFAULT_ANTENNA_GAIN 2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define KR920_ADR_ACK_LIMIT 64
+
+/*!
+ * ADR Ack delay
+ */
+#define KR920_ADR_ACK_DELAY 32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define KR920_DUTY_CYCLE_ENABLED 0
+
+/*!
+ * Maximum RX window duration
+ */
+#define KR920_MAX_RX_WINDOW 4000
+
+/*!
+ * Receive delay 1
+ */
+#define KR920_RECEIVE_DELAY1 1000
+
+/*!
+ * Receive delay 2
+ */
+#define KR920_RECEIVE_DELAY2 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define KR920_JOIN_ACCEPT_DELAY1 5000
+
+/*!
+ * Join accept delay 2
+ */
+#define KR920_JOIN_ACCEPT_DELAY2 6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define KR920_MAX_FCNT_GAP 16384
+
+/*!
+ * Ack timeout
+ */
+#define KR920_ACKTIMEOUT 2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define KR920_ACK_TIMEOUT_RND 1000
+
+#if ( KR920_DEFAULT_DATARATE > DR_5 )
+#error "A default DR higher than DR_5 may lead to connectivity loss."
+#endif
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define KR920_RX_WND_2_FREQ 921900000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define KR920_RX_WND_2_DR DR_0
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define KR920_BEACON_INTERVAL 128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define KR920_BEACON_RESERVED 2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define KR920_BEACON_GUARD 3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define KR920_BEACON_WINDOW 122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define KR920_BEACON_WINDOW_SLOTS 4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define KR920_PING_SLOT_WINDOW 30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define KR920_BEACON_SYMBOL_TO_DEFAULT 8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define KR920_BEACON_SYMBOL_TO_EXPANSION_MAX 400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define KR920_PING_SLOT_SYMBOL_TO_EXPANSION_MAX 40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define KR920_BEACON_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define KR920_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define KR920_MAX_BEACON_LESS_PERIOD 7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define KR920_BEACON_DELAY_BEACON_TIMING_ANS 30
+
+/*!
+ * Beacon frequency
+ */
+#define KR920_BEACON_CHANNEL_FREQ 923100000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define KR920_BEACON_SIZE 17
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define KR920_BEACON_CHANNEL_DR DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define KR920_BEACON_CHANNEL_BW 0
+
+/*!
+ * Maximum number of bands
+ */
+#define KR920_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define KR920_BAND0 { 1 , KR920_MAX_TX_POWER, 0, 0 } // 100.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define KR920_LC1 { 922100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define KR920_LC2 { 922300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 3
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define KR920_LC3 { 922500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define KR920_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
+/*!
+ * RSSI threshold for a free channel [dBm]
+ */
+#define KR920_RSSI_FREE_TH -65
+
+/*!
+ * Specifies the time the node performs a carrier sense
+ */
+#define KR920_CARRIER_SENSE_TIME 6
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesKR920[] = { 12, 11, 10, 9, 8, 7 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsKR920[] = { 125000, 125000, 125000, 125000, 125000, 125000 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with and without a repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateKR920[] = { 51, 51, 51, 115, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterKR920[] = { 51, 51, 51, 115, 222, 222 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionKR920GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionKR920SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionKR920InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionKR920Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ * CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionKR920ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionKR920ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionKR920AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ * The receiver will turn on in a [-rxError : +rxError] ms
+ * interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionKR920ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionKR920TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionKR920LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionKR920RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionKR920NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ * Returns -1, if the functionality is not implemented. In this case, the end node
+ * shall not process the command.
+ */
+int8_t RegionKR920TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionKR920DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionKR920AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionKR920CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ * cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionKR920NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionKR920ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionKR920ChannelsRemove( ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionKR920SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionKR920ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONKR920 */
+
+#endif // __REGION_KR920_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionUS915-Hybrid.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,958 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region US915 Hybrid implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionUS915-Hybrid.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 6
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[US915_HYBRID_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[US915_HYBRID_MAX_NB_BANDS] =
+{
+ US915_HYBRID_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels remaining
+ */
+static uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+ uint8_t nextLowerDr = 0;
+
+ if( dr == minDr )
+ {
+ nextLowerDr = minDr;
+ }
+ else
+ {
+ nextLowerDr = dr - 1;
+ }
+ return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+ switch( BandwidthsUS915_HYBRID[drIndex] )
+ {
+ default:
+ case 125000:
+ return 0;
+ case 250000:
+ return 1;
+ case 500000:
+ return 2;
+ }
+}
+
+static void ReenableChannels( uint16_t mask, uint16_t* channelsMask )
+{
+ uint16_t blockMask = mask;
+
+ for( uint8_t i = 0, j = 0; i < 4; i++, j += 2 )
+ {
+ channelsMask[i] = 0;
+ if( ( blockMask & ( 1 << j ) ) != 0 )
+ {
+ channelsMask[i] |= 0x00FF;
+ }
+ if( ( blockMask & ( 1 << ( j + 1 ) ) ) != 0 )
+ {
+ channelsMask[i] |= 0xFF00;
+ }
+ }
+ channelsMask[4] = blockMask;
+ channelsMask[5] = 0x0000;
+}
+
+static uint8_t CountBits( uint16_t mask, uint8_t nbBits )
+{
+ uint8_t nbActiveBits = 0;
+
+ for( uint8_t j = 0; j < nbBits; j++ )
+ {
+ if( ( mask & ( 1 << j ) ) == ( 1 << j ) )
+ {
+ nbActiveBits++;
+ }
+ }
+ return nbActiveBits;
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+ int8_t txPowerResult = txPower;
+
+ // Limit tx power to the band max
+ txPowerResult = MAX( txPower, maxBandTxPower );
+
+ if( datarate == DR_4 )
+ {// Limit tx power to max 26dBm
+ txPowerResult = MAX( txPower, TX_POWER_2 );
+ }
+ else
+ {
+ if( RegionCommonCountChannels( channelsMask, 0, 4 ) < 50 )
+ {// Limit tx power to max 21dBm
+ txPowerResult = MAX( txPower, TX_POWER_5 );
+ }
+ }
+ return txPowerResult;
+}
+
+static bool ValidateChannelsMask( uint16_t* channelsMask )
+{
+ bool chanMaskState = false;
+ uint16_t block1 = 0;
+ uint16_t block2 = 0;
+ uint8_t index = 0;
+ uint16_t channelsMaskCpy[6];
+
+ // Copy channels mask to not change the input
+ for( uint8_t i = 0; i < 4; i++ )
+ {
+ channelsMaskCpy[i] = channelsMask[i];
+ }
+
+ for( uint8_t i = 0; i < 4; i++ )
+ {
+ block1 = channelsMaskCpy[i] & 0x00FF;
+ block2 = channelsMaskCpy[i] & 0xFF00;
+
+ if( CountBits( block1, 16 ) > 5 )
+ {
+ channelsMaskCpy[i] &= block1;
+ channelsMaskCpy[4] = 1 << ( i * 2 );
+ chanMaskState = true;
+ index = i;
+ break;
+ }
+ else if( CountBits( block2, 16 ) > 5 )
+ {
+ channelsMaskCpy[i] &= block2;
+ channelsMaskCpy[4] = 1 << ( i * 2 + 1 );
+ chanMaskState = true;
+ index = i;
+ break;
+ }
+ }
+
+ // Do only change the channel mask, if we have found a valid block.
+ if( chanMaskState == true )
+ {
+ // Copy channels mask back again
+ for( uint8_t i = 0; i < 4; i++ )
+ {
+ channelsMask[i] = channelsMaskCpy[i];
+
+ if( i != index )
+ {
+ channelsMask[i] = 0;
+ }
+ }
+ channelsMask[4] = channelsMaskCpy[4];
+ }
+ return chanMaskState;
+}
+
+static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTransmission = 0;
+
+ for( uint8_t i = 0, k = 0; i < US915_HYBRID_MAX_NB_CHANNELS; i += 16, k++ )
+ {
+ for( uint8_t j = 0; j < 16; j++ )
+ {
+ if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+ {
+ if( channels[i + j].Frequency == 0 )
+ { // Check if the channel is enabled
+ continue;
+ }
+ if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+ channels[i + j].DrRange.Fields.Max ) == false )
+ { // Check if the current channel selection supports the given datarate
+ continue;
+ }
+ if( bands[channels[i + j].Band].TimeOff > 0 )
+ { // Check if the band is available for transmission
+ delayTransmission++;
+ continue;
+ }
+ enabledChannels[nbEnabledChannels++] = i + j;
+ }
+ }
+ }
+
+ *delayTx = delayTransmission;
+ return nbEnabledChannels;
+}
+
+PhyParam_t RegionUS915HybridGetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ phyParam.Value = US915_HYBRID_RX_MIN_DATARATE;
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ phyParam.Value = US915_HYBRID_TX_MIN_DATARATE;
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = US915_HYBRID_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, US915_HYBRID_TX_MIN_DATARATE );
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = US915_HYBRID_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ phyParam.Value = MaxPayloadOfDatarateUS915_HYBRID[getPhy->Datarate];
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterUS915_HYBRID[getPhy->Datarate];
+ break;
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ phyParam.Value = US915_HYBRID_DUTY_CYCLE_ENABLED;
+ break;
+ }
+ case PHY_MAX_RX_WINDOW:
+ {
+ phyParam.Value = US915_HYBRID_MAX_RX_WINDOW;
+ break;
+ }
+ case PHY_RECEIVE_DELAY1:
+ {
+ phyParam.Value = US915_HYBRID_RECEIVE_DELAY1;
+ break;
+ }
+ case PHY_RECEIVE_DELAY2:
+ {
+ phyParam.Value = US915_HYBRID_RECEIVE_DELAY2;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY1:
+ {
+ phyParam.Value = US915_HYBRID_JOIN_ACCEPT_DELAY1;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY2:
+ {
+ phyParam.Value = US915_HYBRID_JOIN_ACCEPT_DELAY2;
+ break;
+ }
+ case PHY_MAX_FCNT_GAP:
+ {
+ phyParam.Value = US915_HYBRID_MAX_FCNT_GAP;
+ break;
+ }
+ case PHY_ACK_TIMEOUT:
+ {
+ phyParam.Value = ( US915_HYBRID_ACKTIMEOUT + randr( -US915_HYBRID_ACK_TIMEOUT_RND, US915_HYBRID_ACK_TIMEOUT_RND ) );
+ break;
+ }
+ case PHY_DEF_DR1_OFFSET:
+ {
+ phyParam.Value = US915_HYBRID_DEFAULT_RX1_DR_OFFSET;
+ break;
+ }
+ case PHY_DEF_RX2_FREQUENCY:
+ {
+ phyParam.Value = US915_HYBRID_RX_WND_2_FREQ;
+ break;
+ }
+ case PHY_DEF_RX2_DR:
+ {
+ phyParam.Value = US915_HYBRID_RX_WND_2_DR;
+ break;
+ }
+ case PHY_CHANNELS_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = US915_HYBRID_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = Channels;
+ break;
+ }
+ case PHY_DEF_UPLINK_DWELL_TIME:
+ case PHY_DEF_DOWNLINK_DWELL_TIME:
+ {
+ phyParam.Value = 0;
+ break;
+ }
+ case PHY_DEF_MAX_EIRP:
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = 0;
+ break;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ case PHY_DEF_NB_JOIN_TRIALS:
+ {
+ phyParam.Value = 2;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionUS915HybridSetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionUS915HybridInitDefaults( InitType_t type )
+{
+ switch( type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Channels
+ // 125 kHz channels
+ for( uint8_t i = 0; i < US915_HYBRID_MAX_NB_CHANNELS - 8; i++ )
+ {
+ Channels[i].Frequency = 902300000 + i * 200000;
+ Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0;
+ Channels[i].Band = 0;
+ }
+ // 500 kHz channels
+ for( uint8_t i = US915_HYBRID_MAX_NB_CHANNELS - 8; i < US915_HYBRID_MAX_NB_CHANNELS; i++ )
+ {
+ Channels[i].Frequency = 903000000 + ( i - ( US915_HYBRID_MAX_NB_CHANNELS - 8 ) ) * 1600000;
+ Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4;
+ Channels[i].Band = 0;
+ }
+
+ // ChannelsMask
+ ChannelsDefaultMask[0] = 0x00FF;
+ ChannelsDefaultMask[1] = 0x0000;
+ ChannelsDefaultMask[2] = 0x0000;
+ ChannelsDefaultMask[3] = 0x0000;
+ ChannelsDefaultMask[4] = 0x0001;
+ ChannelsDefaultMask[5] = 0x0000;
+
+ // Copy channels default mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+
+ // Copy into channels mask remaining
+ RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 );
+ break;
+ }
+ case INIT_TYPE_RESTORE:
+ {
+ ReenableChannels( ChannelsDefaultMask[4], ChannelsMask );
+
+ for( uint8_t i = 0; i < 6; i++ )
+ { // Copy-And the channels mask
+ ChannelsMaskRemaining[i] &= ChannelsMask[i];
+ }
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionUS915HybridVerify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_HYBRID_TX_MIN_DATARATE, US915_HYBRID_TX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+ }
+ case PHY_RX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_HYBRID_RX_MIN_DATARATE, US915_HYBRID_RX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_POWER:
+ case PHY_TX_POWER:
+ {
+ // Remark: switched min and max!
+ return RegionCommonValueInRange( verify->TxPower, US915_HYBRID_MAX_TX_POWER, US915_HYBRID_MIN_TX_POWER );
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ return US915_HYBRID_DUTY_CYCLE_ENABLED;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ {
+ if( verify->NbJoinTrials < 2 )
+ {
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+void RegionUS915HybridApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ return;
+}
+
+bool RegionUS915HybridChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ uint8_t nbChannels = RegionCommonCountChannels( chanMaskSet->ChannelsMaskIn, 0, 4 );
+
+ // Check the number of active channels
+ if( ( nbChannels < 2 ) &&
+ ( nbChannels > 0 ) )
+ {
+ return false;
+ }
+
+ // Validate the channels mask
+ if( ValidateChannelsMask( chanMaskSet->ChannelsMaskIn ) == false )
+ {
+ return false;
+ }
+
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 );
+
+ for( uint8_t i = 0; i < 6; i++ )
+ { // Copy-And the channels mask
+ ChannelsMaskRemaining[i] &= ChannelsMask[i];
+ }
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegionUS915HybridAdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ bool adrAckReq = false;
+ int8_t datarate = adrNext->Datarate;
+ int8_t txPower = adrNext->TxPower;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Report back the adr ack counter
+ *adrAckCounter = adrNext->AdrAckCounter;
+
+ if( adrNext->AdrEnabled == true )
+ {
+ if( datarate == US915_HYBRID_TX_MIN_DATARATE )
+ {
+ *adrAckCounter = 0;
+ adrAckReq = false;
+ }
+ else
+ {
+ if( adrNext->AdrAckCounter >= US915_HYBRID_ADR_ACK_LIMIT )
+ {
+ adrAckReq = true;
+ txPower = US915_HYBRID_MAX_TX_POWER;
+ }
+ else
+ {
+ adrAckReq = false;
+ }
+ if( adrNext->AdrAckCounter >= ( US915_HYBRID_ADR_ACK_LIMIT + US915_HYBRID_ADR_ACK_DELAY ) )
+ {
+ if( ( adrNext->AdrAckCounter % US915_HYBRID_ADR_ACK_DELAY ) == 1 )
+ {
+ // Decrease the datarate
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.Datarate = datarate;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionUS915HybridGetPhyParam( &getPhy );
+ datarate = phyParam.Value;
+
+ if( datarate == US915_HYBRID_TX_MIN_DATARATE )
+ {
+ // We must set adrAckReq to false as soon as we reach the lowest datarate
+ adrAckReq = false;
+ if( adrNext->UpdateChanMask == true )
+ {
+ // Re-enable default channels
+ ReenableChannels( ChannelsMask[4], ChannelsMask );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *drOut = datarate;
+ *txPowOut = txPower;
+ return adrAckReq;
+}
+
+void RegionUS915HybridComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+ double tSymbol = 0.0;
+
+ // Get the datarate, perform a boundary check
+ rxConfigParams->Datarate = MIN( datarate, US915_HYBRID_RX_MAX_DATARATE );
+ rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+ tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesUS915_HYBRID[rxConfigParams->Datarate], BandwidthsUS915_HYBRID[rxConfigParams->Datarate] );
+
+ RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionUS915HybridRxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+ int8_t dr = rxConfig->Datarate;
+ uint8_t maxPayload = 0;
+ int8_t phyDr = 0;
+ uint32_t frequency = rxConfig->Frequency;
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->Window == 0 )
+ {
+ // Apply window 1 frequency
+ frequency = US915_HYBRID_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_HYBRID_STEPWIDTH_RX1_CHANNEL;
+ }
+
+ // Read the physical datarate from the datarates table
+ phyDr = DataratesUS915_HYBRID[dr];
+
+ Radio.SetChannel( frequency );
+
+ // Radio configuration
+ Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+
+ if( rxConfig->RepeaterSupport == true )
+ {
+ maxPayload = MaxPayloadOfDatarateRepeaterUS915_HYBRID[dr];
+ }
+ else
+ {
+ maxPayload = MaxPayloadOfDatarateUS915_HYBRID[dr];
+ }
+ Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+ *datarate = (uint8_t) dr;
+ return true;
+}
+
+bool RegionUS915HybridTxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ int8_t phyDr = DataratesUS915_HYBRID[txConfig->Datarate];
+ int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+ uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+ int8_t phyTxPower = 0;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_HYBRID_DEFAULT_MAX_ERP, 0 );
+
+ // Setup the radio frequency
+ Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+ Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+
+ // Setup maximum payload lenght of the radio driver
+ Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen );
+ // Get the time-on-air of the next tx frame
+ *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen );
+ *txPower = txPowerLimited;
+
+ return true;
+}
+
+uint8_t RegionUS915HybridLinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+ uint8_t status = 0x07;
+ RegionCommonLinkAdrParams_t linkAdrParams;
+ uint8_t nextIndex = 0;
+ uint8_t bytesProcessed = 0;
+ uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 };
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+ // Initialize local copy of channels mask
+ RegionCommonChanMaskCopy( channelsMask, ChannelsMask, 6 );
+
+ while( bytesProcessed < linkAdrReq->PayloadSize )
+ {
+ nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+ if( nextIndex == 0 )
+ break; // break loop, since no more request has been found
+
+ // Update bytes processed
+ bytesProcessed += nextIndex;
+
+ // Revert status, as we only check the last ADR request for the channel mask KO
+ status = 0x07;
+
+ if( linkAdrParams.ChMaskCtrl == 6 )
+ {
+ // Enable all 125 kHz channels
+ channelsMask[0] = 0xFFFF;
+ channelsMask[1] = 0xFFFF;
+ channelsMask[2] = 0xFFFF;
+ channelsMask[3] = 0xFFFF;
+ // Apply chMask to channels 64 to 71
+ channelsMask[4] = linkAdrParams.ChMask;
+ }
+ else if( linkAdrParams.ChMaskCtrl == 7 )
+ {
+ // Disable all 125 kHz channels
+ channelsMask[0] = 0x0000;
+ channelsMask[1] = 0x0000;
+ channelsMask[2] = 0x0000;
+ channelsMask[3] = 0x0000;
+ // Apply chMask to channels 64 to 71
+ channelsMask[4] = linkAdrParams.ChMask;
+ }
+ else if( linkAdrParams.ChMaskCtrl == 5 )
+ {
+ // RFU
+ status &= 0xFE; // Channel mask KO
+ }
+ else
+ {
+ channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask;
+ }
+ }
+
+ // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels
+ if( ( linkAdrParams.Datarate < DR_4 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) )
+ {
+ status &= 0xFE; // Channel mask KO
+ }
+
+ if( ValidateChannelsMask( channelsMask ) == false )
+ {
+ status &= 0xFE; // Channel mask KO
+ }
+
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+ phyParam = RegionUS915HybridGetPhyParam( &getPhy );
+
+ linkAdrVerifyParams.Status = status;
+ linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+ linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+ linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+ linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+ linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+ linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+ linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+ linkAdrVerifyParams.NbChannels = US915_HYBRID_MAX_NB_CHANNELS;
+ linkAdrVerifyParams.ChannelsMask = channelsMask;
+ linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+ linkAdrVerifyParams.MaxDatarate = US915_HYBRID_TX_MAX_DATARATE;
+ linkAdrVerifyParams.Channels = Channels;
+ linkAdrVerifyParams.MinTxPower = US915_HYBRID_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = US915_HYBRID_MAX_TX_POWER;
+
+ // Verify the parameters and update, if necessary
+ status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+ // Update channelsMask if everything is correct
+ if( status == 0x07 )
+ {
+ // Copy Mask
+ RegionCommonChanMaskCopy( ChannelsMask, channelsMask, 6 );
+
+ ChannelsMaskRemaining[0] &= ChannelsMask[0];
+ ChannelsMaskRemaining[1] &= ChannelsMask[1];
+ ChannelsMaskRemaining[2] &= ChannelsMask[2];
+ ChannelsMaskRemaining[3] &= ChannelsMask[3];
+ ChannelsMaskRemaining[4] = ChannelsMask[4];
+ ChannelsMaskRemaining[5] = ChannelsMask[5];
+ }
+
+ // Update status variables
+ *drOut = linkAdrParams.Datarate;
+ *txPowOut = linkAdrParams.TxPower;
+ *nbRepOut = linkAdrParams.NbRep;
+ *nbBytesParsed = bytesProcessed;
+
+ return status;
+}
+
+uint8_t RegionUS915HybridRxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ uint8_t status = 0x07;
+ uint32_t freq = rxParamSetupReq->Frequency;
+
+ // Verify radio frequency
+ if( ( Radio.CheckRfFrequency( freq ) == false ) ||
+ ( freq < US915_HYBRID_FIRST_RX1_CHANNEL ) ||
+ ( freq > US915_HYBRID_LAST_RX1_CHANNEL ) ||
+ ( ( ( freq - ( uint32_t ) US915_HYBRID_FIRST_RX1_CHANNEL ) % ( uint32_t ) US915_HYBRID_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ // Verify datarate
+ if( RegionCommonValueInRange( rxParamSetupReq->Datarate, US915_HYBRID_RX_MIN_DATARATE, US915_HYBRID_RX_MAX_DATARATE ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+ if( ( RegionCommonValueInRange( rxParamSetupReq->Datarate, DR_5, DR_7 ) == true ) ||
+ ( rxParamSetupReq->Datarate > DR_13 ) )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify datarate offset
+ if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, US915_HYBRID_MIN_RX1_DR_OFFSET, US915_HYBRID_MAX_RX1_DR_OFFSET ) == false )
+ {
+ status &= 0xFB; // Rx1DrOffset range KO
+ }
+
+ return status;
+}
+
+uint8_t RegionUS915HybridNewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+ // Datarate and frequency KO
+ return 0;
+}
+
+int8_t RegionUS915HybridTxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+ return -1;
+}
+
+uint8_t RegionUS915HybridDlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ return 0;
+}
+
+int8_t RegionUS915HybridAlternateDr( AlternateDrParams_t* alternateDr )
+{
+ int8_t datarate = 0;
+
+ // Re-enable 500 kHz default channels
+ ReenableChannels( ChannelsMask[4], ChannelsMask );
+
+ if( ( alternateDr->NbTrials & 0x01 ) == 0x01 )
+ {
+ datarate = DR_4;
+ }
+ else
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
+
+void RegionUS915HybridCalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = Channels;
+ calcBackOffParams.Bands = Bands;
+ calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+ calcBackOffParams.Joined = calcBackOff->Joined;
+ calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+ calcBackOffParams.Channel = calcBackOff->Channel;
+ calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+ calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+ RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionUS915HybridNextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTx = 0;
+ uint8_t enabledChannels[US915_HYBRID_MAX_NB_CHANNELS] = { 0 };
+ TimerTime_t nextTxDelay = 0;
+
+ // Count 125kHz channels
+ if( RegionCommonCountChannels( ChannelsMaskRemaining, 0, 4 ) == 0 )
+ { // Reactivate default channels
+ RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 4 );
+ }
+ // Check other channels
+ if( nextChanParams->Datarate >= DR_4 )
+ {
+ if( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 )
+ {
+ ChannelsMaskRemaining[4] = ChannelsMask[4];
+ }
+ }
+
+ if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, US915_HYBRID_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate,
+ ChannelsMaskRemaining, Channels,
+ Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+ // Disable the channel in the mask
+ RegionCommonChanDisable( ChannelsMaskRemaining, *channel, US915_HYBRID_MAX_NB_CHANNELS - 8 );
+
+ *time = 0;
+ return true;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return true;
+ }
+ // Datarate not supported by any channel
+ *time = 0;
+ return false;
+ }
+}
+
+LoRaMacStatus_t RegionUS915HybridChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+bool RegionUS915HybridChannelsRemove( ChannelRemoveParams_t* channelRemove )
+{
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+void RegionUS915HybridSetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_HYBRID_DEFAULT_MAX_ERP, 0 );
+
+ Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionUS915HybridApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ int8_t datarate = DatarateOffsetsUS915_HYBRID[dr][drOffset];
+
+ if( datarate < 0 )
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionUS915-Hybrid.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,448 @@
+/*!
+ * \file RegionUS915Hybrid-Hybrid.h
+ *
+ * \brief Region definition for US915
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGIONUS915HYB Region US915 in hybrid mode
+ * This is a hybrid implementation for US915, supporting 16 uplink channels only.
+ * \{
+ */
+#ifndef __REGION_US915_HYBRID_H__
+#define __REGION_US915_HYBRID_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define US915_HYBRID_MAX_NB_CHANNELS 72
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define US915_HYBRID_TX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define US915_HYBRID_TX_MAX_DATARATE DR_4
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define US915_HYBRID_RX_MIN_DATARATE DR_8
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define US915_HYBRID_RX_MAX_DATARATE DR_13
+
+/*!
+ * Default datarate used by the node
+ */
+#define US915_HYBRID_DEFAULT_DATARATE DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define US915_HYBRID_MIN_RX1_DR_OFFSET 0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define US915_HYBRID_MAX_RX1_DR_OFFSET 3
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define US915_HYBRID_DEFAULT_RX1_DR_OFFSET 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define US915_HYBRID_MIN_TX_POWER TX_POWER_10
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define US915_HYBRID_MAX_TX_POWER TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define US915_HYBRID_DEFAULT_TX_POWER TX_POWER_0
+
+/*!
+ * Default Max ERP
+ */
+#define US915_HYBRID_DEFAULT_MAX_ERP 30.0f
+
+/*!
+ * ADR Ack limit
+ */
+#define US915_HYBRID_ADR_ACK_LIMIT 64
+
+/*!
+ * ADR Ack delay
+ */
+#define US915_HYBRID_ADR_ACK_DELAY 32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define US915_HYBRID_DUTY_CYCLE_ENABLED 0
+
+/*!
+ * Maximum RX window duration
+ */
+#define US915_HYBRID_MAX_RX_WINDOW 3000
+
+/*!
+ * Receive delay 1
+ */
+#define US915_HYBRID_RECEIVE_DELAY1 1000
+
+/*!
+ * Receive delay 2
+ */
+#define US915_HYBRID_RECEIVE_DELAY2 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define US915_HYBRID_JOIN_ACCEPT_DELAY1 5000
+
+/*!
+ * Join accept delay 2
+ */
+#define US915_HYBRID_JOIN_ACCEPT_DELAY2 6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define US915_HYBRID_MAX_FCNT_GAP 16384
+
+/*!
+ * Ack timeout
+ */
+#define US915_HYBRID_ACKTIMEOUT 2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define US915_HYBRID_ACK_TIMEOUT_RND 1000
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define US915_HYBRID_RX_WND_2_FREQ 923300000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define US915_HYBRID_RX_WND_2_DR DR_8
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define US915_HYBRID_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define US915_HYBRID_BAND0 { 1, US915_HYBRID_MAX_TX_POWER, 0, 0 } // 100.0 %
+
+/*!
+ * Defines the first channel for RX window 1 for US band
+ */
+#define US915_HYBRID_FIRST_RX1_CHANNEL ( (uint32_t) 923300000 )
+
+/*!
+ * Defines the last channel for RX window 1 for US band
+ */
+#define US915_HYBRID_LAST_RX1_CHANNEL ( (uint32_t) 927500000 )
+
+/*!
+ * Defines the step width of the channels for RX window 1
+ */
+#define US915_HYBRID_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600000 )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesUS915_HYBRID[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsUS915_HYBRID[] = { 125000, 125000, 125000, 125000, 500000, 0, 0, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 };
+
+/*!
+ * Up/Down link data rates offset definition
+ */
+static const int8_t DatarateOffsetsUS915_HYBRID[5][4] =
+{
+ { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0
+ { DR_11, DR_10, DR_9 , DR_8 }, // DR_1
+ { DR_12, DR_11, DR_10, DR_9 }, // DR_2
+ { DR_13, DR_12, DR_11, DR_10 }, // DR_3
+ { DR_13, DR_13, DR_12, DR_11 }, // DR_4
+};
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateUS915_HYBRID[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterUS915_HYBRID[] = { 11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionUS915HybridGetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionUS915HybridSetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionUS915HybridInitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionUS915HybridVerify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ * CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionUS915HybridApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionUS915HybridChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionUS915HybridAdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ * The receiver will turn on in a [-rxError : +rxError] ms
+ * interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionUS915HybridComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionUS915HybridRxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionUS915HybridTxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915HybridLinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915HybridRxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915HybridNewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ * Returns -1, if the functionality is not implemented. In this case, the end node
+ * shall not process the command.
+ */
+int8_t RegionUS915HybridTxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915HybridDlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionUS915HybridAlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionUS915HybridCalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ * cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionUS915HybridNextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionUS915HybridChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionUS915HybridChannelsRemove( ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionUS915HybridSetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionUS915HybridApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONUS915HYB */
+
+#endif // __REGION_US915_HYBRID_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionUS915.cpp Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,976 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region US915 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionUS915.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 6
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[US915_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[US915_MAX_NB_BANDS] =
+{
+ US915_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels remaining
+ */
+static uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+ uint8_t nextLowerDr = 0;
+
+ if( dr == minDr )
+ {
+ nextLowerDr = minDr;
+ }
+ else
+ {
+ nextLowerDr = dr - 1;
+ }
+ return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+ switch( BandwidthsUS915[drIndex] )
+ {
+ default:
+ case 125000:
+ return 0;
+ case 250000:
+ return 1;
+ case 500000:
+ return 2;
+ }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+ int8_t txPowerResult = txPower;
+
+ // Limit tx power to the band max
+ txPowerResult = MAX( txPower, maxBandTxPower );
+
+ if( datarate == DR_4 )
+ {// Limit tx power to max 26dBm
+ txPowerResult = MAX( txPower, TX_POWER_2 );
+ }
+ else
+ {
+ if( RegionCommonCountChannels( channelsMask, 0, 4 ) < 50 )
+ {// Limit tx power to max 21dBm
+ txPowerResult = MAX( txPower, TX_POWER_5 );
+ }
+ }
+ return txPowerResult;
+}
+
+static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTransmission = 0;
+
+ for( uint8_t i = 0, k = 0; i < US915_MAX_NB_CHANNELS; i += 16, k++ )
+ {
+ for( uint8_t j = 0; j < 16; j++ )
+ {
+ if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+ {
+ if( channels[i + j].Frequency == 0 )
+ { // Check if the channel is enabled
+ continue;
+ }
+ if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+ channels[i + j].DrRange.Fields.Max ) == false )
+ { // Check if the current channel selection supports the given datarate
+ continue;
+ }
+ if( bands[channels[i + j].Band].TimeOff > 0 )
+ { // Check if the band is available for transmission
+ delayTransmission++;
+ continue;
+ }
+ enabledChannels[nbEnabledChannels++] = i + j;
+ }
+ }
+ }
+
+ *delayTx = delayTransmission;
+ return nbEnabledChannels;
+}
+
+static uint8_t BeaconChannel( uint32_t devAddr, TimerTime_t beaconTime, TimerTime_t beaconInterval )
+{
+ uint32_t frequency = 0;
+
+ frequency = devAddr + ( beaconTime / ( beaconInterval / 1000 ) );
+
+ return ( ( uint8_t )( frequency % 8 ) );
+}
+
+PhyParam_t RegionUS915GetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ phyParam.Value = US915_RX_MIN_DATARATE;
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ phyParam.Value = US915_TX_MIN_DATARATE;
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = US915_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, US915_TX_MIN_DATARATE );
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = US915_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ phyParam.Value = MaxPayloadOfDatarateUS915[getPhy->Datarate];
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterUS915[getPhy->Datarate];
+ break;
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ phyParam.Value = US915_DUTY_CYCLE_ENABLED;
+ break;
+ }
+ case PHY_MAX_RX_WINDOW:
+ {
+ phyParam.Value = US915_MAX_RX_WINDOW;
+ break;
+ }
+ case PHY_RECEIVE_DELAY1:
+ {
+ phyParam.Value = US915_RECEIVE_DELAY1;
+ break;
+ }
+ case PHY_RECEIVE_DELAY2:
+ {
+ phyParam.Value = US915_RECEIVE_DELAY2;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY1:
+ {
+ phyParam.Value = US915_JOIN_ACCEPT_DELAY1;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY2:
+ {
+ phyParam.Value = US915_JOIN_ACCEPT_DELAY2;
+ break;
+ }
+ case PHY_MAX_FCNT_GAP:
+ {
+ phyParam.Value = US915_MAX_FCNT_GAP;
+ break;
+ }
+ case PHY_ACK_TIMEOUT:
+ {
+ phyParam.Value = ( US915_ACKTIMEOUT + randr( -US915_ACK_TIMEOUT_RND, US915_ACK_TIMEOUT_RND ) );
+ break;
+ }
+ case PHY_DEF_DR1_OFFSET:
+ {
+ phyParam.Value = US915_DEFAULT_RX1_DR_OFFSET;
+ break;
+ }
+ case PHY_DEF_RX2_FREQUENCY:
+ {
+ phyParam.Value = US915_RX_WND_2_FREQ;
+ break;
+ }
+ case PHY_DEF_RX2_DR:
+ {
+ phyParam.Value = US915_RX_WND_2_DR;
+ break;
+ }
+ case PHY_CHANNELS_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = US915_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = Channels;
+ break;
+ }
+ case PHY_DEF_UPLINK_DWELL_TIME:
+ case PHY_DEF_DOWNLINK_DWELL_TIME:
+ {
+ phyParam.Value = 0;
+ break;
+ }
+ case PHY_DEF_MAX_EIRP:
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = 0;
+ break;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ case PHY_DEF_NB_JOIN_TRIALS:
+ {
+ phyParam.Value = 2;
+ break;
+ }
+ case PHY_BEACON_INTERVAL:
+ {
+ phyParam.Value = US915_BEACON_INTERVAL;
+ break;
+ }
+ case PHY_BEACON_RESERVED:
+ {
+ phyParam.Value = US915_BEACON_RESERVED;
+ break;
+ }
+ case PHY_BEACON_GUARD:
+ {
+ phyParam.Value = US915_BEACON_GUARD;
+ break;
+ }
+ case PHY_BEACON_WINDOW:
+ {
+ phyParam.Value = US915_BEACON_WINDOW;
+ break;
+ }
+ case PHY_BEACON_WINDOW_SLOTS:
+ {
+ phyParam.Value = US915_BEACON_WINDOW_SLOTS;
+ break;
+ }
+ case PHY_PING_SLOT_WINDOW:
+ {
+ phyParam.Value = US915_PING_SLOT_WINDOW;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_DEFAULT:
+ {
+ phyParam.Value = US915_BEACON_SYMBOL_TO_DEFAULT;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = US915_BEACON_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+ {
+ phyParam.Value = US915_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+ break;
+ }
+ case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = US915_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+ {
+ phyParam.Value = US915_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+ break;
+ }
+ case PHY_MAX_BEACON_LESS_PERIOD:
+ {
+ phyParam.Value = US915_MAX_BEACON_LESS_PERIOD;
+ break;
+ }
+ case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+ {
+ phyParam.Value = US915_BEACON_DELAY_BEACON_TIMING_ANS;
+ break;
+ }
+ case PHY_PINGSLOT_CHANNEL_FREQ:
+ {
+ phyParam.Value = US915_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_SIZE:
+ {
+ phyParam.Value = US915_BEACON_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = US915_BEACON_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionUS915SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionUS915InitDefaults( InitType_t type )
+{
+ switch( type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Channels
+ // 125 kHz channels
+ for( uint8_t i = 0; i < US915_MAX_NB_CHANNELS - 8; i++ )
+ {
+ Channels[i].Frequency = 902300000 + i * 200000;
+ Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0;
+ Channels[i].Band = 0;
+ }
+ // 500 kHz channels
+ for( uint8_t i = US915_MAX_NB_CHANNELS - 8; i < US915_MAX_NB_CHANNELS; i++ )
+ {
+ Channels[i].Frequency = 903000000 + ( i - ( US915_MAX_NB_CHANNELS - 8 ) ) * 1600000;
+ Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4;
+ Channels[i].Band = 0;
+ }
+
+ // ChannelsMask
+ ChannelsDefaultMask[0] = 0xFFFF;
+ ChannelsDefaultMask[1] = 0xFFFF;
+ ChannelsDefaultMask[2] = 0xFFFF;
+ ChannelsDefaultMask[3] = 0xFFFF;
+ ChannelsDefaultMask[4] = 0x00FF;
+ ChannelsDefaultMask[5] = 0x0000;
+
+ // Copy channels default mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+
+ // Copy into channels mask remaining
+ RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 );
+ break;
+ }
+ case INIT_TYPE_RESTORE:
+ {
+ // Copy channels default mask
+ RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+
+ for( uint8_t i = 0; i < 6; i++ )
+ { // Copy-And the channels mask
+ ChannelsMaskRemaining[i] &= ChannelsMask[i];
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool RegionUS915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_TX_MIN_DATARATE, US915_TX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+ }
+ case PHY_RX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_POWER:
+ case PHY_TX_POWER:
+ {
+ // Remark: switched min and max!
+ return RegionCommonValueInRange( verify->TxPower, US915_MAX_TX_POWER, US915_MIN_TX_POWER );
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ return US915_DUTY_CYCLE_ENABLED;
+ }
+ case PHY_NB_JOIN_TRIALS:
+ {
+ if( verify->NbJoinTrials < 2 )
+ {
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+void RegionUS915ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ return;
+}
+
+bool RegionUS915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ uint8_t nbChannels = RegionCommonCountChannels( chanMaskSet->ChannelsMaskIn, 0, 4 );
+
+ // Check the number of active channels
+ if( ( nbChannels < 2 ) &&
+ ( nbChannels > 0 ) )
+ {
+ return false;
+ }
+
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 );
+
+ for( uint8_t i = 0; i < 6; i++ )
+ { // Copy-And the channels mask
+ ChannelsMaskRemaining[i] &= ChannelsMask[i];
+ }
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegionUS915AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ bool adrAckReq = false;
+ int8_t datarate = adrNext->Datarate;
+ int8_t txPower = adrNext->TxPower;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Report back the adr ack counter
+ *adrAckCounter = adrNext->AdrAckCounter;
+
+ if( adrNext->AdrEnabled == true )
+ {
+ if( datarate == US915_TX_MIN_DATARATE )
+ {
+ *adrAckCounter = 0;
+ adrAckReq = false;
+ }
+ else
+ {
+ if( adrNext->AdrAckCounter >= US915_ADR_ACK_LIMIT )
+ {
+ adrAckReq = true;
+ txPower = US915_MAX_TX_POWER;
+ }
+ else
+ {
+ adrAckReq = false;
+ }
+ if( adrNext->AdrAckCounter >= ( US915_ADR_ACK_LIMIT + US915_ADR_ACK_DELAY ) )
+ {
+ if( ( adrNext->AdrAckCounter % US915_ADR_ACK_DELAY ) == 1 )
+ {
+ // Decrease the datarate
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.Datarate = datarate;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionUS915GetPhyParam( &getPhy );
+ datarate = phyParam.Value;
+
+ if( datarate == US915_TX_MIN_DATARATE )
+ {
+ // We must set adrAckReq to false as soon as we reach the lowest datarate
+ adrAckReq = false;
+ if( adrNext->UpdateChanMask == true )
+ {
+ // Re-enable default channels
+ ChannelsMask[0] = 0xFFFF;
+ ChannelsMask[1] = 0xFFFF;
+ ChannelsMask[2] = 0xFFFF;
+ ChannelsMask[3] = 0xFFFF;
+ ChannelsMask[4] = 0x00FF;
+ ChannelsMask[5] = 0x0000;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *drOut = datarate;
+ *txPowOut = txPower;
+ return adrAckReq;
+}
+
+void RegionUS915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+ double tSymbol = 0.0;
+
+ // Get the datarate, perform a boundary check
+ rxConfigParams->Datarate = MIN( datarate, US915_RX_MAX_DATARATE );
+ rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+ tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesUS915[rxConfigParams->Datarate], BandwidthsUS915[rxConfigParams->Datarate] );
+
+ RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+ int8_t dr = rxConfig->Datarate;
+ uint8_t maxPayload = 0;
+ int8_t phyDr = 0;
+ uint32_t frequency = rxConfig->Frequency;
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->Window == 0 )
+ {
+ // Apply window 1 frequency
+ frequency = US915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_STEPWIDTH_RX1_CHANNEL;
+ }
+
+ // Read the physical datarate from the datarates table
+ phyDr = DataratesUS915[dr];
+
+ Radio.SetChannel( frequency );
+
+ // Radio configuration
+ Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+
+ if( rxConfig->RepeaterSupport == true )
+ {
+ maxPayload = MaxPayloadOfDatarateRepeaterUS915[dr];
+ }
+ else
+ {
+ maxPayload = MaxPayloadOfDatarateUS915[dr];
+ }
+ Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+ *datarate = (uint8_t) dr;
+ return true;
+}
+
+bool RegionUS915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ int8_t phyDr = DataratesUS915[txConfig->Datarate];
+ int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+ uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+ int8_t phyTxPower = 0;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 );
+
+ // Setup the radio frequency
+ Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+ Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+
+ // Setup maximum payload lenght of the radio driver
+ Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen );
+ // Get the time-on-air of the next tx frame
+ *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen );
+ *txPower = txPowerLimited;
+
+ return true;
+}
+
+uint8_t RegionUS915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+ uint8_t status = 0x07;
+ RegionCommonLinkAdrParams_t linkAdrParams;
+ uint8_t nextIndex = 0;
+ uint8_t bytesProcessed = 0;
+ uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 };
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+ // Initialize local copy of channels mask
+ RegionCommonChanMaskCopy( channelsMask, ChannelsMask, 6 );
+
+ while( bytesProcessed < linkAdrReq->PayloadSize )
+ {
+ nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+ if( nextIndex == 0 )
+ break; // break loop, since no more request has been found
+
+ // Update bytes processed
+ bytesProcessed += nextIndex;
+
+ // Revert status, as we only check the last ADR request for the channel mask KO
+ status = 0x07;
+
+ if( linkAdrParams.ChMaskCtrl == 6 )
+ {
+ // Enable all 125 kHz channels
+ channelsMask[0] = 0xFFFF;
+ channelsMask[1] = 0xFFFF;
+ channelsMask[2] = 0xFFFF;
+ channelsMask[3] = 0xFFFF;
+ // Apply chMask to channels 64 to 71
+ channelsMask[4] = linkAdrParams.ChMask;
+ }
+ else if( linkAdrParams.ChMaskCtrl == 7 )
+ {
+ // Disable all 125 kHz channels
+ channelsMask[0] = 0x0000;
+ channelsMask[1] = 0x0000;
+ channelsMask[2] = 0x0000;
+ channelsMask[3] = 0x0000;
+ // Apply chMask to channels 64 to 71
+ channelsMask[4] = linkAdrParams.ChMask;
+ }
+ else if( linkAdrParams.ChMaskCtrl == 5 )
+ {
+ // RFU
+ status &= 0xFE; // Channel mask KO
+ }
+ else
+ {
+ channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask;
+ }
+ }
+
+ // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels
+ if( ( linkAdrParams.Datarate < DR_4 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) )
+ {
+ status &= 0xFE; // Channel mask KO
+ }
+
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+ phyParam = RegionUS915GetPhyParam( &getPhy );
+
+ linkAdrVerifyParams.Status = status;
+ linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+ linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+ linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+ linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+ linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+ linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+ linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+ linkAdrVerifyParams.NbChannels = US915_MAX_NB_CHANNELS;
+ linkAdrVerifyParams.ChannelsMask = channelsMask;
+ linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+ linkAdrVerifyParams.MaxDatarate = US915_TX_MAX_DATARATE;
+ linkAdrVerifyParams.Channels = Channels;
+ linkAdrVerifyParams.MinTxPower = US915_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = US915_MAX_TX_POWER;
+
+ // Verify the parameters and update, if necessary
+ status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+ // Update channelsMask if everything is correct
+ if( status == 0x07 )
+ {
+ // Copy Mask
+ RegionCommonChanMaskCopy( ChannelsMask, channelsMask, 6 );
+
+ ChannelsMaskRemaining[0] &= ChannelsMask[0];
+ ChannelsMaskRemaining[1] &= ChannelsMask[1];
+ ChannelsMaskRemaining[2] &= ChannelsMask[2];
+ ChannelsMaskRemaining[3] &= ChannelsMask[3];
+ ChannelsMaskRemaining[4] = ChannelsMask[4];
+ ChannelsMaskRemaining[5] = ChannelsMask[5];
+ }
+
+ // Update status variables
+ *drOut = linkAdrParams.Datarate;
+ *txPowOut = linkAdrParams.TxPower;
+ *nbRepOut = linkAdrParams.NbRep;
+ *nbBytesParsed = bytesProcessed;
+
+ return status;
+}
+
+uint8_t RegionUS915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ uint8_t status = 0x07;
+ uint32_t freq = rxParamSetupReq->Frequency;
+
+ // Verify radio frequency
+ if( ( Radio.CheckRfFrequency( freq ) == false ) ||
+ ( freq < US915_FIRST_RX1_CHANNEL ) ||
+ ( freq > US915_LAST_RX1_CHANNEL ) ||
+ ( ( ( freq - ( uint32_t ) US915_FIRST_RX1_CHANNEL ) % ( uint32_t ) US915_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ // Verify datarate
+ if( RegionCommonValueInRange( rxParamSetupReq->Datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+ if( ( RegionCommonValueInRange( rxParamSetupReq->Datarate, DR_5, DR_7 ) == true ) ||
+ ( rxParamSetupReq->Datarate > DR_13 ) )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify datarate offset
+ if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, US915_MIN_RX1_DR_OFFSET, US915_MAX_RX1_DR_OFFSET ) == false )
+ {
+ status &= 0xFB; // Rx1DrOffset range KO
+ }
+
+ return status;
+}
+
+uint8_t RegionUS915NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+ // Datarate and frequency KO
+ return 0;
+}
+
+int8_t RegionUS915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+ return -1;
+}
+
+uint8_t RegionUS915DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ return 0;
+}
+
+int8_t RegionUS915AlternateDr( AlternateDrParams_t* alternateDr )
+{
+ int8_t datarate = 0;
+
+ // Re-enable 500 kHz default channels
+ ChannelsMask[4] = 0x00FF;
+
+ if( ( alternateDr->NbTrials & 0x01 ) == 0x01 )
+ {
+ datarate = DR_4;
+ }
+ else
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
+
+void RegionUS915CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = Channels;
+ calcBackOffParams.Bands = Bands;
+ calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+ calcBackOffParams.Joined = calcBackOff->Joined;
+ calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+ calcBackOffParams.Channel = calcBackOff->Channel;
+ calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+ calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+ RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionUS915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTx = 0;
+ uint8_t enabledChannels[US915_MAX_NB_CHANNELS] = { 0 };
+ TimerTime_t nextTxDelay = 0;
+
+ // Count 125kHz channels
+ if( RegionCommonCountChannels( ChannelsMaskRemaining, 0, 4 ) == 0 )
+ { // Reactivate default channels
+ RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 4 );
+ }
+ // Check other channels
+ if( nextChanParams->Datarate >= DR_4 )
+ {
+ if( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 )
+ {
+ ChannelsMaskRemaining[4] = ChannelsMask[4];
+ }
+ }
+
+ if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, US915_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate,
+ ChannelsMaskRemaining, Channels,
+ Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+ // Disable the channel in the mask
+ RegionCommonChanDisable( ChannelsMaskRemaining, *channel, US915_MAX_NB_CHANNELS - 8 );
+
+ *time = 0;
+ return true;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return true;
+ }
+ // Datarate not supported by any channel
+ *time = 0;
+ return false;
+ }
+}
+
+LoRaMacStatus_t RegionUS915ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+bool RegionUS915ChannelsRemove( ChannelRemoveParams_t* channelRemove )
+{
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+void RegionUS915SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 );
+
+ Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionUS915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ int8_t datarate = DatarateOffsetsUS915[dr][drOffset];
+
+ if( datarate < 0 )
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
+
+void RegionUS915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesUS915;
+ regionCommonRxBeaconSetup.ChannelPlanFrequency = ( 923.3e6 + ( BeaconChannel( 0, rxBeaconSetup->BeaconTime, rxBeaconSetup->BeaconInterval ) * 600e3 ) );
+ regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = ( 923.3e6 + ( rxBeaconSetup->BeaconTimingChannel * 600e3 ) );
+ regionCommonRxBeaconSetup.BeaconSize = US915_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = US915_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = US915_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+ regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+ regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup, beaconChannelSet );
+
+ // Store downlink datarate
+ *outDr = US915_BEACON_CHANNEL_DR;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionUS915.h Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,538 @@
+/*!
+ * \file RegionUS915.h
+ *
+ * \brief Region definition for US915
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGIONUS915 Region US915
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_US915_H__
+#define __REGION_US915_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define US915_MAX_NB_CHANNELS 72
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define US915_TX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define US915_TX_MAX_DATARATE DR_4
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define US915_RX_MIN_DATARATE DR_8
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define US915_RX_MAX_DATARATE DR_13
+
+/*!
+ * Default datarate used by the node
+ */
+#define US915_DEFAULT_DATARATE DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define US915_MIN_RX1_DR_OFFSET 0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define US915_MAX_RX1_DR_OFFSET 3
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define US915_DEFAULT_RX1_DR_OFFSET 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define US915_MIN_TX_POWER TX_POWER_10
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define US915_MAX_TX_POWER TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define US915_DEFAULT_TX_POWER TX_POWER_0
+
+/*!
+ * Default Max ERP
+ */
+#define US915_DEFAULT_MAX_ERP 30.0f
+
+/*!
+ * ADR Ack limit
+ */
+#define US915_ADR_ACK_LIMIT 64
+
+/*!
+ * ADR Ack delay
+ */
+#define US915_ADR_ACK_DELAY 32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define US915_DUTY_CYCLE_ENABLED 0
+
+/*!
+ * Maximum RX window duration
+ */
+#define US915_MAX_RX_WINDOW 3000
+
+/*!
+ * Receive delay 1
+ */
+#define US915_RECEIVE_DELAY1 1000
+
+/*!
+ * Receive delay 2
+ */
+#define US915_RECEIVE_DELAY2 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define US915_JOIN_ACCEPT_DELAY1 5000
+
+/*!
+ * Join accept delay 2
+ */
+#define US915_JOIN_ACCEPT_DELAY2 6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define US915_MAX_FCNT_GAP 16384
+
+/*!
+ * Ack timeout
+ */
+#define US915_ACKTIMEOUT 2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define US915_ACK_TIMEOUT_RND 1000
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define US915_RX_WND_2_FREQ 923300000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define US915_RX_WND_2_DR DR_8
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define US915_BEACON_INTERVAL 128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define US915_BEACON_RESERVED 2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define US915_BEACON_GUARD 3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define US915_BEACON_WINDOW 122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define US915_BEACON_WINDOW_SLOTS 4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define US915_PING_SLOT_WINDOW 30
+
+/*!
+ * Beacon frequency
+ */
+#define US915_BEACON_CHANNEL_FREQ 923300000
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define US915_BEACON_SYMBOL_TO_DEFAULT 8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define US915_BEACON_SYMBOL_TO_EXPANSION_MAX 400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define US915_PING_SLOT_SYMBOL_TO_EXPANSION_MAX 40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define US915_BEACON_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define US915_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define US915_MAX_BEACON_LESS_PERIOD 7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define US915_BEACON_DELAY_BEACON_TIMING_ANS 30
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define US915_BEACON_SIZE 19
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define US915_BEACON_CHANNEL_DR DR_8
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define US915_BEACON_CHANNEL_BW 2
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define US915_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define US915_BAND0 { 1, US915_MAX_TX_POWER, 0, 0 } // 100.0 %
+
+/*!
+ * Defines the first channel for RX window 1 for US band
+ */
+#define US915_FIRST_RX1_CHANNEL ( (uint32_t) 923300000 )
+
+/*!
+ * Defines the last channel for RX window 1 for US band
+ */
+#define US915_LAST_RX1_CHANNEL ( (uint32_t) 927500000 )
+
+/*!
+ * Defines the step width of the channels for RX window 1
+ */
+#define US915_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600000 )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesUS915[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsUS915[] = { 125000, 125000, 125000, 125000, 500000, 0, 0, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 };
+
+/*!
+ * Up/Down link data rates offset definition
+ */
+static const int8_t DatarateOffsetsUS915[5][4] =
+{
+ { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0
+ { DR_11, DR_10, DR_9 , DR_8 }, // DR_1
+ { DR_12, DR_11, DR_10, DR_9 }, // DR_2
+ { DR_13, DR_12, DR_11, DR_10 }, // DR_3
+ { DR_13, DR_13, DR_12, DR_11 }, // DR_4
+};
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateUS915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterUS915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionUS915GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionUS915SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionUS915InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionUS915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ * CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionUS915ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionUS915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionUS915AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ * The receiver will turn on in a [-rxError : +rxError] ms
+ * interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionUS915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionUS915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ * Returns -1, if the functionality is not implemented. In this case, the end node
+ * shall not process the command.
+ */
+int8_t RegionUS915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionUS915AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionUS915CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ * cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionUS915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionUS915ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionUS915ChannelsRemove( ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionUS915SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionUS915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONUS915 */
+
+#endif // __REGION_US915_H__
