415 lines
13 KiB
C
415 lines
13 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
|
|
******************************************************************************
|
|
*/
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include "LmHandler.h"
|
|
#include "LmhpClockSync.h"
|
|
#include "utilities.h"
|
|
|
|
/* Private typedef -----------------------------------------------------------*/
|
|
/*!
|
|
* 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;
|
|
|
|
/* Private define ------------------------------------------------------------*/
|
|
/*!
|
|
* LoRaWAN Application Layer Clock Synchronization Specification
|
|
*/
|
|
#define CLOCK_SYNC_PORT 202
|
|
#define CLOCK_SYNC_ID 1
|
|
#define CLOCK_SYNC_VERSION 1
|
|
|
|
/* Private macro -------------------------------------------------------------*/
|
|
/* Private function prototypes -----------------------------------------------*/
|
|
/*!
|
|
* 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);
|
|
|
|
/* Private variables ---------------------------------------------------------*/
|
|
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 */
|
|
};
|
|
|
|
/*!
|
|
* Periodic Time start timer
|
|
*/
|
|
static TimerEvent_t PeriodicTimeStartTimer;
|
|
|
|
/* Exported functions ---------------------------------------------------------*/
|
|
LmhPackage_t *LmphClockSyncPackageFactory(void)
|
|
{
|
|
return &LmhpClockSyncPackage;
|
|
}
|
|
|
|
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;
|
|
|
|
/* Subtract Unix to Gps epcoh 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;
|
|
}
|
|
|
|
/* Private functions ---------------------------------------------------------*/
|
|
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;
|
|
|
|
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;
|
|
}
|
|
break;
|
|
}
|
|
case CLOCK_SYNC_APP_TIME_PERIOD_REQ:
|
|
{
|
|
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();
|
|
/* Subtract Unix to Gps epcoh 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);
|
|
}
|
|
}
|
|
|
|
static void OnPeriodicTimeStartTimer(void *context)
|
|
{
|
|
LmhpClockSyncState.NbTransmissions = 1;
|
|
TimerStart(&PeriodicTimeStartTimer);
|
|
}
|