mirror of
https://github.com/espressif/openthread.git
synced 2026-07-28 22:57:47 +00:00
[link-metrics] update the scaling of link margin and RSSI metrics (#8078)
This commit updates the scaling of the link margin and RSSI metrics. The metric values are scaled when appended in the message (in a Report sub-TLV) or when they are read back from received message. The stored value in `MetricsValues` are changed to be always the actual metric value (not the scaled value). This ensures that the value are stored in proper int type (e.g., RSSI is `int8` vs the scaled value which is `[0,255]`). Methods are added to perform the scaling which now rounds to closest integer ensuring the reverse scaling gives back the original value. Unit test is added to validate the scaling methods.
This commit is contained in:
@@ -171,6 +171,22 @@ template <> inline int ThreeWayCompare(bool aFirst, bool aSecond)
|
||||
return (aFirst == aSecond) ? 0 : (aFirst ? 1 : -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* This template function divides two numbers and rounds the result to the closest integer.
|
||||
*
|
||||
* @tparam IntType The integer type.
|
||||
*
|
||||
* @param[in] aDividend The dividend value.
|
||||
* @param[in] aDivisor The divisor value.
|
||||
*
|
||||
* @return The result of division and rounding to the closest integer.
|
||||
*
|
||||
*/
|
||||
template <typename IntType> inline IntType DivideAndRoundToClosest(IntType aDividend, IntType aDivisor)
|
||||
{
|
||||
return (aDividend + (aDivisor / 2)) / aDivisor;
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#endif // NUM_UTILS_HPP_
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include "common/instance.hpp"
|
||||
#include "common/locator_getters.hpp"
|
||||
#include "common/log.hpp"
|
||||
#include "common/num_utils.hpp"
|
||||
#include "common/numeric_limits.hpp"
|
||||
#include "mac/mac.hpp"
|
||||
#include "thread/link_metrics_tlvs.hpp"
|
||||
#include "thread/neighbor_table.hpp"
|
||||
@@ -251,13 +253,10 @@ Error LinkMetrics::AppendReport(Message &aMessage, const Message &aRequestMessag
|
||||
|
||||
if (queryId == kQueryIdSingleProbe)
|
||||
{
|
||||
values.mPduCountValue = aRequestMessage.GetPsduCount();
|
||||
values.mLqiValue = aRequestMessage.GetAverageLqi();
|
||||
// Linearly scale Link Margin from [0, 130] to [0, 255]
|
||||
values.mLinkMarginValue = Get<Mac::Mac>().ComputeLinkMargin(aRequestMessage.GetAverageRss()) * 255 / 130;
|
||||
// Linearly scale rss from [-130, 0] to [0, 255]
|
||||
values.mRssiValue = (aRequestMessage.GetAverageRss() + 130) * 255 / 130;
|
||||
|
||||
values.mPduCountValue = aRequestMessage.GetPsduCount();
|
||||
values.mLqiValue = aRequestMessage.GetAverageLqi();
|
||||
values.mLinkMarginValue = Get<Mac::Mac>().ComputeLinkMargin(aRequestMessage.GetAverageRss());
|
||||
values.mRssiValue = aRequestMessage.GetAverageRss();
|
||||
SuccessOrExit(error = AppendReportSubTlvToMessage(aMessage, values));
|
||||
}
|
||||
else
|
||||
@@ -275,12 +274,10 @@ Error LinkMetrics::AppendReport(Message &aMessage, const Message &aRequestMessag
|
||||
else
|
||||
{
|
||||
values.SetMetrics(seriesInfo->GetLinkMetrics());
|
||||
values.mPduCountValue = seriesInfo->GetPduCount();
|
||||
values.mLqiValue = seriesInfo->GetAverageLqi();
|
||||
// Linearly scale Link Margin from [0, 130] to [0, 255]
|
||||
values.mLinkMarginValue = Get<Mac::Mac>().ComputeLinkMargin(seriesInfo->GetAverageRss()) * 255 / 130;
|
||||
// Linearly scale RSSI from [-130, 0] to [0, 255]
|
||||
values.mRssiValue = (seriesInfo->GetAverageRss() + 130) * 255 / 130;
|
||||
values.mPduCountValue = seriesInfo->GetPduCount();
|
||||
values.mLqiValue = seriesInfo->GetAverageLqi();
|
||||
values.mLinkMarginValue = Get<Mac::Mac>().ComputeLinkMargin(seriesInfo->GetAverageRss());
|
||||
values.mRssiValue = seriesInfo->GetAverageRss();
|
||||
SuccessOrExit(error = AppendReportSubTlvToMessage(aMessage, values));
|
||||
}
|
||||
}
|
||||
@@ -483,15 +480,13 @@ void LinkMetrics::HandleReport(const Message & aMessage,
|
||||
|
||||
case TypeIdFlags::kLinkMargin:
|
||||
values.mMetrics.mLinkMargin = true;
|
||||
// Reverse operation for linear scale, map from [0, 255] to [0, 130]
|
||||
values.mLinkMarginValue = reportTlv.GetMetricsValue8() * 130 / 255;
|
||||
values.mLinkMarginValue = ScaleRawValueToLinkMargin(reportTlv.GetMetricsValue8());
|
||||
LogDebg(" - Margin: %d (dB) (Exponential Moving Average)", values.mLinkMarginValue);
|
||||
break;
|
||||
|
||||
case TypeIdFlags::kRssi:
|
||||
values.mMetrics.mRssi = true;
|
||||
// Reverse operation for linear scale, map from [0, 255] to [-130, 0]
|
||||
values.mRssiValue = reportTlv.GetMetricsValue8() * 130 / 255 - 130;
|
||||
values.mRssiValue = ScaleRawValueToRssi(reportTlv.GetMetricsValue8());
|
||||
LogDebg(" - RSSI: %d (dBm) (Exponential Moving Average)", values.mRssiValue);
|
||||
break;
|
||||
}
|
||||
@@ -559,13 +554,11 @@ void LinkMetrics::ProcessEnhAckIeData(const uint8_t *aData, uint8_t aLength, con
|
||||
}
|
||||
if (values.GetMetrics().mLinkMargin && idx < aLength)
|
||||
{
|
||||
// Reverse operation for linear scale, map from [0, 255] to [0, 130]
|
||||
values.mLinkMarginValue = aData[idx++] * 130 / 255;
|
||||
values.mLinkMarginValue = ScaleRawValueToLinkMargin(aData[idx++]);
|
||||
}
|
||||
if (values.GetMetrics().mRssi && idx < aLength)
|
||||
{
|
||||
// Reverse operation for linear scale, map from [0, 255] to [-130, 0]
|
||||
values.mRssiValue = aData[idx++] * 130 / 255 - 130;
|
||||
values.mRssiValue = ScaleRawValueToRssi(aData[idx++]);
|
||||
}
|
||||
|
||||
mEnhAckProbingIeReportCallback(aNeighbor.GetRloc16(), &aNeighbor.GetExtAddress(), &values,
|
||||
@@ -786,14 +779,14 @@ Error LinkMetrics::AppendReportSubTlvToMessage(Message &aMessage, const MetricsV
|
||||
if (aValues.mMetrics.mLinkMargin)
|
||||
{
|
||||
reportTlv.SetMetricsTypeId(TypeIdFlags(TypeIdFlags::kLinkMargin));
|
||||
reportTlv.SetMetricsValue8(aValues.mLinkMarginValue);
|
||||
reportTlv.SetMetricsValue8(ScaleLinkMarginToRawValue(aValues.mLinkMarginValue));
|
||||
SuccessOrExit(error = reportTlv.AppendTo(aMessage));
|
||||
}
|
||||
|
||||
if (aValues.mMetrics.mRssi)
|
||||
{
|
||||
reportTlv.SetMetricsTypeId(TypeIdFlags(TypeIdFlags::kRssi));
|
||||
reportTlv.SetMetricsValue8(aValues.mRssiValue);
|
||||
reportTlv.SetMetricsValue8(ScaleRssiToRawValue(aValues.mRssiValue));
|
||||
SuccessOrExit(error = reportTlv.AppendTo(aMessage));
|
||||
}
|
||||
|
||||
@@ -801,6 +794,56 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
uint8_t LinkMetrics::ScaleLinkMarginToRawValue(uint8_t aLinkMargin)
|
||||
{
|
||||
// Linearly scale Link Margin from [0, 130] to [0, 255].
|
||||
// `kMaxLinkMargin = 130`.
|
||||
|
||||
uint16_t value;
|
||||
|
||||
value = Min(aLinkMargin, kMaxLinkMargin);
|
||||
value = value * NumericLimits<uint8_t>::kMax;
|
||||
value = DivideAndRoundToClosest<uint16_t>(value, kMaxLinkMargin);
|
||||
|
||||
return static_cast<uint8_t>(value);
|
||||
}
|
||||
|
||||
uint8_t LinkMetrics::ScaleRawValueToLinkMargin(uint8_t aRawValue)
|
||||
{
|
||||
// Scale back raw value of [0, 255] to Link Margin from [0, 130].
|
||||
|
||||
uint16_t value = aRawValue;
|
||||
|
||||
value = value * kMaxLinkMargin;
|
||||
value = DivideAndRoundToClosest<uint16_t>(value, NumericLimits<uint8_t>::kMax);
|
||||
return static_cast<uint8_t>(value);
|
||||
}
|
||||
|
||||
uint8_t LinkMetrics::ScaleRssiToRawValue(int8_t aRssi)
|
||||
{
|
||||
// Linearly scale RSSI from [-130, 0] to [0, 255].
|
||||
// `kMinRssi = -130`, `kMaxRssi = 0`.
|
||||
|
||||
int32_t value = aRssi;
|
||||
|
||||
value = Clamp(value, kMinRssi, kMaxRssi) - kMinRssi;
|
||||
value = value * NumericLimits<uint8_t>::kMax;
|
||||
value = DivideAndRoundToClosest<int32_t>(value, kMaxRssi - kMinRssi);
|
||||
|
||||
return static_cast<uint8_t>(value);
|
||||
}
|
||||
|
||||
int8_t LinkMetrics::ScaleRawValueToRssi(uint8_t aRawValue)
|
||||
{
|
||||
int32_t value = aRawValue;
|
||||
|
||||
value = value * (kMaxRssi - kMinRssi);
|
||||
value = DivideAndRoundToClosest<int32_t>(value, NumericLimits<uint8_t>::kMax);
|
||||
value += kMinRssi;
|
||||
|
||||
return static_cast<int8_t>(value);
|
||||
}
|
||||
|
||||
} // namespace LinkMetrics
|
||||
} // namespace ot
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
|
||||
namespace ot {
|
||||
class Neighbor;
|
||||
class UnitTester;
|
||||
|
||||
namespace LinkMetrics {
|
||||
|
||||
@@ -76,6 +77,7 @@ namespace LinkMetrics {
|
||||
class LinkMetrics : public InstanceLocator, private NonCopyable
|
||||
{
|
||||
friend class ot::Neighbor;
|
||||
friend class ot::UnitTester;
|
||||
|
||||
public:
|
||||
typedef otLinkMetricsReportCallback ReportCallback;
|
||||
@@ -272,6 +274,11 @@ private:
|
||||
static constexpr uint8_t kSeriesIdAllSeries = 255; // This series ID represents all series.
|
||||
static constexpr uint8_t kLinkProbeMaxLen = 64; // Max length of data payload in Link Probe TLV.
|
||||
|
||||
// Constants for scaling Link Margin and RSSI to raw value
|
||||
static constexpr uint8_t kMaxLinkMargin = 130;
|
||||
static constexpr int32_t kMinRssi = -130;
|
||||
static constexpr int32_t kMaxRssi = 0;
|
||||
|
||||
Error SendLinkMetricsQuery(const Ip6::Address &aDestination,
|
||||
uint8_t aSeriesId,
|
||||
const TypeIdFlags * aTypeIdFlags,
|
||||
@@ -292,6 +299,11 @@ private:
|
||||
Metrics & aMetrics);
|
||||
static Error AppendReportSubTlvToMessage(Message &aMessage, const MetricsValues &aValues);
|
||||
|
||||
static uint8_t ScaleLinkMarginToRawValue(uint8_t aLinkMargin);
|
||||
static uint8_t ScaleRawValueToLinkMargin(uint8_t aRawValue);
|
||||
static uint8_t ScaleRssiToRawValue(int8_t aRssi);
|
||||
static int8_t ScaleRawValueToRssi(uint8_t aRawValue);
|
||||
|
||||
ReportCallback mReportCallback;
|
||||
void * mReportCallbackContext;
|
||||
MgmtResponseCallback mMgmtResponseCallback;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include "common/array.hpp"
|
||||
#include "common/code_utils.hpp"
|
||||
#include "thread/link_metrics.hpp"
|
||||
#include "thread/link_quality.hpp"
|
||||
|
||||
namespace ot {
|
||||
@@ -481,6 +482,53 @@ void TestSuccessRateTracker(void)
|
||||
}
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
|
||||
|
||||
class UnitTester
|
||||
{
|
||||
public:
|
||||
static void TestLinkMetricsScaling(void)
|
||||
{
|
||||
printf("\nTestLinkMetricsScaling\n");
|
||||
|
||||
// Test Link Margin scaling from [0,130] -> [0, 255]
|
||||
|
||||
for (uint8_t linkMargin = 0; linkMargin <= 130; linkMargin++)
|
||||
{
|
||||
double scaled = 255.0 / 130.0 * linkMargin;
|
||||
uint8_t scaledAsU8 = static_cast<uint8_t>(scaled + 0.5);
|
||||
|
||||
printf("\nLinkMargin : %-3u -> Scaled : %.1f (rounded:%u)", linkMargin, scaled, scaledAsU8);
|
||||
|
||||
VerifyOrQuit(LinkMetrics::LinkMetrics::ScaleLinkMarginToRawValue(linkMargin) == scaledAsU8);
|
||||
VerifyOrQuit(LinkMetrics::LinkMetrics::ScaleRawValueToLinkMargin(scaledAsU8) == linkMargin);
|
||||
}
|
||||
|
||||
VerifyOrQuit(LinkMetrics::LinkMetrics::ScaleLinkMarginToRawValue(131) == 255);
|
||||
VerifyOrQuit(LinkMetrics::LinkMetrics::ScaleLinkMarginToRawValue(150) == 255);
|
||||
VerifyOrQuit(LinkMetrics::LinkMetrics::ScaleLinkMarginToRawValue(255) == 255);
|
||||
|
||||
// Test RSSI scaling from [-130, 0] -> [0, 255]
|
||||
|
||||
for (int8_t rssi = -128; rssi <= 0; rssi++)
|
||||
{
|
||||
double scaled = 255.0 / 130.0 * (rssi + 130.0);
|
||||
uint8_t scaledAsU8 = static_cast<uint8_t>(scaled + 0.5);
|
||||
|
||||
printf("\nRSSI : %-3d -> Scaled :%.1f (rounded:%u)", rssi, scaled, scaledAsU8);
|
||||
|
||||
VerifyOrQuit(LinkMetrics::LinkMetrics::ScaleRssiToRawValue(rssi) == scaledAsU8);
|
||||
VerifyOrQuit(LinkMetrics::LinkMetrics::ScaleRawValueToRssi(scaledAsU8) == rssi);
|
||||
}
|
||||
|
||||
VerifyOrQuit(LinkMetrics::LinkMetrics::ScaleRssiToRawValue(1) == 255);
|
||||
VerifyOrQuit(LinkMetrics::LinkMetrics::ScaleRssiToRawValue(10) == 255);
|
||||
VerifyOrQuit(LinkMetrics::LinkMetrics::ScaleRssiToRawValue(127) == 255);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
@@ -488,6 +536,10 @@ int main(void)
|
||||
ot::TestRssAveraging();
|
||||
ot::TestLinkQualityCalculations();
|
||||
ot::TestSuccessRateTracker();
|
||||
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
|
||||
ot::UnitTester::TestLinkMetricsScaling();
|
||||
#endif
|
||||
|
||||
printf("\nAll tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -118,6 +118,19 @@ void TestNumUtils(void)
|
||||
VerifyOrQuit(ThreeWayCompare<bool>(true, false) > 0);
|
||||
VerifyOrQuit(ThreeWayCompare<bool>(false, true) < 0);
|
||||
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(2, 1) == 2);
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(1, 3) == 0);
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(1, 2) == 1);
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(2, 3) == 1);
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(3, 2) == 2);
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(4, 2) == 2);
|
||||
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(0, 10) == 0);
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(4, 10) == 0);
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(5, 10) == 1);
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(9, 10) == 1);
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(10, 10) == 1);
|
||||
|
||||
printf("TestNumUtils() passed\n");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user