From fa171812d32766676c7f48f4e7361f77abcb0fe8 Mon Sep 17 00:00:00 2001 From: Eduardo Montoya Date: Wed, 31 Mar 2021 03:39:09 +0200 Subject: [PATCH] [low-power] adaptative CSL window duration (#6311) Make CSL receive window dependent on the accuracy of the crystals used for scheduling and on the elapsed time since last synchronization. CSL Receive window scheduling ============================= a b c d e f g | | | | | | | --------------------------------------------------------------------> a-b: kCslReceiveTimeAhead b-c: -mCslParentUncert c-d: -(mCslParentDrift + otPlatRadioGetCslAccuracy) d: mCslSampleTime d-e: kMinCslWindow e-f: +(mCslParentDrift + otPlatRadioGetCslAccuracy) f-g: +mCslParentUncert --- .../openthread-core-nrf52840-config.h | 27 +++---- examples/platforms/nrf528xx/src/radio.c | 18 +++-- .../openthread-core-simulation-config.h | 10 --- examples/platforms/simulation/radio.c | 7 ++ include/openthread/instance.h | 2 +- include/openthread/platform/radio.h | 12 ++++ src/core/config/mac.h | 29 ++++---- src/core/mac/sub_mac.cpp | 72 ++++++++++++++++--- src/core/mac/sub_mac.hpp | 29 +++++--- src/core/radio/radio.hpp | 20 ++++++ src/core/radio/radio_platform.cpp | 7 ++ src/posix/platform/radio.cpp | 7 ++ tests/unit/test_platform.cpp | 9 ++- 13 files changed, 185 insertions(+), 64 deletions(-) diff --git a/examples/platforms/nrf528xx/nrf52840/openthread-core-nrf52840-config.h b/examples/platforms/nrf528xx/nrf52840/openthread-core-nrf52840-config.h index 2ca2f2089..a89af6e20 100644 --- a/examples/platforms/nrf528xx/nrf52840/openthread-core-nrf52840-config.h +++ b/examples/platforms/nrf528xx/nrf52840/openthread-core-nrf52840-config.h @@ -278,25 +278,26 @@ #define RADIO_CONFIG_SRC_MATCH_EXT_ENTRY_NUM 0 #endif -/** - * @def OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW - * - * CSL sample window in units of 10 symbols. - * - */ -#ifndef OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW -#define OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW 5 -#endif - /** * @def OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD * - * For some reasons, CSL receivers wake up a little later than expected. This variable specifies how much time that - * CSL receiver would wake up earlier than the expected sample window. The time is in unit of 10 symbols. + * Reception scheduling and ramp up time needed for the CSL receiver to be ready, in units of microseconds. * */ #ifndef OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD -#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD 3 +#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD 800 +#endif + +/** + * @def OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON + * + * The total duration, in units of microseconds, required for the CSL receiver to fully receive and acknowledge a frame + * of maximum possible length. + * - Frame preamble: 6*2 symbols + margin + * + */ +#ifndef OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON +#define OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON 12 * 16 #endif /* diff --git a/examples/platforms/nrf528xx/src/radio.c b/examples/platforms/nrf528xx/src/radio.c index 9368f5685..ce9b60bbb 100644 --- a/examples/platforms/nrf528xx/src/radio.c +++ b/examples/platforms/nrf528xx/src/radio.c @@ -118,10 +118,9 @@ static uint8_t sEnergyDetectionChannel; static int8_t sEnergyDetected; #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -static uint32_t sCslPeriod; -static uint32_t sCslSampleTime; -static const uint32_t sCslSampleDur = OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW * OT_US_PER_TEN_SYMBOLS; -static const uint8_t sCslIeHeader[OT_IE_HEADER_SIZE] = {CSL_IE_HEADER_BYTES_LO, CSL_IE_HEADER_BYTES_HI}; +static uint32_t sCslPeriod; +static uint32_t sCslSampleTime; +static const uint8_t sCslIeHeader[OT_IE_HEADER_SIZE] = {CSL_IE_HEADER_BYTES_LO, CSL_IE_HEADER_BYTES_HI}; #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE typedef enum @@ -1068,9 +1067,7 @@ static uint16_t getCslPhase() { uint32_t curTime = otPlatAlarmMicroGetNow(); uint32_t cslPeriodInUs = sCslPeriod * OT_US_PER_TEN_SYMBOLS; - uint32_t diff = - (cslPeriodInUs - (curTime % cslPeriodInUs) + (sCslSampleTime % cslPeriodInUs) + (sCslSampleDur / 2)) % - cslPeriodInUs; + uint32_t diff = (cslPeriodInUs - (curTime % cslPeriodInUs) + (sCslSampleTime % cslPeriodInUs)) % cslPeriodInUs; return (uint16_t)(diff / OT_US_PER_TEN_SYMBOLS + 1); } #endif @@ -1369,6 +1366,13 @@ void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTi sCslSampleTime = aCslSampleTime; } + +uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + return otPlatTimeGetXtalAccuracy() / 2; +} #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE diff --git a/examples/platforms/simulation/openthread-core-simulation-config.h b/examples/platforms/simulation/openthread-core-simulation-config.h index 89def7712..133823907 100644 --- a/examples/platforms/simulation/openthread-core-simulation-config.h +++ b/examples/platforms/simulation/openthread-core-simulation-config.h @@ -155,16 +155,6 @@ */ #define CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER 1 -/** - * @def OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW - * - * The CSL sample window in units of 10 symbols. - * - */ -#ifndef OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW -#define OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW 5 -#endif - /** * @def OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE * diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index a92841e43..dde9b3f51 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -1183,6 +1183,13 @@ void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTi sCslSampleTime = aCslSampleTime; } + +uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + return 0; +} #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE void otPlatRadioSetMacKey(otInstance * aInstance, diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 900d018c0..259eb8855 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (89) +#define OPENTHREAD_API_VERSION (90) /** * @addtogroup api-instance diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index 325394ff1..3e5d788ee 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -969,6 +969,18 @@ otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, const o */ void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime); +/** + * Get the current accuracy, in units of ± ppm, of the clock used for scheduling CSL operations. + * + * @note Platforms may optimize this value based on operational conditions (i.e.: temperature). + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The current CSL rx/tx scheduling drift, in units of ± ppm. + * + */ +uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance); + /** * Set the max transmit power for a specific channel. * diff --git a/src/core/config/mac.h b/src/core/config/mac.h index f733f88be..dc93e9402 100644 --- a/src/core/config/mac.h +++ b/src/core/config/mac.h @@ -375,16 +375,6 @@ #define OPENTHREAD_CONFIG_CSL_TIMEOUT 100 #endif -/** - * @def OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW - * - * The CSL sample window in 10 symbols. - * - */ -#ifndef OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW -#define OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW 5 -#endif - /** * @def OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE * @@ -399,12 +389,25 @@ /** * @def OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD * - * For some reasons, CSL receivers wake up a little later than expected. This variable specifies how much time that - * CSL receiver would wake up earlier than the expected sample window. The time is in unit of 10 symbols. + * Reception scheduling and ramp up time needed for the CSL receiver to be ready, in units of microseconds. * */ #ifndef OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD -#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD 2 +#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD 320 +#endif + +/** + * @def OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON + * + * The total duration, in units of microseconds, required for the CSL receiver to fully receive and acknowledge a frame + * of maximum possible length. + * - Maximum frame size with preamble: 6*2+127*2 symbols + * - AIFS: 12 symbols + * - Maximum ACK size with preamble: 6*2+33*2 symbols + * + */ +#ifndef OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON +#define OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON 356 * 16 #endif /** diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index dcbe4ab46..40fd47cc4 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -71,6 +71,9 @@ SubMac::SubMac(Instance &aInstance) , mCslPeriod(0) , mCslChannel(0) , mIsCslChannelSpecified(false) + , mCslLastSync(0) + , mCslParentDrift(kCslWorstCrystalPpm) + , mCslParentUncert(kCslWorstUncertainty) , mCslState(kCslIdle) , mCslTimer(aInstance, SubMac::HandleCslTimer) #endif @@ -261,14 +264,22 @@ void SubMac::HandleReceiveDone(RxFrame *aFrame, Error aError) UpdateFrameCounter(aFrame->mInfo.mRxInfo.mAckFrameCounter); } -#if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE if (aFrame != nullptr && aError == kErrorNone) { + // Assuming the risk of the parent missing the Enh-ACK in favor of smaller CSL receive window + if ((mCslPeriod > 0) && aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck) + { + mCslLastSync = TimeMicro(static_cast(aFrame->mInfo.mRxInfo.mTimestamp)); + } + +#if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE // Split the log into two lines for RTT to output otLogDebgMac("Received frame in state (SubMac %s, CSL %s), timestamp %u", StateToString(mState), CslStateToString(mCslState), static_cast(aFrame->mInfo.mRxInfo.mTimestamp)); otLogDebgMac("Target sample start time %u, time drift %d", mCslSampleTime.GetValue(), static_cast(aFrame->mInfo.mRxInfo.mTimestamp) - mCslSampleTime.GetValue()); +#endif } #endif @@ -487,7 +498,14 @@ void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aErro { mCallbacks.RecordCcaStatus(ccaSuccess, aFrame.GetChannel()); } - +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + // Actual synchronization timestamp should be from the sent frame instead of the current time. + // Assuming the error here since it is bounded and has very small effect on the final window duration. + if (mCslPeriod > 0) + { + mCslLastSync = TimeMicro(static_cast(otPlatRadioGetNow(&GetInstance()))); + } +#endif break; default: @@ -931,10 +949,10 @@ void SubMac::SetCslPeriod(uint16_t aPeriod) if (mCslPeriod > 0) { - mCslSampleTime = TimerMicro::GetNow(); + mCslSampleTime = TimeMicro(static_cast(otPlatRadioGetNow(&GetInstance()))); Get().UpdateCslSampleTime(mCslSampleTime.GetValue()); - mCslState = kCslSample; + mCslState = kCslSleep; HandleCslTimer(); } else @@ -966,12 +984,25 @@ void SubMac::HandleCslTimer(Timer &aTimer) void SubMac::HandleCslTimer(void) { + /* + * CSL sample timing diagram + * |<---------------------------------Sample--------------------------------->|<--------Sleep--------->| + * | | | + * |<--Ahead-->|<--UnCert-->|<--Drift-->|<--Drift-->|<--UnCert-->|<--MinWin-->| | + * | | | | | | | | + * ---|-----------|------------|-----------|-----------|------------|------------|----------//------------|-- + * -Ahead CslPhase +After -Ahead + */ + uint32_t timeAhead; + uint32_t timeAfter; + GetCslWindowEdges(timeAhead, timeAfter); + switch (mCslState) { case kCslSample: mCslState = kCslSleep; - // kUsPerTenSymbols: computing CSL Phase using floor division. - mCslTimer.StartAt(mCslSampleTime, (mCslPeriod - kCslReceiveTimeAhead) * kUsPerTenSymbols); + + mCslTimer.FireAt(mCslSampleTime - timeAhead); if (mState == kStateCslSample) { #if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE @@ -984,15 +1015,14 @@ void SubMac::HandleCslTimer(void) case kCslSleep: mCslState = kCslSample; + mCslTimer.FireAt(mCslSampleTime + timeAfter); mCslSampleTime += mCslPeriod * kUsPerTenSymbols; + Get().UpdateCslSampleTime(mCslSampleTime.GetValue()); - - mCslTimer.StartAt(mCslSampleTime, kCslSampleWindow); - if (mState == kStateCslSample) { IgnoreError(Get().Receive(mCslChannel)); - otLogDebgMac("CSL sample %u", mCslTimer.GetNow().GetValue()); + otLogDebgMac("CSL sample %u, duration %u", mCslTimer.GetNow().GetValue(), timeAhead + timeAfter); } break; @@ -1004,6 +1034,28 @@ void SubMac::HandleCslTimer(void) break; } } + +void SubMac::GetCslWindowEdges(uint32_t &ahead, uint32_t &after) +{ + uint32_t semiPeriod = mCslPeriod * kUsPerTenSymbols / 2; + uint64_t curTime = otPlatRadioGetNow(&GetInstance()); + uint32_t elapsed, semiWindow; + + if (mCslLastSync.GetValue() > curTime) + { + elapsed = static_cast(UINT64_MAX - mCslLastSync.GetValue() + curTime); + } + else + { + elapsed = static_cast(curTime - mCslLastSync.GetValue()); + } + + semiWindow = elapsed * (Get().GetCslAccuracy() + mCslParentDrift) / 1000000; + semiWindow += mCslParentUncert * kUsPerUncertUnit; + + ahead = (semiWindow + kCslReceiveTimeAhead > semiPeriod) ? semiPeriod : semiWindow + kCslReceiveTimeAhead; + after = (semiWindow + kMinCslWindow > semiPeriod) ? semiPeriod : semiWindow + kMinCslWindow; +} #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE } // namespace Mac diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 4b79ca226..48352d1a8 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -517,6 +517,7 @@ private: #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE static void HandleCslTimer(Timer &aTimer); void HandleCslTimer(void); + void GetCslWindowEdges(uint32_t &ahead, uint32_t &after); #endif enum @@ -553,12 +554,18 @@ private: #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE enum : uint32_t{ - kCslSampleWindow = OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW * - kUsPerTenSymbols, ///< The SSED sample window in units of microseconds. + kMinCslWindow = OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON, ///< CSL receive window for the longest possible + ///< frame and ack duration. kCslReceiveTimeAhead = OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD, ///< CSL receivers would wake up `kCslReceiveTimeAhead` earlier - ///< than expected sample window. The time is in unit of 10 - ///< symbols. + ///< than expected sample window. The time is in unit of + ///< microseconds. + }; + + enum : uint8_t{ + kCslWorstCrystalPpm = 255, ///< Worst possible crystal accuracy, in units of ± ppm. + kCslWorstUncertainty = 255, ///< Worst possible scheduling uncertainty, in units of 100 us. + kUsPerUncertUnit = 100, ///< Number of microseconds by uncertainty unit. }; /** @@ -636,14 +643,18 @@ private: #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - uint32_t mCslTimeout; ///< The CSL synchronized timeout in seconds. - TimeMicro mCslSampleTime; ///< The CSL sample time of the current period. - uint16_t mCslPeriod; ///< The CSL sample period, in units of 10 symbols (160 microseconds). - uint8_t mCslChannel : 7; ///< The actually CSL sample channel. If `mIsCslChannelSpecified` is 0, this should be - ///< equal to the Pan channel of `Mac`. + uint32_t mCslTimeout; ///< The CSL synchronized timeout in seconds. + uint16_t mCslPeriod; ///< The CSL sample period, in units of 10 symbols (160 microseconds). + uint8_t mCslChannel : 7; ///< The actually CSL sample channel. If `mIsCslChannelSpecified` is 0, this should be + ///< equal to the Pan channel of `Mac`. uint8_t mIsCslChannelSpecified : 1; ///< Indicates whether or not the CSL channel was explicitly specified by ///< the user. + TimeMicro mCslSampleTime; ///< The CSL sample time of the current period. + TimeMicro mCslLastSync; ///< The timestamp of the last successful CSL syncronization. + uint8_t mCslParentDrift; ///< Drift of timer used for scheduling CSL transmission by the parent, in ± ppm. + uint8_t mCslParentUncert; ///< Uncertainty of the scheduling CSL of transmission by the parent, in ±100 us units. + CslState mCslState; TimerMicro mCslTimer; diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index 3af3fb1e9..0f25d98a7 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -425,6 +425,16 @@ public: * */ Error EnableCsl(uint32_t aCslPeriod, const otExtAddress *aExtAddr); + + /** + * Get the current accuracy, in units of ± ppm, of the clock used for scheduling CSL operations. + * + * @note Platforms may optimize this value based on operational conditions (i.e.: temperature). + * + * @returns The current CSL rx/tx scheduling drift, in units of ± ppm. + * + */ + uint8_t GetCslAccuracy(void); #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE /** @@ -735,6 +745,11 @@ inline Error Radio::EnableCsl(uint32_t aCslPeriod, const otExtAddress *aExtAddr) { return otPlatRadioEnableCsl(GetInstancePtr(), aCslPeriod, aExtAddr); } + +inline uint8_t Radio::GetCslAccuracy() +{ + return otPlatRadioGetCslAccuracy(GetInstancePtr()); +} #endif inline Mac::TxFrame &Radio::GetTransmitBuffer(void) @@ -883,6 +898,11 @@ inline Error Radio::EnableCsl(uint32_t, const otExtAddress *) { return kErrorNotImplemented; } + +inline uint8_t Radio::GetCslAccuracy(void) +{ + return UINT8_MAX; +} #endif inline Mac::TxFrame &Radio::GetTransmitBuffer(void) diff --git a/src/core/radio/radio_platform.cpp b/src/core/radio/radio_platform.cpp index c5c6328e0..526e83a28 100644 --- a/src/core/radio/radio_platform.cpp +++ b/src/core/radio/radio_platform.cpp @@ -242,6 +242,13 @@ OT_TOOL_WEAK uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance) return 0; } +OT_TOOL_WEAK uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + return UINT8_MAX; +} + OT_TOOL_WEAK Error otPlatRadioGetFemLnaGain(otInstance *aInstance, int8_t *aGain) { OT_UNUSED_VARIABLE(aInstance); diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index 0c2ea12b4..32de15063 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -555,6 +555,13 @@ uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance) return sRadioSpinel.GetBusSpeed(); } +uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + return 0; +} + otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aChannel, int8_t aMaxPower) { OT_UNUSED_VARIABLE(aInstance); diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index bb2e7cc65..32ea9e59e 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -599,7 +599,7 @@ void otPlatFlashWrite(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffse } } -#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE +#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE uint16_t otPlatTimeGetXtalAccuracy(void) { return 0; @@ -621,6 +621,13 @@ void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTi OT_UNUSED_VARIABLE(aInstance); OT_UNUSED_VARIABLE(aCslSampleTime); } + +uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + return static_cast(otPlatTimeGetXtalAccuracy() / 2); +} #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE #if OPENTHREAD_CONFIG_OTNS_ENABLE