| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- /*!
- * \file RegionCommon.c
- *
- * \brief LoRa MAC common region implementation
- *
- * \copyright Revised BSD License, see section \ref LICENSE.
- *
- * \code
- * ______ _
- * / _____) _ | |
- * ( (____ _____ ____ _| |_ _____ ____| |__
- * \____ \| ___ | (_ _) ___ |/ ___) _ \
- * _____) ) ____| | | || |_| ____( (___| | | |
- * (______/|_____)_|_|_| \__)_____)\____)_| |_|
- * (C)2013-2017 Semtech
- *
- * ___ _____ _ ___ _ _____ ___ ___ ___ ___
- * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
- * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
- * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
- * embedded.connectivity.solutions===============
- *
- * \endcode
- *
- * \author Miguel Luis ( Semtech )
- *
- * \author Gregory Cristian ( Semtech )
- *
- * \author Daniel Jaeckle ( STACKFORCE )
- */
- #include <math.h>
- #include "lora-radio.h"
- #include "utilities.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( SysTime_t elapsedTime )
- {
- uint16_t dutyCycle = 0;
- if( elapsedTime.Seconds < 3600 )
- {
- dutyCycle = BACKOFF_DC_1_HOUR;
- }
- else if( elapsedTime.Seconds < ( 3600 + 36000 ) )
- {
- 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_MAX;
- // Update bands Time OFF
- for( uint8_t i = 0; i < nbBands; i++ )
- {
- if( joined == false )
- {
- TimerTime_t elapsedJoin = TimerGetElapsedTime( bands[i].LastJoinTxDoneTime );
- TimerTime_t elapsedTx = TimerGetElapsedTime( bands[i].LastTxDoneTime );
- TimerTime_t txDoneTime = MAX( elapsedJoin,
- ( dutyCycle == true ) ? elapsedTx : 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 )
- {
- TimerTime_t elapsed = TimerGetElapsedTime( bands[i].LastTxDoneTime );
- if( bands[i].TimeOff <= elapsed )
- {
- bands[i].TimeOff = 0;
- }
- if( bands[i].TimeOff != 0 )
- {
- nextTxDelay = MIN( bands[i].TimeOff - elapsed, nextTxDelay );
- }
- }
- else
- {
- nextTxDelay = 0;
- bands[i].TimeOff = 0;
- }
- }
- }
- return ( nextTxDelay == TIMERTIME_T_MAX ) ? 0 : 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
- nbRepetitions = verifyParams->CurrentNbRep;
- 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 )
- { // Restore the default value according to the LoRaWAN specification
- nbRepetitions = 1;
- }
- }
- // 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 rxContinuous = true;
- uint8_t datarate;
- // Set the radio into sleep mode
- Radio.Sleep( );
- // Setup frequency and payload length
- Radio.SetChannel( rxBeaconSetupParams->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 );
- }
|