Слияние кода завершено, страница обновится автоматически
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(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 <math.h>
#include "utilities.h"
#include "secure-element.h"
#include "LoRaMac.h"
#include "LoRaMacClassB.h"
#include "LoRaMacClassBNvm.h"
#include "LoRaMacClassBConfig.h"
#include "LoRaMacCrypto.h"
#include "LoRaMacConfirmQueue.h"
#include "radio.h"
#include "region/Region.h"
#ifdef LORAMAC_CLASSB_ENABLED
/*
* LoRaMac Class B Context structure
*/
typedef struct sLoRaMacClassBCtx
{
/*!
* Class B ping slot context
*/
PingSlotContext_t PingSlotCtx;
/*!
* Class B beacon context
*/
BeaconContext_t BeaconCtx;
/*!
* State of the beaconing mechanism
*/
BeaconState_t BeaconState;
/*!
* State of the ping slot mechanism
*/
PingSlotState_t PingSlotState;
/*!
* State of the multicast slot mechanism
*/
PingSlotState_t MulticastSlotState;
/*!
* Timer for CLASS B beacon acquisition and tracking.
*/
TimerEvent_t BeaconTimer;
/*!
* Timer for CLASS B ping slot timer.
*/
TimerEvent_t PingSlotTimer;
/*!
* Timer for CLASS B multicast ping slot timer.
*/
TimerEvent_t MulticastSlotTimer;
/*!
* Container for the callbacks related to class b.
*/
LoRaMacClassBCallback_t LoRaMacClassBCallbacks;
/*!
* Data structure which holds the parameters which needs to be set
* in class b operation.
*/
LoRaMacClassBParams_t LoRaMacClassBParams;
} LoRaMacClassBCtx_t;
/*!
* Defines the LoRaMac radio events status
*/
typedef union uLoRaMacClassBEvents
{
uint32_t Value;
struct sEvents
{
uint32_t Beacon : 1;
uint32_t PingSlot : 1;
uint32_t MulticastSlot : 1;
}Events;
}LoRaMacClassBEvents_t;
LoRaMacClassBEvents_t LoRaMacClassBEvents = { .Value = 0 };
/*
* Module context.
*/
static LoRaMacClassBCtx_t Ctx;
/*
* Beacon transmit time precision in milliseconds.
* The usage of these values shall be determined by the
* prec value in param field received in a beacon frame.
* As the time base is milli seconds, the precision will be either 0 ms or 1 ms.
*/
static const uint8_t BeaconPrecTimeValue[4] = { 0, 1, 1, 1 };
/*!
* Data structure which holds the parameters which needs to be stored
* in the NVM.
*/
static LoRaMacClassBNvmData_t* ClassBNvm;
/*!
* Computes the Ping Offset
*
* \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
*/
static void ComputePingOffset( uint64_t beaconTime, uint32_t address, uint16_t pingPeriod, uint16_t *pingOffset )
{
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( buffer, 0, 16 );
memset1( cipher, 0, 16 );
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;
SecureElementAesEncrypt( buffer, 16, SLOT_RAND_ZERO_KEY, cipher );
result = ( ( ( uint32_t ) cipher[0] ) + ( ( ( uint32_t ) cipher[1] ) * 256 ) );
*pingOffset = ( uint16_t )( result % pingPeriod );
}
/*!
* \brief Calculates the downlink frequency for a given channel.
*
* \param [IN] channel The channel according to the channel plan.
*
* \param [IN] isBeacon Set to true, if the function shall
* calculate the frequency for a beacon.
*
* \retval The downlink frequency
*/
static uint32_t CalcDownlinkFrequency( uint8_t channel, bool isBeacon )
{
GetPhyParams_t getPhy;
PhyParam_t phyParam;
getPhy.Attribute = PHY_PING_SLOT_CHANNEL_FREQ;
if( isBeacon == true )
{
getPhy.Attribute = PHY_BEACON_CHANNEL_FREQ;
}
getPhy.Channel = channel;
phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
return phyParam.Value;
}
/*!
* \brief Calculates the downlink channel for the beacon and for
* ping slot downlinks.
*
* \param [IN] devAddr The address of the device. Assign 0 if its a beacon.
*
* \param [IN] beaconTime The beacon time of the beacon.
*
* \param [IN] beaconInterval The beacon interval.
*
* \param [IN] isBeacon Set to true, if the function shall
* calculate the frequency for a beacon.
*
* \retval The downlink channel
*/
static uint32_t CalcDownlinkChannelAndFrequency( uint32_t devAddr, TimerTime_t beaconTime,
TimerTime_t beaconInterval, bool isBeacon )
{
GetPhyParams_t getPhy;
PhyParam_t phyParam;
uint32_t channel = 0;
uint8_t nbChannels = 0;
uint8_t offset = 0;
// Default initialization - ping slot channels
getPhy.Attribute = PHY_PING_SLOT_NB_CHANNELS;
if( isBeacon == true )
{
// Beacon channels
getPhy.Attribute = PHY_BEACON_NB_CHANNELS;
}
phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
nbChannels = ( uint8_t ) phyParam.Value;
// nbChannels is > 1, when the channel plan requires more than one possible channel
// defined by the calculation below.
if( nbChannels > 1 )
{
getPhy.Attribute = PHY_BEACON_CHANNEL_OFFSET;
phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
offset = ( uint8_t ) phyParam.Value;
// Calculate the channel for the next downlink
channel = devAddr + ( beaconTime / ( beaconInterval / 1000 ) );
channel = channel % nbChannels;
channel += offset;
}
// Calculate the frequency for the next downlink. This holds
// for beacons and ping slots.
return CalcDownlinkFrequency( channel, isBeacon );
}
/*!
* \brief Calculates the correct frequency and opens up the beacon reception window. Please
* note that the variable WindowTimeout and WindowOffset will be updated according
* to the current settings. Also, the function perform a calculation only, when
* Ctx.BeaconCtx.Ctrl.BeaconAcquired OR Ctx.BeaconCtx.Ctrl.AcquisitionPending is
* set to 1.
*
* \param [IN] rxConfig Reception parameters for the beacon window.
*
* \param [IN] currentSymbolTimeout Current symbol timeout.
*/
static void CalculateBeaconRxWindowConfig( RxConfigParams_t* rxConfig, uint16_t currentSymbolTimeout )
{
GetPhyParams_t getPhy;
PhyParam_t phyParam;
uint32_t maxRxError = 0;
rxConfig->WindowTimeout = currentSymbolTimeout;
rxConfig->WindowOffset = 0;
if( ( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 ) || ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) )
{
// Apply the symbol timeout only if we have acquired the beacon
// Otherwise, take the window enlargement into account
// Read beacon datarate
getPhy.Attribute = PHY_BEACON_CHANNEL_DR;
phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
// Compare and assign the maximum between the region specific rx error window time
// and time precision received from beacon frame format.
maxRxError = MAX( Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError,
( uint32_t ) Ctx.BeaconCtx.BeaconTimePrecision.SubSeconds );
// Calculate downlink symbols
RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion,
( int8_t )phyParam.Value, // datarate
Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
maxRxError,
rxConfig );
}
}
/*!
* \brief Calculates the correct frequency and opens up the beacon reception window.
*
* \param [IN] rxTime The reception time which should be setup
*
* \param [IN] activateDefaultChannel Set to true, if the function shall setup the default channel
*
* \param [IN] symbolTimeout Symbol timeout
*/
static void RxBeaconSetup( TimerTime_t rxTime, bool activateDefaultChannel, uint16_t symbolTimeout )
{
RxBeaconSetup_t rxBeaconSetup;
uint32_t frequency = 0;
if( activateDefaultChannel == true )
{
// This is the default frequency in case we don't know when the next
// beacon will be transmitted. We select channel 0 as default.
frequency = CalcDownlinkFrequency( 0, true );
}
else
{
// This is the frequency according to the channel plan
frequency = CalcDownlinkChannelAndFrequency( 0, Ctx.BeaconCtx.BeaconTime.Seconds + ( CLASSB_BEACON_INTERVAL / 1000 ),
CLASSB_BEACON_INTERVAL, true );
}
if( ClassBNvm->BeaconCtx.Ctrl.CustomFreq == 1 )
{
// Set the frequency from the BeaconFreqReq
frequency = ClassBNvm->BeaconCtx.Frequency;
}
if( Ctx.BeaconCtx.Ctrl.BeaconChannelSet == 1 )
{
// Set the frequency which was provided by BeaconTimingAns MAC command
Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 0;
frequency = CalcDownlinkFrequency( Ctx.BeaconCtx.BeaconTimingChannel, true );
}
rxBeaconSetup.SymbolTimeout = symbolTimeout;
rxBeaconSetup.RxTime = rxTime;
rxBeaconSetup.Frequency = frequency;
RegionRxBeaconSetup( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate );
Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Frequency = frequency;
Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Datarate = Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate;
}
/*!
* \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, uint16_t pingNb, 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 - SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) ) % CLASSB_BEACON_INTERVAL );
slotTime = currentTime - slotTime;
// Add the reserved time and the ping offset
slotTime += CLASSB_BEACON_RESERVED;
slotTime += slotOffset * CLASSB_PING_SLOT_WINDOW;
if( slotTime < currentTime )
{
currentPingSlot = ( ( currentTime - slotTime ) /
( pingPeriod * CLASSB_PING_SLOT_WINDOW ) ) + 1;
slotTime += ( ( TimerTime_t )( currentPingSlot * pingPeriod ) *
CLASSB_PING_SLOT_WINDOW );
}
if( currentPingSlot < pingNb )
{
if( slotTime <= ( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - CLASSB_BEACON_GUARD - CLASSB_PING_SLOT_WINDOW ) )
{
// Calculate the relative ping slot time
slotTime -= currentTime;
slotTime -= Radio.GetWakeupTime( );
slotTime = TimerTempCompensation( slotTime, Ctx.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( );
}
}
static void InitClassB( void )
{
GetPhyParams_t getPhy;
PhyParam_t phyParam;
// Init events
LoRaMacClassBEvents.Value = 0;
// Init variables to default
memset1( ( uint8_t* ) ClassBNvm, 0, sizeof( LoRaMacClassBNvmData_t ) );
memset1( ( uint8_t* ) &Ctx.PingSlotCtx, 0, sizeof( PingSlotContext_t ) );
memset1( ( uint8_t* ) &Ctx.BeaconCtx, 0, sizeof( BeaconContext_t ) );
// Setup default temperature
Ctx.BeaconCtx.Temperature = 25.0;
GetTemperatureLevel( &Ctx.LoRaMacClassBCallbacks, &Ctx.BeaconCtx );
// Setup default ping slot datarate
getPhy.Attribute = PHY_PING_SLOT_CHANNEL_DR;
phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
ClassBNvm->PingSlotCtx.Datarate = ( int8_t )( phyParam.Value );
// Setup default FPending bit
ClassBNvm->PingSlotCtx.FPendingSet = 0;
// Setup default states
Ctx.BeaconState = BEACON_STATE_ACQUISITION;
Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
}
static void InitClassBDefaults( void )
{
// This function shall reset the Class B settings to default,
// but should keep important configurations
LoRaMacClassBBeaconNvmData_t beaconCtx = ClassBNvm->BeaconCtx;
LoRaMacClassBPingSlotNvmData_t pingSlotCtx = ClassBNvm->PingSlotCtx;
InitClassB( );
// Parameters from BeaconFreqReq
ClassBNvm->BeaconCtx.Frequency = beaconCtx.Frequency;
ClassBNvm->BeaconCtx.Ctrl.CustomFreq = beaconCtx.Ctrl.CustomFreq;
// Parameters from PingSlotChannelReq
ClassBNvm->PingSlotCtx.Ctrl.CustomFreq = pingSlotCtx.Ctrl.CustomFreq;
ClassBNvm->PingSlotCtx.Frequency = pingSlotCtx.Frequency;
ClassBNvm->PingSlotCtx.Datarate = pingSlotCtx.Datarate;
}
static void EnlargeWindowTimeout( void )
{
// Update beacon movement
Ctx.BeaconCtx.BeaconWindowMovement *= CLASSB_WINDOW_MOVE_EXPANSION_FACTOR;
if( Ctx.BeaconCtx.BeaconWindowMovement > CLASSB_WINDOW_MOVE_EXPANSION_MAX )
{
Ctx.BeaconCtx.BeaconWindowMovement = CLASSB_WINDOW_MOVE_EXPANSION_MAX;
}
// Update symbol timeout
Ctx.BeaconCtx.SymbolTimeout *= CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
if( Ctx.BeaconCtx.SymbolTimeout > CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX )
{
Ctx.BeaconCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX;
}
Ctx.PingSlotCtx.SymbolTimeout *= CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
if( Ctx.PingSlotCtx.SymbolTimeout > CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX )
{
Ctx.PingSlotCtx.SymbolTimeout = CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
}
}
static void ResetWindowTimeout( void )
{
Ctx.BeaconCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_DEFAULT;
Ctx.PingSlotCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_DEFAULT;
Ctx.BeaconCtx.BeaconWindowMovement = CLASSB_WINDOW_MOVE_DEFAULT;
}
static TimerTime_t CalcDelayForNextBeacon( TimerTime_t currentTime, TimerTime_t lastBeaconRx )
{
TimerTime_t nextBeaconRxTime = 0;
// Calculate the point in time of the next beacon
nextBeaconRxTime = ( ( currentTime - lastBeaconRx ) % CLASSB_BEACON_INTERVAL );
return ( CLASSB_BEACON_INTERVAL - nextBeaconRxTime );
}
static void IndicateBeaconStatus( LoRaMacEventInfoStatus_t status )
{
if( Ctx.BeaconCtx.Ctrl.ResumeBeaconing == 0 )
{
Ctx.LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON;
Ctx.LoRaMacClassBParams.MlmeIndication->Status = status;
Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1;
Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1;
}
Ctx.BeaconCtx.Ctrl.ResumeBeaconing = 0;
}
static TimerTime_t ApplyGuardTime( TimerTime_t beaconEventTime )
{
TimerTime_t timeGuard = beaconEventTime;
if( timeGuard > CLASSB_BEACON_GUARD )
{
timeGuard -= CLASSB_BEACON_GUARD;
}
return timeGuard;
}
static TimerTime_t UpdateBeaconState( LoRaMacEventInfoStatus_t status,
TimerTime_t windowMovement, TimerTime_t currentTime )
{
TimerTime_t beaconEventTime = 0;
// Calculate the next beacon RX time
beaconEventTime = CalcDelayForNextBeacon( currentTime, SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) );
Ctx.BeaconCtx.NextBeaconRx = SysTimeFromMs( currentTime + beaconEventTime );
// Take temperature compensation into account
beaconEventTime = TimerTempCompensation( beaconEventTime, Ctx.BeaconCtx.Temperature );
// Move the window
if( beaconEventTime > windowMovement )
{
beaconEventTime -= windowMovement;
}
Ctx.BeaconCtx.NextBeaconRxAdjusted = currentTime + beaconEventTime;
// Start the RX slot state machine for ping and multicast slots
LoRaMacClassBStartRxSlots( );
// Setup an MLME_BEACON indication to inform the upper layer
IndicateBeaconStatus( status );
// Apply guard time
return ApplyGuardTime( beaconEventTime );
}
static uint8_t CalcPingNb( uint16_t periodicity )
{
return 128 / ( 1 << periodicity );
}
static uint16_t CalcPingPeriod( uint8_t pingNb )
{
return CLASSB_BEACON_WINDOW_SLOTS / pingNb;
}
static bool CheckSlotPriority( uint32_t currentAddress, uint8_t currentFPendingSet, uint8_t currentIsMulticast,
uint32_t address, uint8_t fPendingSet, uint8_t isMulticast )
{
if( currentFPendingSet != fPendingSet )
{
if( currentFPendingSet < fPendingSet )
{
// New slot sequence has priority. It does not matter
// which type it is
return true;
}
return false;
}
else
{
// FPendingSet has the same priority level, decide
// based on multicast or unicast setting
if( currentIsMulticast != isMulticast )
{
if( currentIsMulticast < isMulticast )
{
// New slot sequence has priority. Multicasts have
// more priority than unicasts
return true;
}
return false;
}
else
{
// IsMulticast has the same priority level, decide
// based on the highest address
if( currentAddress < address )
{
// New slot sequence has priority. The sequence with
// the highest address has priority
return true;
}
}
}
return false;
}
#endif // LORAMAC_CLASSB_ENABLED
void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks, LoRaMacClassBNvmData_t* nvm )
{
#ifdef LORAMAC_CLASSB_ENABLED
// Assign non-volatile context
if( nvm == NULL )
{
return;
}
ClassBNvm = nvm;
// Store callbacks
Ctx.LoRaMacClassBCallbacks = *callbacks;
// Store parameter pointers
Ctx.LoRaMacClassBParams = *classBParams;
// Initialize timers
TimerInit( &Ctx.BeaconTimer, LoRaMacClassBBeaconTimerEvent );
TimerInit( &Ctx.PingSlotTimer, LoRaMacClassBPingSlotTimerEvent );
TimerInit( &Ctx.MulticastSlotTimer, LoRaMacClassBMulticastSlotTimerEvent );
InitClassB( );
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBSetBeaconState( BeaconState_t beaconState )
{
#ifdef LORAMAC_CLASSB_ENABLED
if( beaconState == BEACON_STATE_ACQUISITION )
{
// If the MAC has received a time reference for the beacon,
// apply the state BEACON_STATE_ACQUISITION_BY_TIME.
if( ( Ctx.BeaconCtx.Ctrl.BeaconDelaySet == 1 ) &&
( LoRaMacClassBIsAcquisitionPending( ) == false ) )
{
Ctx.BeaconState = BEACON_STATE_ACQUISITION_BY_TIME;
}
else
{
Ctx.BeaconState = beaconState;
}
}
else
{
if( ( Ctx.BeaconState != BEACON_STATE_ACQUISITION ) &&
( Ctx.BeaconState != BEACON_STATE_ACQUISITION_BY_TIME ) )
{
Ctx.BeaconState = beaconState;
}
}
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBSetPingSlotState( PingSlotState_t pingSlotState )
{
#ifdef LORAMAC_CLASSB_ENABLED
Ctx.PingSlotState = pingSlotState;
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBSetMulticastSlotState( PingSlotState_t multicastSlotState )
{
#ifdef LORAMAC_CLASSB_ENABLED
Ctx.MulticastSlotState = multicastSlotState;
#endif // LORAMAC_CLASSB_ENABLED
}
bool LoRaMacClassBIsAcquisitionInProgress( void )
{
#ifdef LORAMAC_CLASSB_ENABLED
if( Ctx.BeaconState == BEACON_STATE_ACQUISITION_BY_TIME )
{
// In this case the acquisition is in progress, as the MAC has
// a time reference for the next beacon RX.
return true;
}
if( LoRaMacClassBIsAcquisitionPending( ) == true )
{
// In this case the acquisition is in progress, as the MAC
// searches for a beacon.
return true;
}
return false;
#else
return false;
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBBeaconTimerEvent( void* context )
{
#ifdef LORAMAC_CLASSB_ENABLED
Ctx.BeaconCtx.TimeStamp = TimerGetCurrentTime( );
TimerStop( &Ctx.BeaconTimer );
LoRaMacClassBEvents.Events.Beacon = 1;
if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL )
{
Ctx.LoRaMacClassBCallbacks.MacProcessNotify( );
}
#endif // LORAMAC_CLASSB_ENABLED
}
#ifdef LORAMAC_CLASSB_ENABLED
static void LoRaMacClassBProcessBeacon( void )
{
bool activateTimer = false;
TimerTime_t beaconEventTime = 1;
RxConfigParams_t beaconRxConfig;
TimerTime_t currentTime = Ctx.BeaconCtx.TimeStamp;
// Beacon state machine
switch( Ctx.BeaconState )
{
case BEACON_STATE_ACQUISITION_BY_TIME:
{
activateTimer = true;
if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 )
{
Radio.Sleep();
Ctx.BeaconState = BEACON_STATE_LOST;
}
else
{
// Default symbol timeouts
ResetWindowTimeout( );
if( Ctx.BeaconCtx.Ctrl.BeaconDelaySet == 1 )
{
// The goal is to calculate beaconRxConfig.WindowTimeout
CalculateBeaconRxWindowConfig( &beaconRxConfig, Ctx.BeaconCtx.SymbolTimeout );
if( Ctx.BeaconCtx.BeaconTimingDelay > 0 )
{
if( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) > currentTime )
{
// Calculate the time when we expect the next beacon
beaconEventTime = TimerTempCompensation( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - currentTime, Ctx.BeaconCtx.Temperature );
if( ( int32_t ) beaconEventTime > beaconRxConfig.WindowOffset )
{
// Apply the offset of the system error respectively beaconing precision setting
beaconEventTime += beaconRxConfig.WindowOffset;
}
}
else
{
// Reset status provides by BeaconTimingAns
Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 0;
Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 0;
Ctx.BeaconState = BEACON_STATE_ACQUISITION;
}
Ctx.BeaconCtx.BeaconTimingDelay = 0;
}
else
{
activateTimer = false;
// Reset status provides by BeaconTimingAns
Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 0;
// Set the node into acquisition mode
Ctx.BeaconCtx.Ctrl.AcquisitionPending = 1;
// Don't use the default channel. We know on which
// channel the next beacon will be transmitted
RxBeaconSetup( CLASSB_BEACON_RESERVED, false, beaconRxConfig.WindowTimeout );
}
}
else
{
Ctx.BeaconCtx.NextBeaconRx.Seconds = 0;
Ctx.BeaconCtx.NextBeaconRx.SubSeconds = 0;
Ctx.BeaconCtx.BeaconTimingDelay = 0;
Ctx.BeaconState = BEACON_STATE_ACQUISITION;
}
}
break;
}
case BEACON_STATE_ACQUISITION:
{
activateTimer = true;
if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 )
{
Radio.Sleep();
Ctx.BeaconState = BEACON_STATE_LOST;
}
else
{
// Default symbol timeouts
ResetWindowTimeout( );
Ctx.BeaconCtx.Ctrl.AcquisitionPending = 1;
beaconEventTime = CLASSB_BEACON_INTERVAL;
// The goal is to calculate beaconRxConfig.WindowTimeout
CalculateBeaconRxWindowConfig( &beaconRxConfig, Ctx.BeaconCtx.SymbolTimeout );
// Start the beacon acquisition. When the MAC has received a beacon in function
// RxBeacon successfully, the next state is BEACON_STATE_LOCKED. If the MAC does not
// find a beacon, the state machine will stay in state BEACON_STATE_ACQUISITION.
// This state detects that a acquisition was pending previously and will change the next
// state to BEACON_STATE_LOST.
RxBeaconSetup( 0, true, beaconRxConfig.WindowTimeout );
}
break;
}
case BEACON_STATE_TIMEOUT:
{
// We have to update the beacon time, since we missed a beacon
Ctx.BeaconCtx.BeaconTime.Seconds += ( CLASSB_BEACON_INTERVAL / 1000 );
Ctx.BeaconCtx.BeaconTime.SubSeconds = 0;
// Enlarge window timeouts to increase the chance to receive the next beacon
EnlargeWindowTimeout( );
// Setup next state
Ctx.BeaconState = BEACON_STATE_REACQUISITION;
}
// Intentional fall through
case BEACON_STATE_REACQUISITION:
{
activateTimer = true;
// The beacon is no longer acquired
Ctx.BeaconCtx.Ctrl.BeaconAcquired = 0;
// Verify if the maximum beacon less period has been elapsed
if( ( currentTime - SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) ) > CLASSB_MAX_BEACON_LESS_PERIOD )
{
Ctx.BeaconState = BEACON_STATE_LOST;
}
else
{
// Handle beacon miss
beaconEventTime = UpdateBeaconState( LORAMAC_EVENT_INFO_STATUS_BEACON_LOST,
Ctx.BeaconCtx.BeaconWindowMovement, currentTime );
// Setup next state
Ctx.BeaconState = BEACON_STATE_IDLE;
}
break;
}
case BEACON_STATE_LOCKED:
{
activateTimer = true;
// We have received a beacon. Acquisition is no longer pending.
Ctx.BeaconCtx.Ctrl.AcquisitionPending = 0;
// Handle beacon reception
beaconEventTime = UpdateBeaconState( LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED,
0, currentTime );
// Setup the MLME confirm for the MLME_BEACON_ACQUISITION
if( Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 )
{
if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true )
{
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_ACQUISITION );
Ctx.LoRaMacClassBParams.MlmeConfirm->TxTimeOnAir = 0;
}
}
// Setup next state
Ctx.BeaconState = BEACON_STATE_IDLE;
break;
}
case BEACON_STATE_IDLE:
{
activateTimer = true;
GetTemperatureLevel( &Ctx.LoRaMacClassBCallbacks, &Ctx.BeaconCtx );
beaconEventTime = Ctx.BeaconCtx.NextBeaconRxAdjusted - Radio.GetWakeupTime( );
currentTime = TimerGetCurrentTime( );
// The goal is to calculate beaconRxConfig.WindowTimeout and beaconRxConfig.WindowOffset
CalculateBeaconRxWindowConfig( &beaconRxConfig, Ctx.BeaconCtx.SymbolTimeout );
if( beaconEventTime > currentTime )
{
Ctx.BeaconState = BEACON_STATE_GUARD;
beaconEventTime -= currentTime;
beaconEventTime = TimerTempCompensation( beaconEventTime, Ctx.BeaconCtx.Temperature );
if( ( int32_t ) beaconEventTime > beaconRxConfig.WindowOffset )
{
// Apply the offset of the system error respectively beaconing precision setting
beaconEventTime += beaconRxConfig.WindowOffset;
}
}
else
{
Ctx.BeaconState = BEACON_STATE_REACQUISITION;
beaconEventTime = 1;
}
break;
}
case BEACON_STATE_GUARD:
{
Ctx.BeaconState = BEACON_STATE_RX;
// Stop slot timers
LoRaMacClassBStopRxSlots( );
// Don't use the default channel. We know on which
// channel the next beacon will be transmitted
RxBeaconSetup( CLASSB_BEACON_RESERVED, false, beaconRxConfig.WindowTimeout );
break;
}
case BEACON_STATE_LOST:
{
// Handle events
if( Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 )
{
if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true )
{
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_BEACON_ACQUISITION );
}
}
else
{
Ctx.LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON_LOST;
Ctx.LoRaMacClassBParams.MlmeIndication->Status = LORAMAC_EVENT_INFO_STATUS_OK;
Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1;
}
// Stop slot timers
LoRaMacClassBStopRxSlots( );
// Initialize default state for class b
InitClassBDefaults( );
Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1;
break;
}
default:
{
Ctx.BeaconState = BEACON_STATE_ACQUISITION;
break;
}
}
if( activateTimer == true )
{
TimerSetValue( &Ctx.BeaconTimer, beaconEventTime );
TimerStart( &Ctx.BeaconTimer );
}
}
#endif // LORAMAC_CLASSB_ENABLED
void LoRaMacClassBPingSlotTimerEvent( void* context )
{
#ifdef LORAMAC_CLASSB_ENABLED
LoRaMacClassBEvents.Events.PingSlot = 1;
if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL )
{
Ctx.LoRaMacClassBCallbacks.MacProcessNotify( );
}
#endif // LORAMAC_CLASSB_ENABLED
}
#ifdef LORAMAC_CLASSB_ENABLED
static void LoRaMacClassBProcessPingSlot( void )
{
static RxConfigParams_t pingSlotRxConfig;
TimerTime_t pingSlotTime = 0;
uint32_t maxRxError = 0;
bool slotHasPriority = false;
switch( Ctx.PingSlotState )
{
case PINGSLOT_STATE_CALC_PING_OFFSET:
{
ComputePingOffset( Ctx.BeaconCtx.BeaconTime.Seconds,
*Ctx.LoRaMacClassBParams.LoRaMacDevAddr,
ClassBNvm->PingSlotCtx.PingPeriod,
&( Ctx.PingSlotCtx.PingOffset ) );
Ctx.PingSlotState = PINGSLOT_STATE_SET_TIMER;
}
// Intentional fall through
case PINGSLOT_STATE_SET_TIMER:
{
if( CalcNextSlotTime( Ctx.PingSlotCtx.PingOffset, ClassBNvm->PingSlotCtx.PingPeriod, ClassBNvm->PingSlotCtx.PingNb, &pingSlotTime ) == true )
{
if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 )
{
// Compare and assign the maximum between the region specific rx error window time
// and time precision received from beacon frame format.
maxRxError = MAX( Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError ,
( uint32_t ) Ctx.BeaconCtx.BeaconTimePrecision.SubSeconds );
// Compute the symbol timeout. Apply it only, if the beacon is acquired
// Otherwise, take the enlargement of the symbols into account.
RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion,
ClassBNvm->PingSlotCtx.Datarate,
Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
maxRxError,
&pingSlotRxConfig );
Ctx.PingSlotCtx.SymbolTimeout = pingSlotRxConfig.WindowTimeout;
if( ( int32_t )pingSlotTime > pingSlotRxConfig.WindowOffset )
{// Apply the window offset
pingSlotTime += pingSlotRxConfig.WindowOffset;
}
}
// Start the timer if the ping slot time is in range
Ctx.PingSlotState = PINGSLOT_STATE_IDLE;
TimerSetValue( &Ctx.PingSlotTimer, pingSlotTime );
TimerStart( &Ctx.PingSlotTimer );
}
break;
}
case PINGSLOT_STATE_IDLE:
{
uint32_t frequency = ClassBNvm->PingSlotCtx.Frequency;
// Apply a custom frequency if the following bit is set
if( ClassBNvm->PingSlotCtx.Ctrl.CustomFreq == 0 )
{
// Restore floor plan
frequency = CalcDownlinkChannelAndFrequency( *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, Ctx.BeaconCtx.BeaconTime.Seconds,
CLASSB_BEACON_INTERVAL, false );
}
if( Ctx.PingSlotCtx.NextMulticastChannel != NULL )
{
// Verify, if the unicast has priority.
slotHasPriority = CheckSlotPriority( *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, ClassBNvm->PingSlotCtx.FPendingSet, 0,
Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.Address, Ctx.PingSlotCtx.NextMulticastChannel->FPendingSet, 1 );
}
// Open the ping slot window only, if there is no multicast ping slot
// open or if the unicast has priority.
if( ( Ctx.MulticastSlotState != PINGSLOT_STATE_RX ) || ( slotHasPriority == true ) )
{
if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX )
{
// Close multicast slot window, if necessary. Multicast slots have priority
Radio.Standby( );
Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
TimerSetValue( &Ctx.MulticastSlotTimer, CLASSB_PING_SLOT_WINDOW );
TimerStart( &Ctx.MulticastSlotTimer );
}
Ctx.PingSlotState = PINGSLOT_STATE_RX;
pingSlotRxConfig.Datarate = ClassBNvm->PingSlotCtx.Datarate;
pingSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
pingSlotRxConfig.Frequency = frequency;
pingSlotRxConfig.RxContinuous = false;
pingSlotRxConfig.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT;
pingSlotRxConfig.NetworkActivation = *Ctx.LoRaMacClassBParams.NetworkActivation;
RegionRxConfig( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &pingSlotRxConfig, ( int8_t* )&Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate );
if( pingSlotRxConfig.RxContinuous == false )
{
Radio.Rx( Ctx.LoRaMacClassBParams.LoRaMacParams->MaxRxWindow );
}
else
{
Radio.Rx( 0 ); // Continuous mode
}
}
else
{
// Multicast slots have priority. Skip Rx
Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
TimerSetValue( &Ctx.PingSlotTimer, CLASSB_PING_SLOT_WINDOW );
TimerStart( &Ctx.PingSlotTimer );
}
break;
}
default:
{
Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
break;
}
}
}
#endif // LORAMAC_CLASSB_ENABLED
void LoRaMacClassBMulticastSlotTimerEvent( void* context )
{
#ifdef LORAMAC_CLASSB_ENABLED
LoRaMacClassBEvents.Events.MulticastSlot = 1;
if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL )
{
Ctx.LoRaMacClassBCallbacks.MacProcessNotify( );
}
#endif // LORAMAC_CLASSB_ENABLED
}
#ifdef LORAMAC_CLASSB_ENABLED
static void LoRaMacClassBProcessMulticastSlot( void )
{
static RxConfigParams_t multicastSlotRxConfig;
TimerTime_t multicastSlotTime = 0;
TimerTime_t slotTime = 0;
uint32_t maxRxError = 0;
MulticastCtx_t *cur = Ctx.LoRaMacClassBParams.MulticastChannels;
bool slotHasPriority = false;
if( cur == NULL )
{
return;
}
if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX )
{
// A multicast slot is already open
return;
}
switch( Ctx.MulticastSlotState )
{
case PINGSLOT_STATE_CALC_PING_OFFSET:
{
// Compute all offsets for every multicast slots
for( uint8_t i = 0; i < 4; i++ )
{
ComputePingOffset( Ctx.BeaconCtx.BeaconTime.Seconds,
cur->ChannelParams.Address,
cur->PingPeriod,
&( cur->PingOffset ) );
cur++;
}
Ctx.MulticastSlotState = PINGSLOT_STATE_SET_TIMER;
}
// Intentional fall through
case PINGSLOT_STATE_SET_TIMER:
{
cur = Ctx.LoRaMacClassBParams.MulticastChannels;
Ctx.PingSlotCtx.NextMulticastChannel = NULL;
for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
{
// Calculate the next slot time for every multicast slot
if( CalcNextSlotTime( cur->PingOffset, cur->PingPeriod, cur->PingNb, &slotTime ) == true )
{
if( ( multicastSlotTime == 0 ) || ( multicastSlotTime > slotTime ) )
{
// Update the slot time and the next multicast channel
multicastSlotTime = slotTime;
Ctx.PingSlotCtx.NextMulticastChannel = cur;
}
}
cur++;
}
// Schedule the next multicast slot
if( Ctx.PingSlotCtx.NextMulticastChannel != NULL )
{
if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 )
{
// Compare and assign the maximum between the region specific rx error window time
// and time precision received from beacon frame format.
maxRxError = MAX( Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError ,
( uint32_t ) Ctx.BeaconCtx.BeaconTimePrecision.SubSeconds );
RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion,
ClassBNvm->PingSlotCtx.Datarate,
Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
maxRxError,
&multicastSlotRxConfig );
Ctx.PingSlotCtx.SymbolTimeout = multicastSlotRxConfig.WindowTimeout;
}
if( ( int32_t )multicastSlotTime > multicastSlotRxConfig.WindowOffset )
{// Apply the window offset
multicastSlotTime += multicastSlotRxConfig.WindowOffset;
}
// Start the timer if the ping slot time is in range
Ctx.MulticastSlotState = PINGSLOT_STATE_IDLE;
TimerSetValue( &Ctx.MulticastSlotTimer, multicastSlotTime );
TimerStart( &Ctx.MulticastSlotTimer );
}
break;
}
case PINGSLOT_STATE_IDLE:
{
uint32_t frequency = 0;
// Verify if the multicast channel is valid
if( Ctx.PingSlotCtx.NextMulticastChannel == NULL )
{
Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
TimerSetValue( &Ctx.MulticastSlotTimer, 1 );
TimerStart( &Ctx.MulticastSlotTimer );
break;
}
// Apply frequency
frequency = Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.RxParams.ClassB.Frequency;
// Restore the floor plan frequency if there is no individual frequency assigned
if( frequency == 0 )
{
// Restore floor plan
frequency = CalcDownlinkChannelAndFrequency( Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.Address,
Ctx.BeaconCtx.BeaconTime.Seconds, CLASSB_BEACON_INTERVAL, false );
}
// Verify, if the unicast has priority.
slotHasPriority = CheckSlotPriority( Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.Address, Ctx.PingSlotCtx.NextMulticastChannel->FPendingSet, 1,
*Ctx.LoRaMacClassBParams.LoRaMacDevAddr, ClassBNvm->PingSlotCtx.FPendingSet, 0 );
// Open the ping slot window only, if there is no multicast ping slot
// open or if the unicast has priority.
if( ( Ctx.PingSlotState != PINGSLOT_STATE_RX ) || ( slotHasPriority == true ) )
{
if( Ctx.PingSlotState == PINGSLOT_STATE_RX )
{
// Close ping slot window, if necessary. Multicast slots have priority
Radio.Standby( );
Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
TimerSetValue( &Ctx.PingSlotTimer, CLASSB_PING_SLOT_WINDOW );
TimerStart( &Ctx.PingSlotTimer );
}
Ctx.MulticastSlotState = PINGSLOT_STATE_RX;
multicastSlotRxConfig.Datarate = Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.RxParams.ClassB.Datarate;
multicastSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
multicastSlotRxConfig.Frequency = frequency;
multicastSlotRxConfig.RxContinuous = false;
multicastSlotRxConfig.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT;
multicastSlotRxConfig.NetworkActivation = *Ctx.LoRaMacClassBParams.NetworkActivation;
RegionRxConfig( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &multicastSlotRxConfig, ( int8_t* )&Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate );
if( multicastSlotRxConfig.RxContinuous == false )
{
Radio.Rx( Ctx.LoRaMacClassBParams.LoRaMacParams->MaxRxWindow );
}
else
{
Radio.Rx( 0 ); // Continuous mode
}
}
else
{
// Unicast slots have priority. Skip Rx
Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
TimerSetValue( &Ctx.MulticastSlotTimer, CLASSB_PING_SLOT_WINDOW );
TimerStart( &Ctx.MulticastSlotTimer );
}
break;
}
default:
{
Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
break;
}
}
}
#endif // LORAMAC_CLASSB_ENABLED
bool LoRaMacClassBRxBeacon( uint8_t *payload, uint16_t size )
{
#ifdef LORAMAC_CLASSB_ENABLED
GetPhyParams_t getPhy;
PhyParam_t phyParam;
bool beaconProcessed = false;
uint16_t crc0 = 0;
uint16_t crc1 = 0;
uint16_t beaconCrc0 = 0;
uint16_t beaconCrc1 = 0;
getPhy.Attribute = PHY_BEACON_FORMAT;
phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
// Verify if we are in the state where we expect a beacon
if( ( Ctx.BeaconState == BEACON_STATE_RX ) || ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) )
{
if( size == phyParam.BeaconFormat.BeaconSize )
{
// A beacon frame is defined as:
// Bytes: | x | 1 | 4 | 2 | 7 | y | 2 |
// |------|-------|------|------|------------|------|------|
// Field: | RFU1 | Param | Time | CRC1 | GwSpecific | RFU2 | CRC2 |
//
// Field RFU1 and RFU2 have variable sizes. It depends on the region specific implementation
// Read CRC1 field from the frame
beaconCrc0 = ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4] ) & 0x00FF;
beaconCrc0 |= ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 1] << 8 ) & 0xFF00;
crc0 = BeaconCrc( payload, phyParam.BeaconFormat.Rfu1Size + 1 + 4 );
// Validate the first crc of the beacon frame
if( crc0 == beaconCrc0 )
{
// Copy the param field for app layer
Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Param = ( payload[phyParam.BeaconFormat.Rfu1Size] );
// Fetch the precise time value in milliseconds that will be used for Rx ping slot delay.
Ctx.BeaconCtx.BeaconTimePrecision.SubSeconds = BeaconPrecTimeValue[Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Param];
// Read Time field from the frame
Ctx.BeaconCtx.BeaconTime.Seconds = ( ( uint32_t )payload[phyParam.BeaconFormat.Rfu1Size + 1] ) & 0x000000FF;
Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 2] << 8 ) ) & 0x0000FF00;
Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 3] << 16 ) ) & 0x00FF0000;
Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 4] << 24 ) ) & 0xFF000000;
Ctx.BeaconCtx.BeaconTime.SubSeconds = 0;
Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Time = Ctx.BeaconCtx.BeaconTime;
beaconProcessed = true;
}
// Read CRC2 field from the frame
beaconCrc1 = ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 2 + 7 + phyParam.BeaconFormat.Rfu2Size] ) & 0x00FF;
beaconCrc1 |= ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 2 + 7 + phyParam.BeaconFormat.Rfu2Size + 1] << 8 ) & 0xFF00;
crc1 = BeaconCrc( &payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 2], 7 + phyParam.BeaconFormat.Rfu2Size );
// Validate the second crc of the beacon frame
if( crc1 == beaconCrc1 )
{
// Read GwSpecific field from the frame
// The GwSpecific field contains 1 byte InfoDesc and 6 bytes Info
Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.InfoDesc = payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 2];
memcpy1( Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.Info, &payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 2 + 1], 6 );
}
// Reset beacon variables, if one of the crc is valid
if( beaconProcessed == true )
{
uint32_t spreadingFactor = 0;
uint32_t bandwith = 0;
getPhy.Attribute = PHY_BEACON_CHANNEL_DR;
phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
getPhy.Attribute = PHY_SF_FROM_DR;
getPhy.Datarate = phyParam.Value;
phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
spreadingFactor = phyParam.Value;
getPhy.Attribute = PHY_BW_FROM_DR;
phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
bandwith = phyParam.Value;
TimerTime_t time = Radio.TimeOnAir( MODEM_LORA, bandwith, spreadingFactor, 1, 10, true, size, false );
SysTime_t timeOnAir;
timeOnAir.Seconds = time / 1000;
timeOnAir.SubSeconds = time - timeOnAir.Seconds * 1000;
Ctx.BeaconCtx.LastBeaconRx = Ctx.BeaconCtx.BeaconTime;
Ctx.BeaconCtx.LastBeaconRx.Seconds += UNIX_GPS_EPOCH_OFFSET;
// Update system time.
SysTimeSet( SysTimeAdd( Ctx.BeaconCtx.LastBeaconRx, timeOnAir ) );
Ctx.BeaconCtx.Ctrl.BeaconAcquired = 1;
Ctx.BeaconCtx.Ctrl.BeaconMode = 1;
ResetWindowTimeout( );
Ctx.BeaconState = BEACON_STATE_LOCKED;
LoRaMacClassBBeaconTimerEvent( NULL );
}
}
if( Ctx.BeaconState == BEACON_STATE_RX )
{
Ctx.BeaconState = BEACON_STATE_TIMEOUT;
LoRaMacClassBBeaconTimerEvent( NULL );
}
// When the MAC listens for a beacon, it is not allowed to process any other
// downlink except the beacon frame itself. The reason for this is that no valid downlink window is open.
// If it receives a frame which is
// 1. not a beacon or
// 2. a beacon with a crc fail
// the MAC shall ignore the frame completely. Thus, the function must always return true, even if no
// valid beacon has been received.
beaconProcessed = true;
}
return beaconProcessed;
#else
return false;
#endif // LORAMAC_CLASSB_ENABLED
}
bool LoRaMacClassBIsBeaconExpected( void )
{
#ifdef LORAMAC_CLASSB_ENABLED
if( ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) ||
( Ctx.BeaconState == BEACON_STATE_RX ) )
{
return true;
}
return false;
#else
return false;
#endif // LORAMAC_CLASSB_ENABLED
}
bool LoRaMacClassBIsPingExpected( void )
{
#ifdef LORAMAC_CLASSB_ENABLED
if( Ctx.PingSlotState == PINGSLOT_STATE_RX )
{
return true;
}
return false;
#else
return false;
#endif // LORAMAC_CLASSB_ENABLED
}
bool LoRaMacClassBIsMulticastExpected( void )
{
#ifdef LORAMAC_CLASSB_ENABLED
if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX )
{
return true;
}
return false;
#else
return false;
#endif // LORAMAC_CLASSB_ENABLED
}
bool LoRaMacClassBIsAcquisitionPending( void )
{
#ifdef LORAMAC_CLASSB_ENABLED
if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 )
{
return true;
}
return false;
#else
return false;
#endif // LORAMAC_CLASSB_ENABLED
}
bool LoRaMacClassBIsBeaconModeActive( void )
{
#ifdef LORAMAC_CLASSB_ENABLED
if( ( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) ||
( Ctx.BeaconState == BEACON_STATE_ACQUISITION_BY_TIME ) )
{
return true;
}
return false;
#else
return false;
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBSetPingSlotInfo( uint8_t periodicity )
{
#ifdef LORAMAC_CLASSB_ENABLED
ClassBNvm->PingSlotCtx.PingNb = CalcPingNb( periodicity );
ClassBNvm->PingSlotCtx.PingPeriod = CalcPingPeriod( ClassBNvm->PingSlotCtx.PingNb );
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBHaltBeaconing( void )
{
#ifdef LORAMAC_CLASSB_ENABLED
if( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 )
{
if( ( Ctx.BeaconState == BEACON_STATE_TIMEOUT ) ||
( Ctx.BeaconState == BEACON_STATE_LOST ) )
{
// Update the state machine before halt
LoRaMacClassBBeaconTimerEvent( NULL );
}
CRITICAL_SECTION_BEGIN( );
LoRaMacClassBEvents.Events.Beacon = 0;
CRITICAL_SECTION_END( );
// Halt ping slot state machine
TimerStop( &Ctx.BeaconTimer );
// Halt beacon state machine
Ctx.BeaconState = BEACON_STATE_HALT;
// Halt ping and multicast slot state machines
LoRaMacClassBStopRxSlots( );
}
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBResumeBeaconing( void )
{
#ifdef LORAMAC_CLASSB_ENABLED
if( Ctx.BeaconState == BEACON_STATE_HALT )
{
Ctx.BeaconCtx.Ctrl.ResumeBeaconing = 1;
// Set default state
Ctx.BeaconState = BEACON_STATE_LOCKED;
if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 0 )
{
// Set the default state for beacon less operation
Ctx.BeaconState = BEACON_STATE_REACQUISITION;
}
LoRaMacClassBBeaconTimerEvent( NULL );
}
#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( ( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) && ( ClassBNvm->PingSlotCtx.Ctrl.Assigned == 1 ) )
{
return LORAMAC_STATUS_OK;
}
}
if( nextClass == CLASS_A )
{// Switch from class b to class a
LoRaMacClassBHaltBeaconing( );
// Initialize default state for class b
InitClassBDefaults( );
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_PING_SLOT_DATARATE:
{
mibGet->Param.PingSlotDatarate = ClassBNvm->PingSlotCtx.Datarate;
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_PING_SLOT_DATARATE:
{
ClassBNvm->PingSlotCtx.Datarate = mibSet->Param.PingSlotDatarate;
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
if( LoRaMacConfirmQueueIsCmdActive( MLME_PING_SLOT_INFO ) == true )
{
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_PING_SLOT_INFO );
ClassBNvm->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;
bool isCustomFreq = false;
if( frequency != 0 )
{
isCustomFreq = true;
verify.Frequency = frequency;
if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_FREQUENCY ) == false )
{
status &= 0xFE; // Channel frequency KO
}
}
verify.DatarateParams.Datarate = datarate;
verify.DatarateParams.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_RX_DR ) == false )
{
status &= 0xFD; // Datarate range KO
}
if( status == 0x03 )
{
if( isCustomFreq == true )
{
ClassBNvm->PingSlotCtx.Ctrl.CustomFreq = 1;
ClassBNvm->PingSlotCtx.Frequency = frequency;
}
else
{
ClassBNvm->PingSlotCtx.Ctrl.CustomFreq = 0;
ClassBNvm->PingSlotCtx.Frequency = 0;
}
ClassBNvm->PingSlotCtx.Datarate = datarate;
}
return status;
#else
return 0;
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel, TimerTime_t lastRxDone )
{
#ifdef LORAMAC_CLASSB_ENABLED
Ctx.BeaconCtx.BeaconTimingDelay = ( CLASSB_BEACON_DELAY_BEACON_TIMING_ANS * beaconTimingDelay );
Ctx.BeaconCtx.BeaconTimingChannel = beaconTimingChannel;
if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_TIMING ) == true )
{
if( Ctx.BeaconCtx.BeaconTimingDelay > CLASSB_BEACON_INTERVAL )
{
// We missed the beacon already
Ctx.BeaconCtx.BeaconTimingDelay = 0;
Ctx.BeaconCtx.BeaconTimingChannel = 0;
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_BEACON_TIMING );
}
else
{
Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 1;
Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 1;
Ctx.BeaconCtx.NextBeaconRx = SysTimeFromMs( lastRxDone + Ctx.BeaconCtx.BeaconTimingDelay );
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_TIMING );
}
Ctx.LoRaMacClassBParams.MlmeConfirm->BeaconTimingDelay = Ctx.BeaconCtx.BeaconTimingDelay;
Ctx.LoRaMacClassBParams.MlmeConfirm->BeaconTimingChannel = Ctx.BeaconCtx.BeaconTimingChannel;
}
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBDeviceTimeAns( void )
{
#ifdef LORAMAC_CLASSB_ENABLED
SysTime_t nextBeacon = SysTimeGet( );
uint32_t currentTimeMs = SysTimeToMs( nextBeacon );
nextBeacon.Seconds = nextBeacon.Seconds + ( 128 - ( nextBeacon.Seconds % 128 ) );
nextBeacon.SubSeconds = 0;
Ctx.BeaconCtx.NextBeaconRx = nextBeacon;
Ctx.BeaconCtx.LastBeaconRx = SysTimeSub( Ctx.BeaconCtx.NextBeaconRx, ( SysTime_t ){ .Seconds = CLASSB_BEACON_INTERVAL / 1000, .SubSeconds = 0 } );
if( LoRaMacConfirmQueueIsCmdActive( MLME_DEVICE_TIME ) == true )
{
if( currentTimeMs > SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) )
{
// We missed the beacon already
Ctx.BeaconCtx.LastBeaconRx.Seconds = 0;
Ctx.BeaconCtx.LastBeaconRx.SubSeconds = 0;
Ctx.BeaconCtx.NextBeaconRx.Seconds = 0;
Ctx.BeaconCtx.NextBeaconRx.SubSeconds = 0;
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_DEVICE_TIME );
}
else
{
Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 1;
Ctx.BeaconCtx.BeaconTimingDelay = SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - currentTimeMs;
Ctx.BeaconCtx.BeaconTime.Seconds = nextBeacon.Seconds - UNIX_GPS_EPOCH_OFFSET - 128;
Ctx.BeaconCtx.BeaconTime.SubSeconds = 0;
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_DEVICE_TIME );
}
}
#endif // LORAMAC_CLASSB_ENABLED
}
bool LoRaMacClassBBeaconFreqReq( uint32_t frequency )
{
#ifdef LORAMAC_CLASSB_ENABLED
VerifyParams_t verify;
if( frequency != 0 )
{
verify.Frequency = frequency;
if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_FREQUENCY ) == true )
{
ClassBNvm->BeaconCtx.Ctrl.CustomFreq = 1;
ClassBNvm->BeaconCtx.Frequency = frequency;
return true;
}
}
else
{
ClassBNvm->BeaconCtx.Ctrl.CustomFreq = 0;
return true;
}
return false;
#else
return false;
#endif // LORAMAC_CLASSB_ENABLED
}
TimerTime_t LoRaMacClassBIsUplinkCollision( TimerTime_t txTimeOnAir )
{
#ifdef LORAMAC_CLASSB_ENABLED
TimerTime_t currentTime = TimerGetCurrentTime( );
TimerTime_t beaconReserved = 0;
TimerTime_t nextBeacon = SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx );
beaconReserved = nextBeacon -
CLASSB_BEACON_GUARD -
Ctx.LoRaMacClassBParams.LoRaMacParams->ReceiveDelay1 -
Ctx.LoRaMacClassBParams.LoRaMacParams->ReceiveDelay2 -
txTimeOnAir;
// Check if the next beacon will be received during the next uplink.
if( ( currentTime >= beaconReserved ) && ( currentTime < ( nextBeacon + CLASSB_BEACON_RESERVED ) ) )
{// Next beacon will be sent during the next uplink.
return CLASSB_BEACON_RESERVED;
}
return 0;
#else
return 0;
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBStopRxSlots( void )
{
#ifdef LORAMAC_CLASSB_ENABLED
TimerStop( &Ctx.PingSlotTimer );
TimerStop( &Ctx.MulticastSlotTimer );
CRITICAL_SECTION_BEGIN( );
LoRaMacClassBEvents.Events.PingSlot = 0;
LoRaMacClassBEvents.Events.MulticastSlot = 0;
CRITICAL_SECTION_END( );
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBStartRxSlots( void )
{
#ifdef LORAMAC_CLASSB_ENABLED
if( ClassBNvm->PingSlotCtx.Ctrl.Assigned == 1 )
{
Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
TimerSetValue( &Ctx.PingSlotTimer, 1 );
TimerStart( &Ctx.PingSlotTimer );
Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
TimerSetValue( &Ctx.MulticastSlotTimer, 1 );
TimerStart( &Ctx.MulticastSlotTimer );
}
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBSetMulticastPeriodicity( MulticastCtx_t* multicastChannel )
{
#ifdef LORAMAC_CLASSB_ENABLED
if( multicastChannel != NULL )
{
multicastChannel->PingNb = CalcPingNb( multicastChannel->ChannelParams.RxParams.ClassB.Periodicity );
multicastChannel->PingPeriod = CalcPingPeriod( multicastChannel->PingNb );
}
#endif // LORAMAC_CLASSB_ENABLED
}
void LoRaMacClassBSetFPendingBit( uint32_t address, uint8_t fPendingSet )
{
#ifdef LORAMAC_CLASSB_ENABLED
MulticastCtx_t *cur = Ctx.LoRaMacClassBParams.MulticastChannels;
if( address == *Ctx.LoRaMacClassBParams.LoRaMacDevAddr )
{
// Unicast
ClassBNvm->PingSlotCtx.FPendingSet = fPendingSet;
}
else
{
for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
{
if( cur != NULL )
{
// Set the fPending bit, if its a multicast
if( address == cur->ChannelParams.Address )
{
cur->FPendingSet = fPendingSet;
}
}
cur++;
}
}
#endif
}
void LoRaMacClassBProcess( void )
{
#ifdef LORAMAC_CLASSB_ENABLED
LoRaMacClassBEvents_t events;
CRITICAL_SECTION_BEGIN( );
events = LoRaMacClassBEvents;
LoRaMacClassBEvents.Value = 0;
CRITICAL_SECTION_END( );
if( events.Value != 0 )
{
if( events.Events.Beacon == 1 )
{
LoRaMacClassBProcessBeacon( );
}
if( events.Events.PingSlot == 1 )
{
LoRaMacClassBProcessPingSlot( );
}
if( events.Events.MulticastSlot == 1 )
{
LoRaMacClassBProcessMulticastSlot( );
}
}
#endif // LORAMAC_CLASSB_ENABLED
}
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарий ( 0 )