[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:
Abtin Keshavarzian
2022-08-25 11:08:44 -07:00
committed by GitHub
parent cba1bebf1f
commit 989a56e96d
5 changed files with 159 additions and 23 deletions
+16
View File
@@ -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_
+66 -23
View File
@@ -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
+12
View File
@@ -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;
+52
View File
@@ -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;
}
+13
View File
@@ -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");
}