[csl] account for accuracy drift for next CSL window (#11602)

This commit improves the accuracy of CSL timer fire time by taking
into account the accuracy drift introduced between the current CSL
window and next CSL window.

Original implementation:
- calculate CSL window edges based on current elapsed time
- CSL window edges used to obtain next CSL timer fireAt time
- does not account for additional drift due to accuracy from now until
  next CSL window
- undesired outcome: if CSL period is large (e.g. 10s) and accuracy is
  poor (e.g. 200ppm), then the additional accuracy drift could be
  significant (e.g. 2ms) and the SSED may wake up too late

Suggested implementation:
- calculate the additional expected drift between now and next CSL
  window (i.e. periodUs) and account for this difference by setting
  CSL timer to fireAt earlier
This commit is contained in:
tanyanquan
2025-12-23 05:55:41 +08:00
committed by GitHub
parent 97c7cb66b3
commit a12ff0d0f5
2 changed files with 14 additions and 5 deletions
+1
View File
@@ -507,6 +507,7 @@ private:
static void HandleCslTimer(Timer &aTimer); static void HandleCslTimer(Timer &aTimer);
void HandleCslTimer(void); void HandleCslTimer(void);
void GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter); void GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter);
uint32_t GetNextCycleDrift(void);
uint32_t GetLocalTime(void); uint32_t GetLocalTime(void);
bool IsCslEnabled(void) const { return mCslPeriod > 0; } bool IsCslEnabled(void) const { return mCslPeriod > 0; }
#if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE #if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
+13 -5
View File
@@ -184,7 +184,7 @@ void SubMac::HandleCslReceiveAt(uint32_t aTimeAhead, uint32_t aTimeAfter)
uint32_t winStart; uint32_t winStart;
uint32_t winDuration; uint32_t winDuration;
mCslTimer.FireAt(mCslSampleTimeLocal - aTimeAhead + periodUs); mCslTimer.FireAt(mCslSampleTimeLocal + periodUs - aTimeAhead - GetNextCycleDrift());
aTimeAhead -= kCslReceiveTimeAhead; aTimeAhead -= kCslReceiveTimeAhead;
winStart = mCslSampleTimeRadio - aTimeAhead; winStart = mCslSampleTimeRadio - aTimeAhead;
winDuration = aTimeAhead + aTimeAfter; winDuration = aTimeAhead + aTimeAfter;
@@ -221,7 +221,7 @@ void SubMac::HandleCslReceiveOrSleep(uint32_t aTimeAhead, uint32_t aTimeAfter)
if (mIsCslSampling) if (mIsCslSampling)
{ {
mIsCslSampling = false; mIsCslSampling = false;
mCslTimer.FireAt(mCslSampleTimeLocal - aTimeAhead); mCslTimer.FireAt(mCslSampleTimeLocal - aTimeAhead - GetNextCycleDrift());
if (mState == kStateRadioSample) if (mState == kStateRadioSample)
{ {
LogDebg("CSL sleep %lu", ToUlong(mCslTimer.GetNow().GetValue())); LogDebg("CSL sleep %lu", ToUlong(mCslTimer.GetNow().GetValue()));
@@ -266,15 +266,23 @@ void SubMac::GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter)
curTime = GetLocalTime(); curTime = GetLocalTime();
elapsed = curTime - mCslLastSync.GetValue(); elapsed = curTime - mCslLastSync.GetValue();
semiWindow = semiWindow = static_cast<uint32_t>(static_cast<uint64_t>(elapsed) *
static_cast<uint32_t>(static_cast<uint64_t>(elapsed) * (Get<Radio>().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) /
(Get<Radio>().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) / 1000000); Time::kOneSecondInUsec);
semiWindow += mCslParentAccuracy.GetUncertaintyInMicrosec() + Get<Radio>().GetCslUncertainty() * 10; semiWindow += mCslParentAccuracy.GetUncertaintyInMicrosec() + Get<Radio>().GetCslUncertainty() * 10;
aAhead = Min(semiPeriod, semiWindow + kMinReceiveOnAhead + kCslReceiveTimeAhead); aAhead = Min(semiPeriod, semiWindow + kMinReceiveOnAhead + kCslReceiveTimeAhead);
aAfter = Min(semiPeriod, semiWindow + kMinReceiveOnAfter); aAfter = Min(semiPeriod, semiWindow + kMinReceiveOnAfter);
} }
uint32_t SubMac::GetNextCycleDrift(void)
{
uint64_t periodUs = mCslPeriod * kUsPerTenSymbols;
return static_cast<uint32_t>(periodUs * (Get<Radio>().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) /
Time::kOneSecondInUsec);
}
uint32_t SubMac::GetLocalTime(void) uint32_t SubMac::GetLocalTime(void)
{ {
uint32_t now; uint32_t now;