1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/lupyuen-LoRaMac-node-nuttx

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Это зеркальный репозиторий, синхронизируется ежедневно с исходного репозитория.
Клонировать/Скачать
LoRaMacClassB.c 65 КБ
Копировать Редактировать Исходные данные Просмотреть построчно История
Miguel Luis Отправлено 4 лет назад 243864e
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(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 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/lupyuen-LoRaMac-node-nuttx.git
git@gitlife.ru:oschina-mirror/lupyuen-LoRaMac-node-nuttx.git
oschina-mirror
lupyuen-LoRaMac-node-nuttx
lupyuen-LoRaMac-node-nuttx
v4.5.2