427 lines
14 KiB
C
427 lines
14 KiB
C
/*!
|
|
* \file LmhpClockSync.c
|
|
*
|
|
* \brief Implements the LoRa-Alliance clock synchronization package
|
|
* Specification: https://lora-alliance.org/sites/default/files/2018-09/application_layer_clock_synchronization_v1.0.0.pdf
|
|
*
|
|
* \copyright Revised BSD License, see section \ref LICENSE.
|
|
*
|
|
* \code
|
|
* ______ _
|
|
* / _____) _ | |
|
|
* ( (____ _____ ____ _| |_ _____ ____| |__
|
|
* \____ \| ___ | (_ _) ___ |/ ___) _ \
|
|
* _____) ) ____| | | || |_| ____( (___| | | |
|
|
* (______/|_____)_|_|_| \__)_____)\____)_| |_|
|
|
* (C)2013-2018 Semtech
|
|
*
|
|
* \endcode
|
|
*
|
|
* \author Miguel Luis ( Semtech )
|
|
*/
|
|
/**
|
|
******************************************************************************
|
|
*
|
|
* Portions COPYRIGHT 2020 STMicroelectronics
|
|
*
|
|
* @file LmhpClockSync.c
|
|
* @author MCD Application Team
|
|
* @brief Clock Synchronisation Package definition
|
|
******************************************************************************
|
|
*/
|
|
#include "systime.h"
|
|
#include "LmHandler.h"
|
|
#include "LmhpClockSync.h"
|
|
#include "utilities.h"
|
|
|
|
/*!
|
|
* LoRaWAN Application Layer Clock Synchronization Specification
|
|
*/
|
|
#define CLOCK_SYNC_PORT 202
|
|
|
|
#define CLOCK_SYNC_ID 1
|
|
#define CLOCK_SYNC_VERSION 1
|
|
|
|
/*!
|
|
* Package current context
|
|
*/
|
|
typedef struct LmhpClockSyncState_s
|
|
{
|
|
bool Initialized;
|
|
bool IsRunning;
|
|
uint8_t DataBufferMaxSize;
|
|
uint8_t *DataBuffer;
|
|
union
|
|
{
|
|
uint8_t Value;
|
|
struct
|
|
{
|
|
uint8_t TokenReq: 4;
|
|
uint8_t AnsRequired: 1;
|
|
uint8_t RFU: 3;
|
|
}Fields;
|
|
}TimeReqParam;
|
|
bool AppTimeReqPending;
|
|
bool AdrEnabledPrev;
|
|
uint8_t NbTransPrev;
|
|
uint8_t DataratePrev;
|
|
uint8_t NbTransmissions;
|
|
}LmhpClockSyncState_t;
|
|
|
|
typedef enum LmhpClockSyncMoteCmd_e
|
|
{
|
|
CLOCK_SYNC_PKG_VERSION_ANS = 0x00,
|
|
CLOCK_SYNC_APP_TIME_REQ = 0x01,
|
|
CLOCK_SYNC_APP_TIME_PERIOD_ANS = 0x02,
|
|
CLOCK_SYNC_FORCE_RESYNC_ANS = 0x03,
|
|
}LmhpClockSyncMoteCmd_t;
|
|
|
|
typedef enum LmhpClockSyncSrvCmd_e
|
|
{
|
|
CLOCK_SYNC_PKG_VERSION_REQ = 0x00,
|
|
CLOCK_SYNC_APP_TIME_ANS = 0x01,
|
|
CLOCK_SYNC_APP_TIME_PERIOD_REQ = 0x02,
|
|
CLOCK_SYNC_FORCE_RESYNC_REQ = 0x03,
|
|
}LmhpClockSyncSrvCmd_t;
|
|
|
|
/*!
|
|
* Initializes the package with provided parameters
|
|
*
|
|
* \param [IN] params Pointer to the package parameters
|
|
* \param [IN] dataBuffer Pointer to main application buffer
|
|
* \param [IN] dataBufferMaxSize Main application buffer maximum size
|
|
*/
|
|
static void LmhpClockSyncInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize );
|
|
|
|
/*!
|
|
* Returns the current package initialization status.
|
|
*
|
|
* \retval status Package initialization status
|
|
* [true: Initialized, false: Not initialized]
|
|
*/
|
|
static bool LmhpClockSyncIsInitialized( void );
|
|
|
|
/*!
|
|
* Returns the package operation status.
|
|
*
|
|
* \retval status Package operation status
|
|
* [true: Running, false: Not running]
|
|
*/
|
|
static bool LmhpClockSyncIsRunning( void );
|
|
|
|
/*!
|
|
* Processes the internal package events.
|
|
*/
|
|
static void LmhpClockSyncProcess( void );
|
|
|
|
/*!
|
|
* Processes the MCSP Confirm
|
|
*
|
|
* \param [IN] mcpsConfirm MCPS confirmation primitive data
|
|
*/
|
|
static void LmhpClockSyncOnMcpsConfirm( McpsConfirm_t *mcpsConfirm );
|
|
|
|
/*!
|
|
* Processes the MCPS Indication
|
|
*
|
|
* \param [IN] mcpsIndication MCPS indication primitive data
|
|
*/
|
|
static void LmhpClockSyncOnMcpsIndication( McpsIndication_t *mcpsIndication );
|
|
|
|
static void OnPeriodicTimeStartTimer(void *context);
|
|
|
|
static LmhpClockSyncState_t LmhpClockSyncState =
|
|
{
|
|
.Initialized = false,
|
|
.IsRunning = false,
|
|
.TimeReqParam.Value = 0,
|
|
.AppTimeReqPending = false,
|
|
.AdrEnabledPrev = false,
|
|
.NbTransPrev = 0,
|
|
.NbTransmissions = 0,
|
|
};
|
|
|
|
static LmhPackage_t LmhpClockSyncPackage =
|
|
{
|
|
.Port = CLOCK_SYNC_PORT,
|
|
.Init = LmhpClockSyncInit,
|
|
.IsInitialized = LmhpClockSyncIsInitialized,
|
|
.IsRunning = LmhpClockSyncIsRunning,
|
|
.Process = LmhpClockSyncProcess,
|
|
.OnMcpsConfirmProcess = LmhpClockSyncOnMcpsConfirm,
|
|
.OnMcpsIndicationProcess = LmhpClockSyncOnMcpsIndication,
|
|
.OnMlmeConfirmProcess = NULL, // Not used in this package
|
|
.OnJoinRequest = NULL, // To be initialized by LmHandler
|
|
.OnSendRequest = NULL, // To be initialized by LmHandler
|
|
.OnDeviceTimeRequest = NULL, // To be initialized by LmHandler
|
|
.OnSysTimeUpdate = NULL, // To be initialized by LmHandler
|
|
.OnPackageProcessEvent = NULL, // To be initialized by LmHandler
|
|
};
|
|
|
|
/*!
|
|
* Periodic Time start timer
|
|
*/
|
|
static TimerEvent_t PeriodicTimeStartTimer;
|
|
|
|
LmhPackage_t *LmhpClockSyncPackageFactory( void )
|
|
{
|
|
return &LmhpClockSyncPackage;
|
|
}
|
|
|
|
static void LmhpClockSyncInit( void * params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize )
|
|
{
|
|
if( dataBuffer != NULL )
|
|
{
|
|
LmhpClockSyncState.DataBuffer = dataBuffer;
|
|
LmhpClockSyncState.DataBufferMaxSize = dataBufferMaxSize;
|
|
LmhpClockSyncState.Initialized = true;
|
|
LmhpClockSyncState.IsRunning = true;
|
|
TimerInit(&PeriodicTimeStartTimer, OnPeriodicTimeStartTimer);
|
|
}
|
|
else
|
|
{
|
|
LmhpClockSyncState.IsRunning = false;
|
|
LmhpClockSyncState.Initialized = false;
|
|
}
|
|
}
|
|
|
|
static bool LmhpClockSyncIsInitialized( void )
|
|
{
|
|
return LmhpClockSyncState.Initialized;
|
|
}
|
|
|
|
static bool LmhpClockSyncIsRunning( void )
|
|
{
|
|
if( LmhpClockSyncState.Initialized == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return LmhpClockSyncState.IsRunning;
|
|
}
|
|
|
|
static void LmhpClockSyncProcess( void )
|
|
{
|
|
if( LmhpClockSyncState.NbTransmissions > 0 )
|
|
{
|
|
if( LmhpClockSyncAppTimeReq( ) == LORAMAC_HANDLER_SUCCESS )
|
|
{
|
|
LmhpClockSyncState.NbTransmissions--;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void LmhpClockSyncOnMcpsConfirm( McpsConfirm_t *mcpsConfirm )
|
|
{
|
|
MibRequestConfirm_t mibReq;
|
|
|
|
if( LmhpClockSyncState.AppTimeReqPending == true )
|
|
{
|
|
// Revert ADR setting
|
|
mibReq.Type = MIB_ADR;
|
|
mibReq.Param.AdrEnable = LmhpClockSyncState.AdrEnabledPrev;
|
|
LoRaMacMibSetRequestConfirm( &mibReq );
|
|
|
|
// Revert NbTrans setting
|
|
mibReq.Type = MIB_CHANNELS_NB_TRANS;
|
|
mibReq.Param.ChannelsNbTrans = LmhpClockSyncState.NbTransPrev;
|
|
LoRaMacMibSetRequestConfirm( &mibReq );
|
|
|
|
// Revert data rate setting
|
|
mibReq.Type = MIB_CHANNELS_DATARATE;
|
|
mibReq.Param.ChannelsDatarate = LmhpClockSyncState.DataratePrev;
|
|
LoRaMacMibSetRequestConfirm( &mibReq );
|
|
|
|
LmhpClockSyncState.AppTimeReqPending = false;
|
|
}
|
|
}
|
|
|
|
static void LmhpClockSyncOnMcpsIndication( McpsIndication_t *mcpsIndication )
|
|
{
|
|
uint8_t cmdIndex = 0;
|
|
uint8_t dataBufferIndex = 0;
|
|
|
|
if( mcpsIndication->Port != CLOCK_SYNC_PORT )
|
|
{
|
|
return;
|
|
}
|
|
|
|
while( cmdIndex < mcpsIndication->BufferSize )
|
|
{
|
|
switch( mcpsIndication->Buffer[cmdIndex++] )
|
|
{
|
|
case CLOCK_SYNC_PKG_VERSION_REQ:
|
|
{
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_PKG_VERSION_ANS;
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_ID;
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_VERSION;
|
|
break;
|
|
}
|
|
case CLOCK_SYNC_APP_TIME_ANS:
|
|
{
|
|
LmhpClockSyncState.NbTransmissions = 0;
|
|
|
|
// Check if a more precise time correction has been received.
|
|
// If yes then don't process and ignore this answer.
|
|
if( mcpsIndication->DeviceTimeAnsReceived == true )
|
|
{
|
|
cmdIndex += 5;
|
|
break;
|
|
}
|
|
int32_t timeCorrection = 0;
|
|
timeCorrection = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
|
|
timeCorrection += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
|
|
timeCorrection += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
|
|
timeCorrection += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
|
|
if( ( mcpsIndication->Buffer[cmdIndex++] & 0x0F ) == LmhpClockSyncState.TimeReqParam.Fields.TokenReq )
|
|
{
|
|
SysTime_t curTime = { .Seconds = 0, .SubSeconds = 0 };
|
|
curTime = SysTimeGet( );
|
|
curTime.Seconds += timeCorrection;
|
|
SysTimeSet( curTime );
|
|
LmhpClockSyncState.TimeReqParam.Fields.TokenReq = ( LmhpClockSyncState.TimeReqParam.Fields.TokenReq + 1 ) & 0x0F;
|
|
if( LmhpClockSyncPackage.OnSysTimeUpdate != NULL )
|
|
{
|
|
if( ( timeCorrection >= -1 ) && ( timeCorrection <= 1 ) )
|
|
{
|
|
LmhpClockSyncPackage.OnSysTimeUpdate( );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case CLOCK_SYNC_APP_TIME_PERIOD_REQ:
|
|
{
|
|
// Increment index
|
|
cmdIndex++;
|
|
|
|
uint32_t periodTime = mcpsIndication->Buffer[cmdIndex++] & 0x0F;
|
|
periodTime = (128 << periodTime) + randr(0, 30);
|
|
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_APP_TIME_PERIOD_ANS;
|
|
// Answer status supported.
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = 0x00;
|
|
|
|
SysTime_t curTime = SysTimeGet( );
|
|
// Substract Unix to Gps epoch offset. The system time is based on Unix time.
|
|
curTime.Seconds -= UNIX_GPS_EPOCH_OFFSET;
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 0 ) & 0xFF;
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 8 ) & 0xFF;
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 16 ) & 0xFF;
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 24 ) & 0xFF;
|
|
|
|
/* Start Periodic timer */
|
|
TimerSetValue(&PeriodicTimeStartTimer, periodTime * 1000);
|
|
TimerStart(&PeriodicTimeStartTimer);
|
|
|
|
break;
|
|
}
|
|
case CLOCK_SYNC_FORCE_RESYNC_REQ:
|
|
{
|
|
LmhpClockSyncState.NbTransmissions = mcpsIndication->Buffer[cmdIndex++] & 0X07;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( dataBufferIndex != 0 )
|
|
{
|
|
// Answer commands
|
|
LmHandlerAppData_t appData =
|
|
{
|
|
.Buffer = LmhpClockSyncState.DataBuffer,
|
|
.BufferSize = dataBufferIndex,
|
|
.Port = CLOCK_SYNC_PORT
|
|
};
|
|
|
|
bool current_dutycycle;
|
|
LmHandlerGetDutyCycleEnable(¤t_dutycycle);
|
|
|
|
/* force Duty Cycle OFF to this Send */
|
|
LmHandlerSetDutyCycleEnable(false);
|
|
LmhpClockSyncPackage.OnSendRequest(&appData, LORAMAC_HANDLER_UNCONFIRMED_MSG, NULL, true);
|
|
|
|
/* restore initial Duty Cycle */
|
|
LmHandlerSetDutyCycleEnable(current_dutycycle);
|
|
}
|
|
}
|
|
|
|
LmHandlerErrorStatus_t LmhpClockSyncAppTimeReq( void )
|
|
{
|
|
if( LmHandlerIsBusy( ) == true )
|
|
{
|
|
return LORAMAC_HANDLER_ERROR;
|
|
}
|
|
|
|
if( LmhpClockSyncState.AppTimeReqPending == false )
|
|
{
|
|
MibRequestConfirm_t mibReq;
|
|
|
|
// Disable ADR
|
|
mibReq.Type = MIB_ADR;
|
|
LoRaMacMibGetRequestConfirm( &mibReq );
|
|
LmhpClockSyncState.AdrEnabledPrev = mibReq.Param.AdrEnable;
|
|
mibReq.Param.AdrEnable = false;
|
|
LoRaMacMibSetRequestConfirm( &mibReq );
|
|
|
|
// Set NbTrans = 1
|
|
mibReq.Type = MIB_CHANNELS_NB_TRANS;
|
|
LoRaMacMibGetRequestConfirm( &mibReq );
|
|
LmhpClockSyncState.NbTransPrev = mibReq.Param.ChannelsNbTrans;
|
|
mibReq.Param.ChannelsNbTrans = 1;
|
|
LoRaMacMibSetRequestConfirm( &mibReq );
|
|
|
|
// Store data rate
|
|
mibReq.Type = MIB_CHANNELS_DATARATE;
|
|
LoRaMacMibGetRequestConfirm( &mibReq );
|
|
LmhpClockSyncState.DataratePrev = mibReq.Param.ChannelsDatarate;
|
|
|
|
// Add DeviceTimeReq MAC command.
|
|
// In case the network server supports this more precise command
|
|
// this package will use DeviceTimeAns answer as clock synchronization
|
|
// mechanism.
|
|
LmhpClockSyncPackage.OnDeviceTimeRequest( );
|
|
}
|
|
|
|
SysTime_t curTime = SysTimeGet( );
|
|
uint8_t dataBufferIndex = 0;
|
|
|
|
// Substract Unix to Gps epoch offset. The system time is based on Unix time.
|
|
curTime.Seconds -= UNIX_GPS_EPOCH_OFFSET;
|
|
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_APP_TIME_REQ;
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 0 ) & 0xFF;
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 8 ) & 0xFF;
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 16 ) & 0xFF;
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 24 ) & 0xFF;
|
|
LmhpClockSyncState.TimeReqParam.Fields.AnsRequired = 0;
|
|
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = LmhpClockSyncState.TimeReqParam.Value;
|
|
|
|
LmHandlerAppData_t appData =
|
|
{
|
|
.Buffer = LmhpClockSyncState.DataBuffer,
|
|
.BufferSize = dataBufferIndex,
|
|
.Port = CLOCK_SYNC_PORT
|
|
};
|
|
LmhpClockSyncState.AppTimeReqPending = true;
|
|
|
|
bool current_dutycycle;
|
|
LmHandlerGetDutyCycleEnable(¤t_dutycycle);
|
|
|
|
/* force Duty Cycle OFF to this Send */
|
|
LmHandlerSetDutyCycleEnable(false);
|
|
LmHandlerErrorStatus_t status = LmhpClockSyncPackage.OnSendRequest(&appData, LORAMAC_HANDLER_UNCONFIRMED_MSG, NULL, true);
|
|
|
|
/* restore initial Duty Cycle */
|
|
LmHandlerSetDutyCycleEnable(current_dutycycle);
|
|
|
|
return status;
|
|
}
|
|
|
|
static void OnPeriodicTimeStartTimer(void *context)
|
|
{
|
|
LmhpClockSyncState.NbTransmissions = 1;
|
|
TimerStart(&PeriodicTimeStartTimer);
|
|
LmhpClockSyncPackage.OnPackageProcessEvent();
|
|
}
|