[radio] add radio statistics of tx/rx/sleep cycle (#9071)

This commit implements time counting in radio to collect the time when
radio is in sleep/tx/rx time. 2 new APIs are added to get and reset
the statistics. This feature is only available on FTD and MTD. This PR
also adds cli commands to call the APIs on example cli firmware.  This
feature is by default disabled in OT core config. It's enabled on
simulation platform by default now.

Regarding implementation, this feature is totally implemented by
software.  It uses a simplified model to calculate the time. It may
not be very accurate, but it should be enough to give us an overview
of the time.
This commit is contained in:
Li Cao
2023-06-15 04:30:02 +08:00
committed by GitHub
parent 0178ac83e5
commit 4b0b93ad00
21 changed files with 593 additions and 14 deletions
@@ -285,4 +285,15 @@
#define OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE 1
#endif
/**
* @def OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
*
* Set to 1 to enable support for Radio Statistics. Note that this option only works for OPENTHREAD_FTD and
* OPENTHREAD_MTD.
*
*/
#ifndef OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
#define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 1
#endif
#endif // OPENTHREAD_CORE_SIMULATION_CONFIG_H_
+1
View File
@@ -78,6 +78,7 @@ openthread_headers = \
openthread/netdiag.h \
openthread/network_time.h \
openthread/ping_sender.h \
openthread/radio_stats.h \
openthread/random_crypto.h \
openthread/random_noncrypto.h \
openthread/server.h \
+1
View File
@@ -105,6 +105,7 @@ source_set("openthread") {
"platform/toolchain.h",
"platform/trel.h",
"platform/udp.h",
"radio_stats.h",
"random_crypto.h",
"random_noncrypto.h",
"server.h",
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (331)
#define OPENTHREAD_API_VERSION (332)
/**
* @addtogroup api-instance
+99
View File
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2023, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* @brief
* This file includes the OpenThread API for radio stats module.
*/
#ifndef OPENTHREAD_RADIO_STATS_H_
#define OPENTHREAD_RADIO_STATS_H_
#include <stdint.h>
#include <openthread/error.h>
#include <openthread/instance.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup api-radio-stats
*
* @{
*
*/
/**
* Contains the statistics of radio.
*
*/
typedef struct otRadioTimeStats
{
uint64_t mDisabledTime; ///< The total time that radio is in disabled state, in unit of microseconds.
uint64_t mSleepTime; ///< The total time that radio is in sleep state, in unit of microseconds.
uint64_t mTxTime; ///> The total time that radio is doing transmission, in unit of microseconds.
uint64_t mRxTime; ///> The total time that radio is in receive state, in unit of microseconds.
} otRadioTimeStats;
/**
* Gets the radio statistics.
*
* The radio statistics include the time when the radio is in TX/RX/Sleep state. These times are in units of
* microseconds. All times are calculated from the last reset of radio statistics.
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
* @returns A const pointer to the otRadioTimeStats struct that contains the data.
*
*/
const otRadioTimeStats *otRadioTimeStatsGet(otInstance *aInstance);
/**
* Resets the radio statistics.
*
* All times are reset to 0.
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
*
*/
void otRadioTimeStatsReset(otInstance *aInstance);
/**
* @}
*
*/
#ifdef __cplusplus
} // extern "C"
#endif
#endif // OPENTHREAD_RADIO_STATS_H_
+1 -1
View File
@@ -446,7 +446,7 @@ do_expect()
fi
fi
else
test_patterns=(-name 'cli-*.exp' -o -name 'simulation-*.exp')
test_patterns=(-name 'cli-*.exp' -o -name 'simulation-*.exp' -o -name 'cli_non_rcp-*.exp')
fi
if [[ $# != 0 ]]; then
+29
View File
@@ -95,6 +95,7 @@ Done
- [promiscuous](#promiscuous)
- [pskc](#pskc)
- [pskcref](#pskcref)
- [radio](#radio-stats)
- [radiofilter](#radiofilter)
- [rcp](#rcp)
- [region](#region)
@@ -2786,6 +2787,34 @@ Disable radio promiscuous operation.
Done
```
### radio stats
`OPENTHREAD_CONFIG_RADIO_STATS_ENABLE` is required. This feature is only available on FTD and MTD.
The radio statistics shows the time when the radio is in sleep/tx/rx state. The command will show the time of these states since last reset in unit of microseconds. It will also show the percentage of the time.
```bash
> radio stats
Radio Statistics:
Total Time: 67.756s
Tx Time: 0.022944s (0.03%)
Rx Time: 1.482353s (2.18%)
Sleep Time: 66.251128s (97.77%)
Disabled Time: 0.000080s (0.00%)
Done
```
### radio stats clear
`OPENTHREAD_CONFIG_RADIO_STATS_ENABLE` is required. This feature is only available on FTD and MTD.
This command resets the radio statistics. It sets all the time to 0.
```bash
> radio stats clear
Done
```
### radiofilter
`OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` is required.
+87 -1
View File
@@ -86,7 +86,9 @@
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
#include <openthread/nat64.h>
#endif
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
#include <openthread/radio_stats.h>
#endif
#include "common/new.hpp"
#include "common/string.hpp"
#include "mac/channel_mask.hpp"
@@ -5842,6 +5844,87 @@ template <> otError Interpreter::Process<Cmd("radiofilter")>(Arg aArgs[])
}
#endif
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
inline unsigned long UsToSInt(uint64_t aUs) { return ToUlong(static_cast<uint32_t>(aUs / 1000000)); }
inline unsigned long UsToSDec(uint64_t aUs) { return ToUlong(static_cast<uint32_t>(aUs % 1000000)); }
void Interpreter::OutputRadioStatsTime(const char *aTimeName, uint64_t aTimeUs, uint64_t aTotalTimeUs)
{
uint32_t timePercentInt = static_cast<uint32_t>(aTimeUs * 100 / aTotalTimeUs);
uint32_t timePercentDec = static_cast<uint32_t>((aTimeUs * 100 % aTotalTimeUs) * 100 / aTotalTimeUs);
OutputLine("%s Time: %lu.%06lus (%lu.%02lu%%)", aTimeName, UsToSInt(aTimeUs), UsToSDec(aTimeUs),
ToUlong(timePercentInt), ToUlong(timePercentDec));
}
template <> otError Interpreter::Process<Cmd("radio")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
/**
* @cli radio stats
* @code
* radio stats
* Radio Statistics:
* Total Time: 67.756s
* Tx Time: 0.022944s (0.03%)
* Rx Time: 1.482353s (2.18%)
* Sleep Time: 66.251128s (97.77%)
* Disabled Time: 0.000080s (0.00%)
* Done
* @endcode
* @par api_copy
* #otRadioTimeStatsGet
*/
if (aArgs[0] == "stats")
{
if (aArgs[1].IsEmpty())
{
const otRadioTimeStats *radioStats = nullptr;
uint64_t totalTimeUs;
radioStats = otRadioTimeStatsGet(GetInstancePtr());
totalTimeUs =
radioStats->mSleepTime + radioStats->mTxTime + radioStats->mRxTime + radioStats->mDisabledTime;
if (totalTimeUs == 0)
{
OutputLine("Total Time is 0!");
}
else
{
OutputLine("Radio Statistics:");
OutputLine("Total Time: %lu.%03lus", ToUlong(static_cast<uint32_t>(totalTimeUs / 1000000)),
ToUlong((totalTimeUs % 1000000) / 1000));
OutputRadioStatsTime("Tx", radioStats->mTxTime, totalTimeUs);
OutputRadioStatsTime("Rx", radioStats->mRxTime, totalTimeUs);
OutputRadioStatsTime("Sleep", radioStats->mSleepTime, totalTimeUs);
OutputRadioStatsTime("Disabled", radioStats->mDisabledTime, totalTimeUs);
}
}
/**
* @cli radio stats clear
* @code
* radio stats clear
* Done
* @endcode
* @par api_copy
* #otRadioTimeStatsReset
*/
else if (aArgs[1] == "clear")
{
otRadioTimeStatsReset(GetInstancePtr());
}
}
else
{
error = OT_ERROR_INVALID_COMMAND;
}
return error;
}
#endif // OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
template <> otError Interpreter::Process<Cmd("rcp")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
@@ -7544,6 +7627,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[])
CmdEntry("pskcref"),
#endif
#endif
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
CmdEntry("radio"),
#endif
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
CmdEntry("radiofilter"),
#endif
+3
View File
@@ -471,6 +471,9 @@ private:
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
void OutputNat64Counters(const otNat64Counters &aCounters);
#endif
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
void OutputRadioStatsTime(const char *aTimeName, uint64_t aTimeUs, uint64_t aTotalTime);
#endif
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
static void HandleSntpResponse(void *aContext, uint64_t aTime, otError aResult);
+1
View File
@@ -338,6 +338,7 @@ openthread_core_files = [
"api/netdiag_api.cpp",
"api/network_time_api.cpp",
"api/ping_sender_api.cpp",
"api/radio_stats_api.cpp",
"api/random_crypto_api.cpp",
"api/random_noncrypto_api.cpp",
"api/server_api.cpp",
+1
View File
@@ -70,6 +70,7 @@ set(COMMON_SOURCES
api/netdiag_api.cpp
api/network_time_api.cpp
api/ping_sender_api.cpp
api/radio_stats_api.cpp
api/random_crypto_api.cpp
api/random_noncrypto_api.cpp
api/server_api.cpp
+1
View File
@@ -160,6 +160,7 @@ SOURCES_COMMON = \
api/netdiag_api.cpp \
api/network_time_api.cpp \
api/ping_sender_api.cpp \
api/radio_stats_api.cpp \
api/random_crypto_api.cpp \
api/random_noncrypto_api.cpp \
api/server_api.cpp \
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2023, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements the OpenThread APIs for radio statistics
*/
#include "openthread-core-config.h"
#include <openthread/radio_stats.h>
#include "common/as_core_type.hpp"
#include "common/locator_getters.hpp"
using namespace ot;
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
const otRadioTimeStats *otRadioTimeStatsGet(otInstance *aInstance)
{
return &AsCoreType(aInstance).Get<RadioStatistics>().GetStats();
}
void otRadioTimeStatsReset(otInstance *aInstance) { AsCoreType(aInstance).Get<RadioStatistics>().ResetTime(); }
#endif // OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
+4
View File
@@ -671,6 +671,10 @@ template <> inline Radio &Instance::Get(void) { return mRadio; }
template <> inline Radio::Callbacks &Instance::Get(void) { return mRadio.mCallbacks; }
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
template <> inline RadioStatistics &Instance::Get(void) { return mRadio.mRadioStatistics; }
#endif
#if OPENTHREAD_CONFIG_UPTIME_ENABLE
template <> inline Uptime &Instance::Get(void) { return mUptime; }
#endif
+11
View File
@@ -55,6 +55,17 @@
#define OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
*
* Set to 1 to enable support for Radio Statistics. Note that this option only works for OPENTHREAD_FTD and
* OPENTHREAD_MTD.
*
*/
#ifndef OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
#define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 0
#endif
//--------------------------------------------------------------
#if !OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE && !OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
+11 -5
View File
@@ -353,6 +353,15 @@ public:
static constexpr uint16_t kInfoStringSize = 128; ///< Max chars for `InfoString` (ToInfoString()).
static constexpr uint8_t kPreambleSize = 4;
static constexpr uint8_t kSfdSize = 1;
static constexpr uint8_t kPhrSize = 1;
static constexpr uint8_t kPhyHeaderSize = kPreambleSize + kSfdSize + kPhrSize;
static constexpr uint8_t kFcfSize = sizeof(uint16_t);
static constexpr uint8_t kDsnSize = sizeof(uint8_t);
static constexpr uint8_t k154FcsSize = sizeof(uint16_t);
static constexpr uint8_t kImmAckLength = kFcfSize + kDsnSize + k154FcsSize;
/**
* Defines the fixed-length `String` object returned from `ToInfoString()` method.
*
@@ -1081,12 +1090,9 @@ public:
uint16_t GetFrameControlField(void) const;
protected:
static constexpr uint8_t kFcfSize = sizeof(uint16_t);
static constexpr uint8_t kDsnSize = sizeof(uint8_t);
static constexpr uint8_t kSecurityControlSize = sizeof(uint8_t);
static constexpr uint8_t kFrameCounterSize = sizeof(uint32_t);
static constexpr uint8_t kCommandIdSize = sizeof(uint8_t);
static constexpr uint8_t k154FcsSize = sizeof(uint16_t);
static constexpr uint8_t kKeyIndexSize = sizeof(uint8_t);
static constexpr uint16_t kFcfFrameTypeMask = 7 << 0;
@@ -1119,8 +1125,6 @@ protected:
static constexpr uint8_t kKeySourceSizeMode2 = 4;
static constexpr uint8_t kKeySourceSizeMode3 = 8;
static constexpr uint8_t kImmAckLength = kFcfSize + kDsnSize + k154FcsSize;
static constexpr uint8_t kInvalidIndex = 0xff;
static constexpr uint8_t kInvalidSize = kInvalidIndex;
static constexpr uint8_t kMaxPsduSize = kInvalidSize - 1;
@@ -1152,6 +1156,8 @@ protected:
static uint8_t CalculateAddrFieldSize(uint16_t aFcf);
static uint8_t CalculateSecurityHeaderSize(uint8_t aSecurityControl);
static uint8_t CalculateMicSize(uint8_t aSecurityControl);
public:
};
/**
+110
View File
@@ -28,7 +28,10 @@
#include "radio.hpp"
#include "common/code_utils.hpp"
#include "common/locator_getters.hpp"
#include "common/timer.hpp"
#include "mac/mac_frame.hpp"
#include "utils/otns.hpp"
namespace ot {
@@ -92,4 +95,111 @@ Error Radio::Transmit(Mac::TxFrame &aFrame)
}
#endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
inline uint64_t UintSafeMinus(uint64_t aLhs, uint64_t aRhs) { return aLhs > aRhs ? (aLhs - aRhs) : 0; }
RadioStatistics::RadioStatistics(void)
: mStatus(kDisabled)
{
ResetTime();
}
void RadioStatistics::RecordStateChange(Status aStatus)
{
UpdateTime();
mStatus = aStatus;
}
void RadioStatistics::HandleReceiveAt(uint32_t aDurationUs)
{
// The actual rx time of ReceiveAt cannot be obtained from software level. This is a workaround.
if (mStatus == kSleep)
{
mTimeStats.mRxTime += aDurationUs;
}
}
void RadioStatistics::RecordTxDone(otError aError, uint16_t aPsduLength)
{
if (aError == kErrorNone || aError == kErrorNoAck)
{
uint32_t txTimeUs = (aPsduLength + Mac::Frame::kPhyHeaderSize) * Radio::kSymbolsPerOctet * Radio::kSymbolTime;
uint32_t rxAckTimeUs = (Mac::Frame::kImmAckLength + Mac::Frame::kPhyHeaderSize) * Radio::kPhyUsPerByte;
UpdateTime();
mTimeStats.mTxTime += txTimeUs;
if (mStatus == kReceive)
{
mTimeStats.mRxTime = UintSafeMinus(mTimeStats.mRxTime, txTimeUs);
}
else if (mStatus == kSleep)
{
mTimeStats.mSleepTime = UintSafeMinus(mTimeStats.mSleepTime, txTimeUs);
if (aError == kErrorNone)
{
mTimeStats.mRxTime += rxAckTimeUs;
mTimeStats.mSleepTime = UintSafeMinus(mTimeStats.mSleepTime, rxAckTimeUs);
}
}
}
}
void RadioStatistics::RecordRxDone(otError aError)
{
uint32_t ackTimeUs;
VerifyOrExit(aError == kErrorNone);
UpdateTime();
// Currently we cannot know the actual length of ACK. So assume the ACK is an immediate ACK.
ackTimeUs = (Mac::Frame::kImmAckLength + Mac::Frame::kPhyHeaderSize) * Radio::kPhyUsPerByte;
mTimeStats.mTxTime += ackTimeUs;
if (mStatus == kReceive)
{
mTimeStats.mRxTime = UintSafeMinus(mTimeStats.mRxTime, ackTimeUs);
}
exit:
return;
}
const otRadioTimeStats &RadioStatistics::GetStats(void)
{
UpdateTime();
return mTimeStats;
}
void RadioStatistics::ResetTime(void)
{
mTimeStats.mDisabledTime = 0;
mTimeStats.mSleepTime = 0;
mTimeStats.mRxTime = 0;
mTimeStats.mTxTime = 0;
mLastUpdateTime = TimerMicro::GetNow();
}
void RadioStatistics::UpdateTime(void)
{
TimeMicro nowTime = TimerMicro::GetNow();
uint32_t timeElapsed = nowTime - mLastUpdateTime;
switch (mStatus)
{
case kSleep:
mTimeStats.mSleepTime += timeElapsed;
break;
case kReceive:
mTimeStats.mRxTime += timeElapsed;
break;
case kDisabled:
mTimeStats.mDisabledTime += timeElapsed;
break;
}
mLastUpdateTime = nowTime;
}
#endif // OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
} // namespace ot
+98 -6
View File
@@ -36,11 +36,13 @@
#include "openthread-core-config.h"
#include <openthread/radio_stats.h>
#include <openthread/platform/radio.h>
#include <openthread/platform/crypto.h>
#include "common/locator.hpp"
#include "common/non_copyable.hpp"
#include "common/time.hpp"
#include "mac/mac_frame.hpp"
namespace ot {
@@ -68,6 +70,60 @@ static constexpr uint64_t kMaxCslTimeout = OPENTHREAD_CONFIG_MAC_CSL_MAX_TIMEOUT
*
*/
/**
* Implements the radio statistics logic.
*
* The radio statistics are the time when the radio in TX/RX/radio state.
* Since this class collects these statistics from pure software level and no platform API is involved, a simplied
* model is used to calculate the time of different radio states. The data may not be very accurate, but it's
* sufficient to provide a general understanding of the proportion of time a device is in different radio states.
*
* The simplified model is:
* - The RadioStats is only aware of 2 states: RX and sleep.
* - Each time `Radio::Receive` or `Radio::Sleep` is called, it will check the current state and add the time since
* last time the methods were called. For example, `Sleep` is first called and `Receive` is called after 1 second,
* then 1 second will be added to SleepTime and the current state switches to `Receive`.
* - The time of TX will be calculated from the callback of TransmitDone. If TX returns OT_ERROR_NONE or
* OT_ERROR_NO_ACK, the tx time will be added according to the number of bytes sent. And the SleepTime or RxTime
* will be reduced accordingly.
* - When `GetStats` is called, an operation will be executed to calcute the time for the last state. And the result
* will be returned.
*
*/
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
#if !OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
#error "OPENTHREAD_CONFIG_RADIO_STATS_ENABLE requires OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE".
#endif
class RadioStatistics
{
public:
enum Status : uint8_t
{
kDisabled,
kSleep,
kReceive,
};
explicit RadioStatistics(void);
void RecordStateChange(Status aStatus);
void HandleReceiveAt(uint32_t aDurationUs);
void RecordTxDone(otError aError, uint16_t aPsduLength);
void RecordRxDone(otError aError);
const otRadioTimeStats &GetStats(void);
void ResetTime(void);
private:
void UpdateTime(void);
Status mStatus;
otRadioTimeStats mTimeStats;
TimeMicro mLastUpdateTime;
};
#endif // OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
/**
* Represents an OpenThread radio abstraction.
*
@@ -77,7 +133,9 @@ class Radio : public InstanceLocator, private NonCopyable
friend class Instance;
public:
static constexpr uint32_t kSymbolTime = OT_RADIO_SYMBOL_TIME;
static constexpr uint32_t kSymbolTime = OT_RADIO_SYMBOL_TIME;
static constexpr uint8_t kSymbolsPerOctet = OT_RADIO_SYMBOLS_PER_OCTET;
static constexpr uint32_t kPhyUsPerByte = kSymbolsPerOctet * kSymbolTime;
#if (OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT && OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT)
static constexpr uint16_t kNumChannelPages = 2;
static constexpr uint32_t kSupportedChannels =
@@ -679,6 +737,9 @@ private:
otInstance *GetInstancePtr(void) const { return reinterpret_cast<otInstance *>(&InstanceLocator::GetInstance()); }
Callbacks mCallbacks;
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
RadioStatistics mRadioStatistics;
#endif
};
//---------------------------------------------------------------------------------------------------------------------
@@ -744,15 +805,39 @@ inline void Radio::SetPromiscuous(bool aEnable) { otPlatRadioSetPromiscuous(GetI
inline otRadioState Radio::GetState(void) { return otPlatRadioGetState(GetInstancePtr()); }
inline Error Radio::Enable(void) { return otPlatRadioEnable(GetInstancePtr()); }
inline Error Radio::Enable(void)
{
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
mRadioStatistics.RecordStateChange(RadioStatistics::kSleep);
#endif
return otPlatRadioEnable(GetInstancePtr());
}
inline Error Radio::Disable(void) { return otPlatRadioDisable(GetInstancePtr()); }
inline Error Radio::Disable(void)
{
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
mRadioStatistics.RecordStateChange(RadioStatistics::kDisabled);
#endif
return otPlatRadioDisable(GetInstancePtr());
}
inline bool Radio::IsEnabled(void) { return otPlatRadioIsEnabled(GetInstancePtr()); }
inline Error Radio::Sleep(void) { return otPlatRadioSleep(GetInstancePtr()); }
inline Error Radio::Sleep(void)
{
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
mRadioStatistics.RecordStateChange(RadioStatistics::kSleep);
#endif
return otPlatRadioSleep(GetInstancePtr());
}
inline Error Radio::Receive(uint8_t aChannel) { return otPlatRadioReceive(GetInstancePtr(), aChannel); }
inline Error Radio::Receive(uint8_t aChannel)
{
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
mRadioStatistics.RecordStateChange(RadioStatistics::kReceive);
#endif
return otPlatRadioReceive(GetInstancePtr(), aChannel);
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
inline void Radio::UpdateCslSampleTime(uint32_t aCslSampleTime)
@@ -762,7 +847,14 @@ inline void Radio::UpdateCslSampleTime(uint32_t aCslSampleTime)
inline Error Radio::ReceiveAt(uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
{
return otPlatRadioReceiveAt(GetInstancePtr(), aChannel, aStart, aDuration);
Error error = otPlatRadioReceiveAt(GetInstancePtr(), aChannel, aStart, aDuration);
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
if (error == kErrorNone)
{
mRadioStatistics.HandleReceiveAt(aDuration);
}
#endif
return error;
}
inline Error Radio::EnableCsl(uint32_t aCslPeriod, otShortAddress aShortAddr, const otExtAddress *aExtAddr)
+6
View File
@@ -40,6 +40,9 @@ namespace ot {
void Radio::Callbacks::HandleReceiveDone(Mac::RxFrame *aFrame, Error aError)
{
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
Get<RadioStatistics>().RecordRxDone(aError);
#endif
Get<Mac::SubMac>().HandleReceiveDone(aFrame, aError);
}
@@ -47,6 +50,9 @@ void Radio::Callbacks::HandleTransmitStarted(Mac::TxFrame &aFrame) { Get<Mac::Su
void Radio::Callbacks::HandleTransmitDone(Mac::TxFrame &aFrame, Mac::RxFrame *aAckFrame, Error aError)
{
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
Get<RadioStatistics>().RecordTxDone(aError, aFrame.GetPsduLength());
#endif
Get<Mac::SubMac>().HandleTransmitDone(aFrame, aAckFrame, aError);
}
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/expect -f
#
# Copyright (c) 2023, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
source "tests/scripts/expect/_common.exp"
source "tests/scripts/expect/_multinode.exp"
setup_two_nodes "-"
switch_node 2
set addr [get_ipaddr mleid]
send "pollperiod 3000\n"
expect_line "Done"
send "radio stats clear\n"
expect_line "Done"
switch_node 1
for {set i 1} {$i <= 10} {incr i} {
send "ping $addr\n"
sleep 3
expect "16 bytes from $addr: icmp_seq="
}
switch_node 2
send "radio stats\n"
expect_line "Radio Statistics:"
expect -re {Total Time: \d+\.\d+s}
expect -re {Tx Time: \d+\.\d+s \(\d+\.\d+%\)}
expect -re {Rx Time: \d+\.\d+s \(\d+\.\d+%\)}
expect -re {Sleep Time: \d+\.\d+s \(\d+\.\d+%\)}
expect -re {Disabled Time: \d+\.\d+s \(\d+\.\d+%\)}
dispose_all
@@ -99,4 +99,12 @@
*/
#define OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE 1
/**
* @def OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
*
* Disable the radio statistics.
*
*/
#define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 0
#endif /* OPENTHREAD_CORE_TORANJ_CONFIG_SIMULATION_H_ */