diff --git a/src/LoRaWan_APP.cpp b/src/LoRaWan_APP.cpp index 163d78c..781cdf2 100644 --- a/src/LoRaWan_APP.cpp +++ b/src/LoRaWan_APP.cpp @@ -554,6 +554,9 @@ void LoRaWanClass::init(DeviceClass_t lorawanClass,LoRaMacRegion_t region) case LORAMAC_REGION_US915_HYBRID: Serial.print("US915_HYBRID "); break; + case LORAMAC_REGION_RU864: + Serial.print("RU864"); + break; default: break; } diff --git a/src/loramac/LoRaMac.h b/src/loramac/LoRaMac.h index 6c1ecd5..19c3ef3 100644 --- a/src/loramac/LoRaMac.h +++ b/src/loramac/LoRaMac.h @@ -2181,7 +2181,11 @@ typedef enum eLoRaMacRegion_t { /*! * AS band on 923.2-924.6MHz */ - LORAMAC_REGION_AS923_AS2 + LORAMAC_REGION_AS923_AS2, + /*! + * Russia band on 864MHz + */ + LORAMAC_REGION_RU864, } LoRaMacRegion_t; /*! diff --git a/src/loramac/region/Region.c b/src/loramac/region/Region.c index 52eab86..f89a86c 100644 --- a/src/loramac/region/Region.c +++ b/src/loramac/region/Region.c @@ -579,6 +579,60 @@ Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jae #define US915_HYBRID_RX_BEACON_SETUP( ) #endif +#ifdef REGION_RU864 +#include "RegionRU864.h" +#define RU864_CASE case LORAMAC_REGION_RU864: +#define RU864_IS_ACTIVE( ) RU864_CASE { return true; } +#define RU864_GET_PHY_PARAM( ) RU864_CASE { return RegionRU864GetPhyParam( getPhy ); } +#define RU864_SET_BAND_TX_DONE( ) RU864_CASE { RegionRU864SetBandTxDone( txDone ); break; } +#define RU864_INIT_DEFAULTS( ) RU864_CASE { RegionRU864InitDefaults( type ); break; } +#define RU864_VERIFY( ) RU864_CASE { return RegionRU864Verify( verify, phyAttribute ); } +#define RU864_APPLY_CF_LIST( ) RU864_CASE { RegionRU864ApplyCFList( applyCFList ); break; } +#define RU864_CHAN_MASK_SET( ) RU864_CASE { return RegionRU864ChanMaskSet( chanMaskSet ); } +#define RU864_ADR_NEXT( ) RU864_CASE { return RegionRU864AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); } +#define RU864_COMPUTE_RX_WINDOW_PARAMETERS( ) RU864_CASE { RegionRU864ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define RU864_RX_CONFIG( ) RU864_CASE { return RegionRU864RxConfig( rxConfig, datarate ); } +#define RU864_TX_CONFIG( ) RU864_CASE { return RegionRU864TxConfig( txConfig, txPower, txTimeOnAir ); } +#define RU864_LINK_ADR_REQ( ) RU864_CASE { return RegionRU864LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define RU864_RX_PARAM_SETUP_REQ( ) RU864_CASE { return RegionRU864RxParamSetupReq( rxParamSetupReq ); } +#define RU864_NEW_CHANNEL_REQ( ) RU864_CASE { return RegionRU864NewChannelReq( newChannelReq ); } +#define RU864_TX_PARAM_SETUP_REQ( ) RU864_CASE { return RegionRU864TxParamSetupReq( txParamSetupReq ); } +#define RU864_DL_CHANNEL_REQ( ) RU864_CASE { return RegionRU864DlChannelReq( dlChannelReq ); } +#define RU864_ALTERNATE_DR( ) RU864_CASE { return RegionRU864AlternateDr( alternateDr ); } +#define RU864_CALC_BACKOFF( ) RU864_CASE { RegionRU864CalcBackOff( calcBackOff ); break; } +#define RU864_NEXT_CHANNEL( ) RU864_CASE { return RegionRU864NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define RU864_CHANNEL_ADD( ) RU864_CASE { return RegionRU864ChannelAdd( channelAdd ); } +#define RU864_CHANNEL_REMOVE( ) RU864_CASE { return RegionRU864ChannelsRemove( channelRemove ); } +#define RU864_SET_CONTINUOUS_WAVE( ) RU864_CASE { RegionRU864SetContinuousWave( continuousWave ); break; } +#define RU864_APPLY_DR_OFFSET( ) RU864_CASE { return RegionRU864ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define RU864_RX_BEACON_SETUP( ) RU864_CASE { RegionRU864RxBeaconSetup( rxBeaconSetup, outDr ); } +#else +#define RU864_IS_ACTIVE( ) +#define RU864_GET_PHY_PARAM( ) +#define RU864_SET_BAND_TX_DONE( ) +#define RU864_INIT_DEFAULTS( ) +#define RU864_VERIFY( ) +#define RU864_APPLY_CF_LIST( ) +#define RU864_CHAN_MASK_SET( ) +#define RU864_ADR_NEXT( ) +#define RU864_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define RU864_RX_CONFIG( ) +#define RU864_TX_CONFIG( ) +#define RU864_LINK_ADR_REQ( ) +#define RU864_RX_PARAM_SETUP_REQ( ) +#define RU864_NEW_CHANNEL_REQ( ) +#define RU864_TX_PARAM_SETUP_REQ( ) +#define RU864_DL_CHANNEL_REQ( ) +#define RU864_ALTERNATE_DR( ) +#define RU864_CALC_BACKOFF( ) +#define RU864_NEXT_CHANNEL( ) +#define RU864_CHANNEL_ADD( ) +#define RU864_CHANNEL_REMOVE( ) +#define RU864_SET_CONTINUOUS_WAVE( ) +#define RU864_APPLY_DR_OFFSET( ) +#define RU864_RX_BEACON_SETUP( ) +#endif + bool RegionIsActive( LoRaMacRegion_t region ) { switch( region ) @@ -593,6 +647,7 @@ bool RegionIsActive( LoRaMacRegion_t region ) IN865_IS_ACTIVE( ); US915_IS_ACTIVE( ); US915_HYBRID_IS_ACTIVE( ); + RU864_IS_ACTIVE( ); default: { return false; @@ -615,6 +670,7 @@ PhyParam_t RegionGetPhyParam( LoRaMacRegion_t region, GetPhyParams_t* getPhy ) IN865_GET_PHY_PARAM( ); US915_GET_PHY_PARAM( ); US915_HYBRID_GET_PHY_PARAM( ); + RU864_GET_PHY_PARAM( ); default: { return phyParam; @@ -636,6 +692,7 @@ void RegionSetBandTxDone( LoRaMacRegion_t region, SetBandTxDoneParams_t* txDone IN865_SET_BAND_TX_DONE( ); US915_SET_BAND_TX_DONE( ); US915_HYBRID_SET_BAND_TX_DONE( ); + RU864_SET_BAND_TX_DONE( ); default: { return; @@ -657,6 +714,7 @@ void RegionInitDefaults( LoRaMacRegion_t region, InitType_t type ) IN865_INIT_DEFAULTS( ); US915_INIT_DEFAULTS( ); US915_HYBRID_INIT_DEFAULTS( ); + RU864_INIT_DEFAULTS( ); default: { break; @@ -678,6 +736,7 @@ bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_ IN865_VERIFY( ); US915_VERIFY( ); US915_HYBRID_VERIFY( ); + RU864_VERIFY( ); default: { return false; @@ -699,6 +758,7 @@ void RegionApplyCFList( LoRaMacRegion_t region, ApplyCFListParams_t* applyCFList IN865_APPLY_CF_LIST( ); US915_APPLY_CF_LIST( ); US915_HYBRID_APPLY_CF_LIST( ); + RU864_APPLY_CF_LIST( ); default: { break; @@ -720,6 +780,7 @@ bool RegionChanMaskSet( LoRaMacRegion_t region, ChanMaskSetParams_t* chanMaskSet IN865_CHAN_MASK_SET( ); US915_CHAN_MASK_SET( ); US915_HYBRID_CHAN_MASK_SET( ); + RU864_CHAN_MASK_SET( ); default: { return false; @@ -741,6 +802,7 @@ bool RegionAdrNext( LoRaMacRegion_t region, AdrNextParams_t* adrNext, int8_t* dr IN865_ADR_NEXT( ); US915_ADR_NEXT( ); US915_HYBRID_ADR_NEXT( ); + RU864_ADR_NEXT( ); default: { return false; @@ -762,6 +824,7 @@ void RegionComputeRxWindowParameters( LoRaMacRegion_t region, int8_t datarate, u IN865_COMPUTE_RX_WINDOW_PARAMETERS( ); US915_COMPUTE_RX_WINDOW_PARAMETERS( ); US915_HYBRID_COMPUTE_RX_WINDOW_PARAMETERS( ); + RU864_COMPUTE_RX_WINDOW_PARAMETERS( ); default: { break; @@ -783,6 +846,7 @@ bool RegionRxConfig( LoRaMacRegion_t region, RxConfigParams_t* rxConfig, int8_t* IN865_RX_CONFIG( ); US915_RX_CONFIG( ); US915_HYBRID_RX_CONFIG( ); + RU864_RX_CONFIG( ); default: { return false; @@ -804,6 +868,7 @@ bool RegionTxConfig( LoRaMacRegion_t region, TxConfigParams_t* txConfig, int8_t* IN865_TX_CONFIG( ); US915_TX_CONFIG( ); US915_HYBRID_TX_CONFIG( ); + RU864_TX_CONFIG( ); default: { return false; @@ -825,6 +890,7 @@ uint8_t RegionLinkAdrReq( LoRaMacRegion_t region, LinkAdrReqParams_t* linkAdrReq IN865_LINK_ADR_REQ( ); US915_LINK_ADR_REQ( ); US915_HYBRID_LINK_ADR_REQ( ); + RU864_LINK_ADR_REQ( ); default: { return 0; @@ -846,6 +912,7 @@ uint8_t RegionRxParamSetupReq( LoRaMacRegion_t region, RxParamSetupReqParams_t* IN865_RX_PARAM_SETUP_REQ( ); US915_RX_PARAM_SETUP_REQ( ); US915_HYBRID_RX_PARAM_SETUP_REQ( ); + RU864_RX_PARAM_SETUP_REQ( ); default: { return 0; @@ -867,6 +934,7 @@ uint8_t RegionNewChannelReq( LoRaMacRegion_t region, NewChannelReqParams_t* newC IN865_NEW_CHANNEL_REQ( ); US915_NEW_CHANNEL_REQ( ); US915_HYBRID_NEW_CHANNEL_REQ( ); + RU864_NEW_CHANNEL_REQ( ); default: { return 0; @@ -888,6 +956,7 @@ int8_t RegionTxParamSetupReq( LoRaMacRegion_t region, TxParamSetupReqParams_t* t IN865_TX_PARAM_SETUP_REQ( ); US915_TX_PARAM_SETUP_REQ( ); US915_HYBRID_TX_PARAM_SETUP_REQ( ); + RU864_TX_PARAM_SETUP_REQ( ); default: { return 0; @@ -909,6 +978,7 @@ uint8_t RegionDlChannelReq( LoRaMacRegion_t region, DlChannelReqParams_t* dlChan IN865_DL_CHANNEL_REQ( ); US915_DL_CHANNEL_REQ( ); US915_HYBRID_DL_CHANNEL_REQ( ); + RU864_DL_CHANNEL_REQ( ); default: { return 0; @@ -930,6 +1000,7 @@ int8_t RegionAlternateDr( LoRaMacRegion_t region, AlternateDrParams_t* alternate IN865_ALTERNATE_DR( ); US915_ALTERNATE_DR( ); US915_HYBRID_ALTERNATE_DR( ); + RU864_ALTERNATE_DR( ); default: { return 0; @@ -951,6 +1022,7 @@ void RegionCalcBackOff( LoRaMacRegion_t region, CalcBackOffParams_t* calcBackOff IN865_CALC_BACKOFF( ); US915_CALC_BACKOFF( ); US915_HYBRID_CALC_BACKOFF( ); + RU864_CALC_BACKOFF( ); default: { break; @@ -972,6 +1044,7 @@ bool RegionNextChannel( LoRaMacRegion_t region, NextChanParams_t* nextChanParams IN865_NEXT_CHANNEL( ); US915_NEXT_CHANNEL( ); US915_HYBRID_NEXT_CHANNEL( ); + RU864_NEXT_CHANNEL( ); default: { return false; @@ -993,6 +1066,7 @@ LoRaMacStatus_t RegionChannelAdd( LoRaMacRegion_t region, ChannelAddParams_t* ch IN865_CHANNEL_ADD( ); US915_CHANNEL_ADD( ); US915_HYBRID_CHANNEL_ADD( ); + RU864_CHANNEL_ADD( ); default: { return LORAMAC_STATUS_PARAMETER_INVALID; @@ -1014,6 +1088,7 @@ bool RegionChannelsRemove( LoRaMacRegion_t region, ChannelRemoveParams_t* channe IN865_CHANNEL_REMOVE( ); US915_CHANNEL_REMOVE( ); US915_HYBRID_CHANNEL_REMOVE( ); + RU864_CHANNEL_REMOVE( ); default: { return false; @@ -1035,6 +1110,7 @@ void RegionSetContinuousWave( LoRaMacRegion_t region, ContinuousWaveParams_t* co IN865_SET_CONTINUOUS_WAVE( ); US915_SET_CONTINUOUS_WAVE( ); US915_HYBRID_SET_CONTINUOUS_WAVE( ); + RU864_SET_CONTINUOUS_WAVE( ); default: { break; @@ -1056,6 +1132,7 @@ uint8_t RegionApplyDrOffset( LoRaMacRegion_t region, uint8_t downlinkDwellTime, IN865_APPLY_DR_OFFSET( ); US915_APPLY_DR_OFFSET( ); US915_HYBRID_APPLY_DR_OFFSET( ); + RU864_APPLY_DR_OFFSET( ); default: { return dr; @@ -1077,6 +1154,7 @@ void RegionRxBeaconSetup( LoRaMacRegion_t region, RxBeaconSetup_t* rxBeaconSetup IN865_RX_BEACON_SETUP( ); US915_RX_BEACON_SETUP( ); US915_HYBRID_RX_BEACON_SETUP( ); + RU864_RX_BEACON_SETUP( ); default: { break; diff --git a/src/loramac/region/Region.h b/src/loramac/region/Region.h index 8166127..5c54d74 100644 --- a/src/loramac/region/Region.h +++ b/src/loramac/region/Region.h @@ -51,6 +51,7 @@ * - #define REGION_IN865 * - #define REGION_US915 * - #define REGION_US915_HYBRID + * - #define REGION_RU864 * * \{ */ @@ -90,6 +91,7 @@ * KR920 | SF12 - BW125 * US915 | SF10 - BW125 * US915_HYBRID | SF10 - BW125 + * RU864 | SF12 - BW125 */ #define DR_0 0 @@ -107,6 +109,7 @@ * KR920 | SF11 - BW125 * US915 | SF9 - BW125 * US915_HYBRID | SF9 - BW125 + * RU864 | SF11 - BW125 */ #define DR_1 1 @@ -124,6 +127,7 @@ * KR920 | SF10 - BW125 * US915 | SF8 - BW125 * US915_HYBRID | SF8 - BW125 + * RU864 | SF10 - BW125 */ #define DR_2 2 @@ -141,6 +145,7 @@ * KR920 | SF9 - BW125 * US915 | SF7 - BW125 * US915_HYBRID | SF7 - BW125 + * RU864 | SF9 - BW125 */ #define DR_3 3 @@ -158,6 +163,7 @@ * KR920 | SF8 - BW125 * US915 | SF8 - BW500 * US915_HYBRID | SF8 - BW500 + * RU864 | SF8 - BW125 */ #define DR_4 4 @@ -175,6 +181,7 @@ * KR920 | SF7 - BW125 * US915 | RFU * US915_HYBRID | RFU + * RU864 | SF7 - BW125 */ #define DR_5 5 @@ -192,6 +199,7 @@ * KR920 | RFU * US915 | RFU * US915_HYBRID | RFU + * RU864 | SF7 - BW250 */ #define DR_6 6 @@ -209,6 +217,7 @@ * KR920 | RFU * US915 | RFU * US915_HYBRID | RFU + * RU864 | FSK */ #define DR_7 7 @@ -226,6 +235,7 @@ * KR920 | RFU * US915 | SF12 - BW500 * US915_HYBRID | SF12 - BW500 + * RU864 | RFU */ #define DR_8 8 @@ -243,6 +253,7 @@ * KR920 | RFU * US915 | SF11 - BW500 * US915_HYBRID | SF11 - BW500 + * RU864 | RFU */ #define DR_9 9 @@ -260,6 +271,7 @@ * KR920 | RFU * US915 | SF10 - BW500 * US915_HYBRID | SF10 - BW500 + * RU864 | RFU */ #define DR_10 10 @@ -277,6 +289,7 @@ * KR920 | RFU * US915 | SF9 - BW500 * US915_HYBRID | SF9 - BW500 + * RU864 | RFU */ #define DR_11 11 @@ -294,6 +307,7 @@ * KR920 | RFU * US915 | SF8 - BW500 * US915_HYBRID | SF8 - BW500 + * RU864 | RFU */ #define DR_12 12 @@ -311,6 +325,7 @@ * KR920 | RFU * US915 | SF7 - BW500 * US915_HYBRID | SF7 - BW500 + * RU864 | RFU */ #define DR_13 13 @@ -328,6 +343,7 @@ * KR920 | RFU * US915 | RFU * US915_HYBRID | RFU + * RU864 | RFU */ #define DR_14 14 @@ -345,6 +361,7 @@ * KR920 | RFU * US915 | RFU * US915_HYBRID | RFU + * RU864 | RFU */ #define DR_15 15 @@ -364,6 +381,7 @@ * KR920 | Max EIRP * US915 | Max ERP * US915_HYBRID | Max ERP + * RU864 | Max EIRP */ #define TX_POWER_0 0 @@ -381,6 +399,7 @@ * KR920 | Max EIRP - 2 * US915 | Max ERP - 2 * US915_HYBRID | Max ERP - 2 + * RU864 | Max EIRP - 2 */ #define TX_POWER_1 1 @@ -398,6 +417,7 @@ * KR920 | Max EIRP - 4 * US915 | Max ERP - 4 * US915_HYBRID | Max ERP - 4 + * RU864 | Max EIRP - 4 */ #define TX_POWER_2 2 @@ -415,6 +435,7 @@ * KR920 | Max EIRP - 6 * US915 | Max ERP - 6 * US915_HYBRID | Max ERP - 6 + * RU864 | Max EIRP - 6 */ #define TX_POWER_3 3 @@ -432,6 +453,7 @@ * KR920 | Max EIRP - 8 * US915 | Max ERP - 8 * US915_HYBRID | Max ERP - 8 + * RU864 | Max EIRP - 8 */ #define TX_POWER_4 4 @@ -449,6 +471,7 @@ * KR920 | Max EIRP - 10 * US915 | Max ERP - 10 * US915_HYBRID | Max ERP - 10 + * RU864 | Max EIRP - 10 */ #define TX_POWER_5 5 @@ -466,6 +489,7 @@ * KR920 | Max EIRP - 12 * US915 | Max ERP - 12 * US915_HYBRID | Max ERP - 12 + * RU864 | Max EIRP - 12 */ #define TX_POWER_6 6 @@ -483,6 +507,7 @@ * KR920 | Max EIRP - 14 * US915 | Max ERP - 14 * US915_HYBRID | Max ERP - 14 + * RU864 | Max EIRP - 14 */ #define TX_POWER_7 7 @@ -499,6 +524,7 @@ * KR920 | - * US915 | Max ERP - 16 * US915_HYBRID | Max ERP -16 + * RU864 | - */ #define TX_POWER_8 8 @@ -515,6 +541,7 @@ * KR920 | - * US915 | Max ERP - 16 * US915_HYBRID | Max ERP - 16 + * RU864 | - */ #define TX_POWER_9 9 @@ -531,6 +558,7 @@ * KR920 | - * US915 | Max ERP - 10 * US915_HYBRID | Max ERP - 10 + * RU864 | - */ #define TX_POWER_10 10 diff --git a/src/loramac/region/RegionRU864.c b/src/loramac/region/RegionRU864.c new file mode 100644 index 0000000..a512f47 --- /dev/null +++ b/src/loramac/region/RegionRU864.c @@ -0,0 +1,1094 @@ +/* + * Copyright (C) 2015-2017 Alibaba Group Holding Limited + */ + +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC region RU864 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 +#include +#include +#include +#include "Region.h" +#include "RegionCommon.h" +#include "RegionRU864.h" + + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +// Global attributes +/*! + * LoRaMAC channels + */ +RTC_DATA_ATTR static ChannelParams_t Channels[RU864_MAX_NB_CHANNELS]; + +/*! + * LoRaMac bands + */ +RTC_DATA_ATTR static Band_t Bands[RU864_MAX_NB_BANDS] = +{ + RU864_BAND0 +}; + +/*! + * LoRaMac channels mask + */ +RTC_DATA_ATTR static uint16_t ChannelsMask[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels default mask + */ +RTC_DATA_ATTR 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( BandwidthsRU864[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 < 864000000 ) || ( freq > 870000000 ) ) + { + 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 < RU864_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( ( RU864_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 RegionRU864GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = RU864_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = RU864_TX_MIN_DATARATE; + break; + } + case PHY_MAX_TX_DR: + { + phyParam.Value = RU864_TX_MAX_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = RU864_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, RU864_TX_MIN_DATARATE ); + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = RU864_DEFAULT_TX_POWER; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateRU864[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterRU864[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = RU864_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = RU864_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = RU864_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = RU864_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = RU864_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = RU864_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = RU864_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( RU864_ACKTIMEOUT + randr( -RU864_ACK_TIMEOUT_RND, RU864_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = RU864_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = RU864_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = RU864_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 = RU864_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 = RU864_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = RU864_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_NB_JOIN_TRIALS: + case PHY_DEF_NB_JOIN_TRIALS: + { + phyParam.Value = 48; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = RU864_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = RU864_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = RU864_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = RU864_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = RU864_BEACON_CHANNEL_DR; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionRU864SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionRU864InitDefaults( InitType_t type ) +{ + switch( type ) + { + case INIT_TYPE_INIT: + { + // Channels + Channels[0] = ( ChannelParams_t ) RU864_LC1; + Channels[1] = ( ChannelParams_t ) RU864_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 RegionRU864Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, RU864_TX_MIN_DATARATE, RU864_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, RU864_RX_MIN_DATARATE, RU864_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, RU864_MAX_TX_POWER, RU864_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return RU864_DUTY_CYCLE_ENABLED; + } + case PHY_NB_JOIN_TRIALS: + { + if( verify->NbJoinTrials < 48 ) + { + return false; + } + break; + } + default: + return false; + } + return true; +} + +void RegionRU864ApplyCFList( 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 = RU864_NUMB_DEFAULT_CHANNELS; chanIdx < RU864_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( RU864_NUMB_CHANNELS_CF_LIST + RU864_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 + RegionRU864ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionRU864ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionRU864ChanMaskSet( 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 RegionRU864AdrNext( 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 == RU864_TX_MIN_DATARATE ) + { + *adrAckCounter = 0; + adrAckReq = false; + } + else + { + if( adrNext->AdrAckCounter >= RU864_ADR_ACK_LIMIT ) + { + adrAckReq = true; + txPower = RU864_MAX_TX_POWER; + } + else + { + adrAckReq = false; + } + if( adrNext->AdrAckCounter >= ( RU864_ADR_ACK_LIMIT + RU864_ADR_ACK_DELAY ) ) + { + if( ( adrNext->AdrAckCounter % RU864_ADR_ACK_DELAY ) == 1 ) + { + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionRU864GetPhyParam( &getPhy ); + datarate = phyParam.Value; + + if( datarate == RU864_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 ); + } + } + } + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + return adrAckReq; +} + +void RegionRU864ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + uint32_t radioWakeUpTime; + + rxConfigParams->Datarate = datarate; + rxConfigParams->Bandwidth = GetBandwidth( datarate ); + + if( datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesRU864[datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesRU864[datarate], BandwidthsRU864[datarate] ); + } + + radioWakeUpTime = Radio.GetWakeupTime( ); + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, radioWakeUpTime, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionRU864RxConfig( 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->RxSlot == RX_SLOT_WIN_1 ) + { + // 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 = DataratesRU864[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 = MaxPayloadOfDatarateRepeaterRU864[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateRU864[dr]; + } + Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + FREQ_PRINTF("RX on freq %u Hz at DR %d\r\n", (unsigned int)frequency, dr); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionRU864TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesRU864[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, LORAWAN_PREAMBLE_LENGTH, false, true, 0, 0, false, 3e3 ); + } + FREQ_PRINTF("TX on freq %u Hz at DR %d\r\n", (unsigned int)Channels[txConfig->Channel].Frequency, txConfig->Datarate); + // 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 = txConfig->TxPower; + return true; +} + +uint8_t RegionRU864LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + LinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + + 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 < RU864_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 + } + } + } + } + } + + // Verify datarate + if( RegionCommonChanVerifyDr( RU864_MAX_NB_CHANNELS, &chMask, linkAdrParams.Datarate, RU864_TX_MIN_DATARATE, RU864_TX_MAX_DATARATE, Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( RegionCommonValueInRange( linkAdrParams.TxPower, RU864_MAX_TX_POWER, RU864_MIN_TX_POWER ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( RU864_MAX_TX_POWER > linkAdrParams.TxPower ) + { // Apply maximum TX power. Accept TX power. + linkAdrParams.TxPower = RU864_MAX_TX_POWER; + } + else + { + status &= 0xFB; // TxPower KO + } + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + if( linkAdrParams.NbRep == 0 ) + { // Value of 0 is not allowed, revert to default. + linkAdrParams.NbRep = 1; + } + + // 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 RegionRU864RxParamSetupReq( 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, RU864_RX_MIN_DATARATE, RU864_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, RU864_MIN_RX1_DR_OFFSET, RU864_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionRU864NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionRU864ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionRU864ChannelAdd( &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 RegionRU864TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionRU864DlChannelReq( 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 RegionRU864AlternateDr( 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 RegionRU864CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + uint8_t channel = calcBackOff->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( calcBackOff->Joined == false ) + { + // Get the join duty cycle + joinDutyCycle = RegionCommonGetJoinDc( calcBackOff->ElapsedTime ); + // Apply the most restricting duty cycle + dutyCycle = MAX( dutyCycle, joinDutyCycle ); + // Apply band time-off. + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + else + { + if( calcBackOff->DutyCycleEnabled == true ) + { + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + } +} + +bool RegionRU864NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[RU864_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, RU864_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 ); + *time = 0; + return false; + } +} + +LoRaMacStatus_t RegionRU864ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + uint8_t band = 0; + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id >= RU864_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, RU864_TX_MIN_DATARATE, RU864_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, RU864_TX_MIN_DATARATE, RU864_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 < RU864_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, RU864_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 RegionRU864ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < RU864_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, RU864_MAX_NB_CHANNELS ); +} + +void RegionRU864SetContinuousWave( 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 RegionRU864ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionRU864RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesRU864; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = RU864_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = RU864_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = RU864_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = RU864_BEACON_CHANNEL_DR; +} diff --git a/src/loramac/region/RegionRU864.h b/src/loramac/region/RegionRU864.h new file mode 100644 index 0000000..ef79cae --- /dev/null +++ b/src/loramac/region/RegionRU864.h @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2015-2017 Alibaba Group Holding Limited + */ + +/*! + * \file RegionRU864.h + * + * \brief Region definition for RU864 + * + * \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 REGIONRU864 Region RU864 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ + +#ifndef __REGION_RU864_H__ +#define __REGION_RU864_H__ + +#include "../LoRaMac.h" + +/*! + * LoRaMac maximum number of channels + */ +#define RU864_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define RU864_NUMB_DEFAULT_CHANNELS 2 + +/*! + * Number of channels to apply for the CF list + */ +#define RU864_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define RU864_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define RU864_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define RU864_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define RU864_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define RU864_DEFAULT_DATARATE DR_5 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define RU864_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define RU864_MAX_RX1_DR_OFFSET 5 + +/*! + * Default Rx1 receive datarate offset + */ +#define RU864_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define RU864_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define RU864_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define RU864_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define RU864_DEFAULT_MAX_EIRP 16.0f + +/*! + * Default antenna gain + */ +#define RU864_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define RU864_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define RU864_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define RU864_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define RU864_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define RU864_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define RU864_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define RU864_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define RU864_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define RU864_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define RU864_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define RU864_ACK_TIMEOUT_RND 1000 + +/*! + * Verification of default datarate + */ +#if ( RU864_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 RU864_RX_WND_2_FREQ 869100000 + +/*! + * Second reception window channel datarate definition. + */ +#define RU864_RX_WND_2_DR DR_0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define RU864_BEACON_CHANNEL_FREQ 869100000 + +/*! + * Payload size of a beacon frame + */ +#define RU864_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define RU864_RFU1_SIZE 2 + +/*! + * Size of RFU 2 field + */ +#define RU864_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define RU864_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel + */ +#define RU864_BEACON_CHANNEL_BW 0 + +/*! + * LoRaMac maximum number of bands + */ +#define RU864_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define RU864_BAND0 { 100, RU864_MAX_TX_POWER, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define RU864_LC1 { 868900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define RU864_LC2 { 869100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define RU864_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesRU864[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsRU864[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRU864[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterRU864[] = { 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 RegionRU864GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionRU864SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionRU864InitDefaults( 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 RegionRU864Verify( 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 RegionRU864ApplyCFList( 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 RegionRU864ChanMaskSet( 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 RegionRU864AdrNext( 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 RegionRU864ComputeRxWindowParameters( 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 RegionRU864RxConfig( 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 RegionRU864TxConfig( 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 RegionRU864LinkAdrReq( 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 RegionRU864RxParamSetupReq( 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 RegionRU864NewChannelReq( 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 RegionRU864TxParamSetupReq( 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 RegionRU864DlChannelReq( 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 RegionRU864AlternateDr( AlternateDrParams_t* alternateDr ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionRU864CalcBackOff( 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 RegionRU864NextChannel( 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 RegionRU864ChannelAdd( 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 RegionRU864ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionRU864SetContinuousWave( 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 RegionRU864ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionRU864RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONRU864 */ + +#endif // __REGION_RU864_H__