[radio] add Radio::DetermineClockDrift() for clock drift calculation (#13453)

This commit introduces `Radio::DetermineClockDrift()` to calculate
clock drift in microseconds for a given interval and clock accuracy (in
ppm), using `DivideAndRoundUp` to ensure a conservative (rounded-up)
drift value.

It updates `SubMac` to use the new helper for CSL window and cycle
drift calculations.
This commit is contained in:
Abtin Keshavarzian
2026-07-30 20:01:46 -07:00
committed by GitHub
parent 8fb0bdd858
commit f232204651
5 changed files with 45 additions and 9 deletions
+14
View File
@@ -126,6 +126,20 @@ template <typename UintType> uint16_t ClampToUint16(UintType aValue)
return static_cast<uint16_t>(Min(aValue, static_cast<UintType>(NumericLimits<uint16_t>::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<uint32_t>(Min(aValue, static_cast<uint64_t>(NumericLimits<uint32_t>::kMax)));
}
/**
* Returns a clamped version of given integer to a `int8_t`.
*
+2 -1
View File
@@ -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
+9 -8
View File
@@ -260,22 +260,23 @@ void SubMac::GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter)
curTime = GetLocalTime();
elapsed = curTime - mCslLastSync.GetValue();
semiWindow = static_cast<uint32_t>(static_cast<uint64_t>(elapsed) *
(Get<Radio::Radio>().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) /
Time::kOneSecondInUsec);
semiWindow = DetermineClockDrift(elapsed);
semiWindow += mCslParentAccuracy.GetUncertaintyInMicrosec() + Get<Radio::Radio>().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<Radio::Radio>().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy();
return static_cast<uint32_t>(periodUs *
(Get<Radio::Radio>().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) /
Time::kOneSecondInUsec);
return Radio::DetermineClockDrift(clockAccuracy, aIntervalUs);
}
uint32_t SubMac::GetNextCycleDrift(void) const
{
return DetermineClockDrift(static_cast<uint32_t>(mCslPeriod) * Radio::kUsPerTenSymbols);
}
uint32_t SubMac::GetLocalTime(void)
+9
View File
@@ -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<uint64_t>(aIntervalUs) * static_cast<uint64_t>(aClockAccuracy);
return ClampToUint32(DivideAndRoundUp<uint64_t>(drift, kPpmDivisor));
}
//---------------------------------------------------------------------------------------------------------------------
// SyncedTime
+11
View File
@@ -88,6 +88,17 @@ inline Time32 ConvertTime64To32(Time64 aTime64) { return static_cast<Time32>(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
/**