mirror of
https://github.com/espressif/openthread.git
synced 2026-06-06 05:24:51 +00:00
[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:
@@ -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_
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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_ */
|
||||
|
||||
Reference in New Issue
Block a user