diff --git a/src/core/common/num_utils.hpp b/src/core/common/num_utils.hpp index b40b0d332..5f6c42223 100644 --- a/src/core/common/num_utils.hpp +++ b/src/core/common/num_utils.hpp @@ -126,6 +126,20 @@ template uint16_t ClampToUint16(UintType aValue) return static_cast(Min(aValue, static_cast(NumericLimits::kMax))); } +/** + * Returns a clamped version of given integer to a `uint32_t`. + * + * If @p aValue is greater than max value of a `uint32_t`, the max value is returned. + * + * @param[in] aValue The value to clamp. + * + * @returns The clamped version of @p aValue to `uint32_t`. + */ +inline uint32_t ClampToUint32(uint64_t aValue) +{ + return static_cast(Min(aValue, static_cast(NumericLimits::kMax))); +} + /** * Returns a clamped version of given integer to a `int8_t`. * diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index b418b4b7a..83699a5cd 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -614,7 +614,8 @@ private: void UpdateCslLastSyncTimestamp(RxFrame *aFrame, Error aError); void HandleCslTimer(void); void GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter); - uint32_t GetNextCycleDrift(void); + uint32_t DetermineClockDrift(uint32_t aIntervalUs) const; + uint32_t GetNextCycleDrift(void) const; uint32_t GetLocalTime(void); bool IsCslEnabled(void) const { return mCslPeriod > 0; } #if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE diff --git a/src/core/mac/sub_mac_csl_receiver.cpp b/src/core/mac/sub_mac_csl_receiver.cpp index 67b3d7830..71dc2382a 100644 --- a/src/core/mac/sub_mac_csl_receiver.cpp +++ b/src/core/mac/sub_mac_csl_receiver.cpp @@ -260,22 +260,23 @@ void SubMac::GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter) curTime = GetLocalTime(); elapsed = curTime - mCslLastSync.GetValue(); - semiWindow = static_cast(static_cast(elapsed) * - (Get().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) / - Time::kOneSecondInUsec); + semiWindow = DetermineClockDrift(elapsed); semiWindow += mCslParentAccuracy.GetUncertaintyInMicrosec() + Get().GetCslUncertainty() * 10; aAhead = Min(semiPeriod, semiWindow + kMinReceiveOnAhead + kCslReceiveTimeAhead); aAfter = Min(semiPeriod, semiWindow + kMinReceiveOnAfter); } -uint32_t SubMac::GetNextCycleDrift(void) +uint32_t SubMac::DetermineClockDrift(uint32_t aIntervalUs) const { - uint64_t periodUs = mCslPeriod * Radio::kUsPerTenSymbols; + uint16_t clockAccuracy = Get().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy(); - return static_cast(periodUs * - (Get().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) / - Time::kOneSecondInUsec); + return Radio::DetermineClockDrift(clockAccuracy, aIntervalUs); +} + +uint32_t SubMac::GetNextCycleDrift(void) const +{ + return DetermineClockDrift(static_cast(mCslPeriod) * Radio::kUsPerTenSymbols); } uint32_t SubMac::GetLocalTime(void) diff --git a/src/core/radio/radio_types.cpp b/src/core/radio/radio_types.cpp index eec5e1b5d..0396e85b7 100644 --- a/src/core/radio/radio_types.cpp +++ b/src/core/radio/radio_types.cpp @@ -46,6 +46,15 @@ bool IsTimeStrictlyBefore(Time32 aFirstTime, Time32 aSecondTime) return (firstTime < secondTime); } +uint32_t DetermineClockDrift(uint16_t aClockAccuracy, uint32_t aIntervalUs) +{ + static constexpr uint64_t kPpmDivisor = 1000000u; + + uint64_t drift = static_cast(aIntervalUs) * static_cast(aClockAccuracy); + + return ClampToUint32(DivideAndRoundUp(drift, kPpmDivisor)); +} + //--------------------------------------------------------------------------------------------------------------------- // SyncedTime diff --git a/src/core/radio/radio_types.hpp b/src/core/radio/radio_types.hpp index 046d760a9..59ea5c1f1 100644 --- a/src/core/radio/radio_types.hpp +++ b/src/core/radio/radio_types.hpp @@ -88,6 +88,17 @@ inline Time32 ConvertTime64To32(Time64 aTime64) { return static_cast(aTi */ bool IsTimeStrictlyBefore(Time32 aFirstTime, Time32 aSecondTime); +/** + * Calculates clock drift in microseconds for a given interval and clock accuracy. + * + * @param[in] aClockAccuracy The clock accuracy in ppm (can be for a single device or combined accuracy of transmitter + * and receiver). + * @param[in] aIntervalUs The interval duration in microseconds. + * + * @returns The calculated clock drift in microseconds (rounded up). + */ +uint32_t DetermineClockDrift(uint16_t aClockAccuracy, uint32_t aIntervalUs); + #if OT_CONFIG_RADIO_TIME_ENABLE && OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE /**