Enhance the calculation of link quality metrics (#197)

This commit adds a new class `LinkQualityInfo` which stores all
relevant information about quality of a link, including average
received signal strength (RSS), link margin, and link quality value.
The RSS average is obtained using an adaptive exponential moving
average filter. A unit-test is also added for the new class.

The Neighbor class now includes an instance of LinkQualityInfo. The
RSS average is updated from 'mac.cpp' whenever a valid/verified packet
is received from the corresponding neighbor. The mesh_forwarder,
mle_router, and mle modules are modified to use the new methods from
the newly added class.

This change addresses issue #175.
This commit is contained in:
Abtin Keshavarzian
2016-06-27 14:15:11 -07:00
committed by Jonathan Hui
parent 3635bb9691
commit cb33898827
13 changed files with 935 additions and 62 deletions
+1
View File
@@ -95,6 +95,7 @@ extern "C" {
* @defgroup core-tasklet Tasklet
* @defgroup core-timer Timer
* @defgroup core-udp UDP
* @defgroup core-link-quality Link Quality
*
* @}
*
+2
View File
@@ -57,6 +57,7 @@ libopenthread_a_SOURCES = \
net/udp6.cpp \
thread/address_resolver.cpp \
thread/key_manager.cpp \
thread/link_quality.cpp \
thread/lowpan.cpp \
thread/mesh_forwarder.cpp \
thread/mle.cpp \
@@ -96,6 +97,7 @@ noinst_HEADERS = \
net/udp6.hpp \
thread/address_resolver.hpp \
thread/key_manager.hpp \
thread/link_quality.hpp \
thread/lowpan.hpp \
thread/mesh_forwarder.hpp \
thread/mle.hpp \
+5
View File
@@ -867,6 +867,11 @@ void Mac::ReceiveDoneTask(Frame *aFrame, ThreadError aError)
// Security Processing
SuccessOrExit(error = ProcessReceiveSecurity(*aFrame, srcaddr, neighbor));
if (neighbor != NULL)
{
neighbor->mLinkInfo.AddRss(aFrame->mPower);
}
switch (mState)
{
case kStateActiveScan:
+286
View File
@@ -0,0 +1,286 @@
/*
* Copyright (c) 2016, Nest Labs, Inc.
* 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 link quality information processing and storage.
*/
#include <stdio.h>
#include <string.h>
#include <openthread-types.h>
#include <common/code_utils.hpp>
#include <thread/link_quality.hpp>
namespace Thread {
enum
{
kDefaultNoiseFloor = -100, // Default noise floor used if no average value is available.
};
// This array gives the decimal point digits representing 0/8, 1/8, ..., 7/8 (it does not include the '.').
static const char *const kLinkQualityDecimalDigitsString[8] =
{
// 0/8, 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8
"000", "125", "250", "375", "500", "625", "750", "875"
};
const char LinkQualityInfo::kUnknownRssString[] = "Unknown RSS";
static LinkQualityInfo sNoiseFloorAverage; // Store the noise floor average.
//-------------------------------------------------------------------------------
LinkQualityInfo::LinkQualityInfo(void)
{
Clear();
}
void LinkQualityInfo::Clear(void)
{
mRssAverage = 0;
mCount = 0;
mLinkQuality = 0;
}
void LinkQualityInfo::AddRss(int8_t anRss)
{
uint16_t newValue;
uint16_t oldAverage;
// Restrict/Cap the RSS value to the closed range [0, -128] so the value can fit in 8 bits.
if (anRss > 0)
{
anRss = 0;
}
// Multiply the the RSS value by a precision multiple (currently -8).
newValue = -anRss;
newValue <<= kRssAveragePrecisionMultipleBitShift;
oldAverage = mRssAverage;
if (mCount >= kRssCountForWeightCoefficientOneEighth)
{
// New average = old average * 7/8 + new value * 1/8
mRssAverage = ((oldAverage << 3) - oldAverage + newValue) >> 3;
}
else if (mCount >= kRssCountForWeightCoefficientOneFourth)
{
// New average = old average * 3/4 + new value * 1/4
mRssAverage = ((oldAverage << 2) - oldAverage + newValue) >> 2;
}
else if (mCount >= kRssCountForWeightCoefficientOneHalf)
{
// New average = old average * 1/2 + new value * 1/2
mRssAverage = (oldAverage + newValue) >> 1;
}
else
{
mRssAverage = newValue;
}
if (mCount < kRssCountMax)
{
mCount++;
}
UpdateLinkQuality();
}
int8_t LinkQualityInfo::GetAverageRss(void) const
{
int8_t average = kUnknownRss;
if (mCount != 0)
{
average = -(static_cast<int16_t>(mRssAverage >> kRssAveragePrecisionMultipleBitShift));
// Check for round up (e.g. average of -71.5 --> -72)
if ((mRssAverage & kRssAveragePrecisionMultipleBitMask) >= (kRssAveragePrecisionMultiple >> 1))
{
average--;
}
}
return average;
}
uint16_t LinkQualityInfo::GetAverageRssAsEncodedWord(void) const
{
return mRssAverage;
}
ThreadError LinkQualityInfo::GetAverageRssAsString(char *aCharBuffer, size_t aBufferLen) const
{
ThreadError error = kThreadError_None;
int charsWritten = 0;
if (mCount == 0)
{
VerifyOrExit(aBufferLen >= sizeof(kUnknownRssString), error = kThreadError_NoBufs);
strncpy(aCharBuffer, kUnknownRssString, aBufferLen);
}
else
{
charsWritten = snprintf(aCharBuffer, aBufferLen, "%d.%s dBm",
-(mRssAverage >> kRssAveragePrecisionMultipleBitShift),
kLinkQualityDecimalDigitsString[mRssAverage & kRssAveragePrecisionMultipleBitMask]);
VerifyOrExit(charsWritten >= 0, error = kThreadError_NoBufs);
VerifyOrExit(static_cast<size_t>(charsWritten) < aBufferLen, error = kThreadError_NoBufs);
}
exit:
return error;
}
uint8_t LinkQualityInfo::GetLinkMargin(void) const
{
return ConvertRssToLinkMargin(GetAverageRss());
}
uint8_t LinkQualityInfo::GetLinkQuality(void)
{
UpdateLinkQuality();
return mLinkQuality;
}
void LinkQualityInfo::UpdateLinkQuality(void)
{
if (mCount != 0)
{
mLinkQuality = CalculateLinkQuality(GetLinkMargin(), mLinkQuality);
}
else
{
mLinkQuality = CalculateLinkQuality(GetLinkMargin(), kNoLastLinkQualityValue);
}
}
uint8_t LinkQualityInfo::ConvertRssToLinkMargin(int8_t anRss)
{
int8_t linkMargin = anRss - GetAverageNoiseFloor();
if (linkMargin < 0 || anRss == kUnknownRss)
{
linkMargin = 0;
}
return static_cast<uint8_t>(linkMargin);
}
uint8_t LinkQualityInfo::ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin)
{
return CalculateLinkQuality(aLinkMargin, kNoLastLinkQualityValue);
}
uint8_t LinkQualityInfo::ConvertRssToLinkQuality(int8_t anRss)
{
return ConvertLinkMarginToLinkQuality(ConvertRssToLinkMargin(anRss));
}
uint8_t LinkQualityInfo::CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality)
{
uint8_t threshold1, threshold2, threshold3;
uint8_t linkQuality = 0;
threshold1 = kLinkMarginThresholdForLinkQuality1;
threshold2 = kLinkMarginThresholdForLinkQuality2;
threshold3 = kLinkMarginThresholdForLinkQuality3;
// Apply the hysteresis threshold based on the last link quality value.
switch (aLastLinkQuality)
{
case 0:
threshold1 += kLinkMarginHysteresisThreshold;
// Intentional fall-through to next case.
case 1:
threshold2 += kLinkMarginHysteresisThreshold;
// Intentional fall-through to next case.
case 2:
threshold3 += kLinkMarginHysteresisThreshold;
// Intentional fall-through to next case.
default:
break;
}
if (aLinkMargin > threshold3)
{
linkQuality = 3;
}
else if (aLinkMargin > threshold2)
{
linkQuality = 2;
}
else if (aLinkMargin > threshold1)
{
linkQuality = 1;
}
return linkQuality;
}
int8_t GetAverageNoiseFloor(void)
{
int8_t averageNoiseFloor = sNoiseFloorAverage.GetAverageRss();
if (averageNoiseFloor == LinkQualityInfo::kUnknownRss)
{
averageNoiseFloor = kDefaultNoiseFloor;
}
return averageNoiseFloor;
}
void AddNoiseFloor(int8_t aNoiseFloor)
{
sNoiseFloorAverage.AddRss(aNoiseFloor);
}
void ClearNoiseFloorAverage(void)
{
sNoiseFloorAverage.Clear();
}
} // namespace Thread
+242
View File
@@ -0,0 +1,242 @@
/*
* Copyright (c) 2016, Nest Labs, Inc.
* 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 includes definitions for storing and processing link quality information.
*/
#ifndef LINK_QUALITY_HPP_
#define LINK_QUALITY_HPP_
#include <openthread-types.h>
namespace Thread {
/**
* @addtogroup core-link-quality
*
* @brief
* This module includes definitions for Thread link quality metrics.
*
* @{
*/
/**
* This class encapsulates/stores all relevant information about quality of a link, including average received signal
* strength (RSS), link margin and link quality value (value in 0-3). The average is obtained using an adaptive
* exponential moving average filter.
*
*/
class LinkQualityInfo
{
public:
enum
{
kUnknownRss = 127, ///< Indicates an unknown signal strength value or average.
};
/**
* This constructor initializes an instance of the LinkQualityInfo class.
*
*/
LinkQualityInfo(void);
/**
* This method clears the all the data in this instance.
*
*/
void Clear(void);
/**
* This method adds a new received signal strength (RSS) value to the average.
*
* @param[in] anRss A new received signal strength value (in dBm) to be added to the average.
*
*/
void AddRss(int8_t anRss);
/**
* This method returns the current average signal strength value.
*
* @returns The current average value or @c kUnknownRss if no average is available.
*
*/
int8_t GetAverageRss(void) const;
/**
* This method returns an encoded version of current average signal strength value. The encoded value is the
* average multiplied by a precision factor (currently -8).
*
* @returns The current average multiplied by precision factor or zero if no average is available.
*
*/
uint16_t GetAverageRssAsEncodedWord(void) const;
/**
* This method provides the current average received signal strength (RSS) as a human-readable string (e.g.,
* "-80.375 dBm"). If the average is unknown, "unknown RSS" is used/returned in the buffer.
*
* @param[out] aCharBuffer A char buffer to store the string corresponding to current average value.
* @param[in] aBufferLen The char buffer length.
*
* @retval kThreadError_None Successfully formed the string in the given char buffer.
* @retval kThreadError_NoBuf The string representation of the average value could not fit in the given buffer.
*
*/
ThreadError GetAverageRssAsString(char *aCharBuffer, size_t aBufferLen) const;
/**
* This method returns the link margin. The link margin is calculated using the link's current average received
* signal strength (RSS) and average noise floor.
*
* @returns Link margin derived from average received signal strength and average noise floor.
*
*/
uint8_t GetLinkMargin(void) const;
/**
* Returns the current one-way link quality value. The link quality value is a number 0-3.
*
* The link quality is calculated by comparing the current link margin with a set of thresholds (per Thread spec).
* More specifically, link margin > 20 dB gives link quality 3, link margin > 10 dB gives link quality 2,
* link margin > 2 dB gives link quality 1, and link margin below or equal to 2 dB yields link quality of 0.
*
* In order to ensure that a link margin near the boundary of two different link quality values does not cause
* frequent changes, a hysteresis of 2 dB is applied when determining the link quality. For example, the average
* link margin must be at least 12 dB to change a quality 1 link to a quality 2 link.
*
* @returns The current link quality value (value 0-3 as per Thread specification).
*/
uint8_t GetLinkQuality(void);
/**
* This method converts a received signal strength value to a link margin value.
*
* @param[in] anRss The received signal strength value (in dBm).
*
* @returns The link margin value.
*
*/
static uint8_t ConvertRssToLinkMargin(int8_t anRss);
/**
* This method converts a link margin value to a link quality value.
*
* @param[in] aLinkMargin The Link Margin in dB.
*
* @returns The link quality value (0-3).
*
*/
static uint8_t ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin);
/**
* This method converts a received signal strength value to a link quality value.
*
* @param[in] anRss The received signal strength value (in dBm).
*
* @returns The link quality value (0-3).
*
*/
static uint8_t ConvertRssToLinkQuality(int8_t anRss);
private:
enum
{
// Constants for obtaining link quality from link margin:
kLinkMarginThresholdForLinkQuality3 = 20, // Link margin threshold for quality 3 link.
kLinkMarginThresholdForLinkQuality2 = 10, // Link margin threshold for quality 2 link.
kLinkMarginThresholdForLinkQuality1 = 2, // Link margin threshold for quality 1 link.
kLinkMarginHysteresisThreshold = 2, // Link margin hysteresis threshold.
kNoLastLinkQualityValue = 0xff, // Used to indicate that there is no previous/last link quality.
// Constants related to RSS adaptive exponential moving average filter:
kRssAveragePrecisionMultipleBitShift = 3, // Precision multiple for RSS average (1 << PrecisionBitShift).
kRssAveragePrecisionMultiple = (1 << kRssAveragePrecisionMultipleBitShift),
kRssAveragePrecisionMultipleBitMask = (kRssAveragePrecisionMultiple - 1),
kRssCountMax = 7, // mCount max limit value.
kRssCountForWeightCoefficientOneEighth = 5, // mCount threshold to use average weight coefficient of 1/8.
kRssCountForWeightCoefficientOneFourth = 2, // mCount threshold to use average weight coefficient of 1/4.
kRssCountForWeightCoefficientOneHalf = 1, // mCount threshold to use average weight coefficient of 1/2.
};
/* Private method to update the mLinkQuality value. This is called when a new RSS value is added to average
* or when GetLinkQuality() is invoked.
*/
void UpdateLinkQuality(void);
/* Static private method to calculate the link quality from a given link margin while taking into account the last
* link quality value and adding the hysteresis value to the thresholds. If there is no previous value for link
* quality, the constant kNoLastLinkQualityValue should be passed as the second argument.
*
*/
static uint8_t CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality);
static const char kUnknownRssString[]; // Constant string used when RSS average is unknown.
// All data should fit into a 16-bit (uint16_t) value.
uint16_t mRssAverage : 11; // The encoded average signal strength value (stored as rss times precision multiple).
uint8_t mCount : 3; // Number of RSS values added to average so far (limited to kRssCountMax).
uint8_t mLinkQuality : 2; // Current link quality value (0-3).
};
/**
* This function returns the current average noise floor level (in dBm).
*
* @returns The current average noise floor level (in dBm).
*/
int8_t GetAverageNoiseFloor(void);
/**
* This method adds a new noise floor value (in dBm) to the running average.
*
* @param[in] aNoiseFloor A new noise floor value (in dBm) to be added to the average.
*
*/
void AddNoiseFloor(int8_t aNoiseFloor);
/**
* This method clears the current average noise floor value.
*
*/
void ClearNoiseFloorAverage(void);
/**
* @}
*/
} // namespace Thread
#endif // LINK_QUALITY_HPP_
+1 -1
View File
@@ -1015,7 +1015,7 @@ void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame, ThreadError aError)
}
SuccessOrExit(aFrame.GetDstAddr(macDest));
messageInfo.mLinkMargin = aFrame.GetPower() - -100;
messageInfo.mRss = aFrame.GetPower();
messageInfo.mLqi = aFrame.GetLqi();
messageInfo.mLinkSecurity = aFrame.GetSecurityEnabled();
+5 -22
View File
@@ -1554,30 +1554,11 @@ exit:
return error;
}
uint8_t Mle::LinkMarginToQuality(uint8_t aLinkMargin)
{
if (aLinkMargin > 20)
{
return 3;
}
else if (aLinkMargin > 10)
{
return 2;
}
else if (aLinkMargin > 2)
{
return 1;
}
else
{
return 0;
}
}
ThreadError Mle::HandleParentResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo,
uint32_t aKeySequence)
{
ThreadError error = kThreadError_None;
const ThreadMessageInfo *threadMessageInfo = reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo);
ResponseTlv response;
SourceAddressTlv sourceAddress;
LeaderDataTlv leaderData;
@@ -1649,14 +1630,14 @@ ThreadError Mle::HandleParentResponse(const Message &aMessage, const Ip6::Messag
SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLinkMargin, sizeof(linkMarginTlv), linkMarginTlv));
VerifyOrExit(linkMarginTlv.IsValid(), error = kThreadError_Parse);
linkMargin = reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo)->mLinkMargin;
linkMargin = LinkQualityInfo::ConvertRssToLinkMargin(threadMessageInfo->mRss);
if (linkMargin > linkMarginTlv.GetLinkMargin())
{
linkMargin = linkMarginTlv.GetLinkMargin();
}
link_quality = LinkMarginToQuality(linkMargin);
link_quality = LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMargin);
VerifyOrExit(mParentRequestState != kParentRequestRouter || link_quality == 3, ;);
@@ -1708,6 +1689,8 @@ ThreadError Mle::HandleParentResponse(const Message &aMessage, const Ip6::Messag
mParent.mValid.mLinkFrameCounter = linkFrameCounter.GetFrameCounter();
mParent.mValid.mMleFrameCounter = mleFrameCounter.GetFrameCounter();
mParent.mMode = ModeTlv::kModeFFD | ModeTlv::kModeRxOnWhenIdle | ModeTlv::kModeFullNetworkData;
mParent.mLinkInfo.Clear();
mParent.mLinkInfo.AddRss(threadMessageInfo->mRss);
mParent.mState = Neighbor::kStateValid;
assert(aKeySequence == mKeyManager.GetCurrentKeySequence() ||
aKeySequence == mKeyManager.GetPreviousKeySequence());
-10
View File
@@ -805,16 +805,6 @@ protected:
*/
Mac::ShortAddress GetNextHop(uint16_t aDestination) const;
/**
* This method converts a link margin value to a link quality value.
*
* @param[in] aLinkMargin The Link Margin in dB.
*
* @returns The link quality value.
*
*/
uint8_t LinkMarginToQuality(uint8_t aLinkMargin);
/**
* This method generates an MLE Data Request message.
*
+34 -22
View File
@@ -615,8 +615,12 @@ ThreadError MleRouter::HandleLinkRequest(const Message &aMessage, const Ip6::Mes
if (neighbor->mState != Neighbor::kStateValid)
{
const ThreadMessageInfo *threadMessageInfo =
reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo);
memcpy(&neighbor->mMacAddr, &macAddr, sizeof(neighbor->mMacAddr));
neighbor->mRssi = reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo)->mLinkMargin;
neighbor->mLinkInfo.Clear();
neighbor->mLinkInfo.AddRss(threadMessageInfo->mRss);
neighbor->mState = Neighbor::kStateLinkRequest;
}
else
@@ -651,6 +655,7 @@ ThreadError MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, Neig
const TlvRequestTlv &aTlvRequest, const ChallengeTlv &aChallenge)
{
ThreadError error = kThreadError_None;
const ThreadMessageInfo *threadMessageInfo = reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo);
static const uint8_t routerTlvs[] = {Tlv::kLinkMargin};
Message *message;
Header::Command command;
@@ -669,7 +674,7 @@ ThreadError MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, Neig
SuccessOrExit(error = AppendMleFrameCounter(*message));
// always append a link margin, regardless of whether or not it was requested
linkMargin = reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo)->mLinkMargin;
linkMargin = LinkQualityInfo::ConvertRssToLinkMargin(threadMessageInfo->mRss);
SuccessOrExit(error = AppendLinkMargin(*message, linkMargin));
if (aNeighbor != NULL && GetChildId(aNeighbor->mValid.mRloc16) == 0)
@@ -748,6 +753,7 @@ ThreadError MleRouter::HandleLinkAccept(const Message &aMessage, const Ip6::Mess
uint32_t aKeySequence, bool aRequest)
{
ThreadError error = kThreadError_None;
const ThreadMessageInfo *threadMessageInfo = reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo);
Neighbor *neighbor = NULL;
Mac::ExtAddress macAddr;
VersionTlv version;
@@ -860,9 +866,8 @@ ThreadError MleRouter::HandleLinkAccept(const Message &aMessage, const Ip6::Mess
case kDeviceStateChild:
SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLinkMargin, sizeof(linkMargin), linkMargin));
VerifyOrExit(linkMargin.IsValid(), error = kThreadError_Parse);
mRouters[routerId].mLinkQualityOut = LinkMarginToQuality(linkMargin.GetLinkMargin());
mRouters[routerId].mLinkQualityIn = LinkMarginToQuality(reinterpret_cast<const ThreadMessageInfo *>
(aMessageInfo.mLinkInfo)->mLinkMargin);
mRouters[routerId].mLinkQualityOut =
LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMargin.GetLinkMargin());
break;
case kDeviceStateRouter:
@@ -875,9 +880,8 @@ ThreadError MleRouter::HandleLinkAccept(const Message &aMessage, const Ip6::Mess
// Link Margin
SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLinkMargin, sizeof(linkMargin), linkMargin));
VerifyOrExit(linkMargin.IsValid(), error = kThreadError_Parse);
mRouters[routerId].mLinkQualityOut = LinkMarginToQuality(linkMargin.GetLinkMargin());
mRouters[routerId].mLinkQualityIn = LinkMarginToQuality(reinterpret_cast<const ThreadMessageInfo *>
(aMessageInfo.mLinkInfo)->mLinkMargin);
mRouters[routerId].mLinkQualityOut =
LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMargin.GetLinkMargin());
// update routing table
if (routerId != mRouterId && mRouters[routerId].mNextHop == kMaxRouterId)
@@ -896,6 +900,8 @@ ThreadError MleRouter::HandleLinkAccept(const Message &aMessage, const Ip6::Mess
neighbor->mValid.mMleFrameCounter = mleFrameCounter.GetFrameCounter();
neighbor->mLastHeard = Timer::GetNow();
neighbor->mMode = ModeTlv::kModeFFD | ModeTlv::kModeRxOnWhenIdle | ModeTlv::kModeFullNetworkData;
neighbor->mLinkInfo.Clear();
neighbor->mLinkInfo.AddRss(threadMessageInfo->mRss);
neighbor->mState = Neighbor::kStateValid;
assert(aKeySequence == mKeyManager.GetCurrentKeySequence() ||
aKeySequence == mKeyManager.GetPreviousKeySequence());
@@ -1018,7 +1024,7 @@ uint8_t MleRouter::GetLinkCost(uint8_t aRouterId)
mRouters[aRouterId].mState == Neighbor::kStateValid,
rval = kMaxRouteCost);
rval = mRouters[aRouterId].mLinkQualityIn;
rval = mRouters[aRouterId].mLinkInfo.GetLinkQuality();
if (rval > mRouters[aRouterId].mLinkQualityOut)
{
@@ -1070,6 +1076,7 @@ exit:
ThreadError MleRouter::HandleAdvertisement(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
ThreadError error = kThreadError_None;
const ThreadMessageInfo *threadMessageInfo = reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo);
Mac::ExtAddress macAddr;
SourceAddressTlv sourceAddress;
LeaderDataTlv leaderData;
@@ -1171,6 +1178,8 @@ ThreadError MleRouter::HandleAdvertisement(const Message &aMessage, const Ip6::M
if (router->mState != Neighbor::kStateValid)
{
memcpy(&router->mMacAddr, &macAddr, sizeof(router->mMacAddr));
router->mLinkInfo.Clear();
router->mLinkInfo.AddRss(threadMessageInfo->mRss);
router->mState = Neighbor::kStateLinkRequest;
router->mPreviousKey = false;
SendLinkRequest(router);
@@ -1179,8 +1188,6 @@ ThreadError MleRouter::HandleAdvertisement(const Message &aMessage, const Ip6::M
}
router->mLastHeard = Timer::GetNow();
router->mLinkQualityIn =
LinkMarginToQuality(reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo)->mLinkMargin);
ExitNow();
@@ -1198,6 +1205,8 @@ ThreadError MleRouter::HandleAdvertisement(const Message &aMessage, const Ip6::M
if (router->mState != Neighbor::kStateValid)
{
memcpy(&router->mMacAddr, &macAddr, sizeof(router->mMacAddr));
router->mLinkInfo.Clear();
router->mLinkInfo.AddRss(threadMessageInfo->mRss);
router->mState = Neighbor::kStateLinkRequest;
router->mDataRequest = false;
router->mPreviousKey = false;
@@ -1206,8 +1215,6 @@ ThreadError MleRouter::HandleAdvertisement(const Message &aMessage, const Ip6::M
}
router->mLastHeard = Timer::GetNow();
router->mLinkQualityIn =
LinkMarginToQuality(reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo)->mLinkMargin);
break;
}
@@ -1335,7 +1342,8 @@ void MleRouter::UpdateRoutes(const RouteTlv &aRoute, uint8_t aRouterId)
}
otLogDebgMle("%x: %x %d %d %d %d\n", GetRloc16(i), GetRloc16(mRouters[i].mNextHop),
mRouters[i].mCost, GetLinkCost(i), mRouters[i].mLinkQualityIn, mRouters[i].mLinkQualityOut);
mRouters[i].mCost, GetLinkCost(i), mRouters[i].mLinkInfo.GetLinkQuality(),
mRouters[i].mLinkQualityOut);
}
#endif
@@ -1344,6 +1352,7 @@ void MleRouter::UpdateRoutes(const RouteTlv &aRoute, uint8_t aRouterId)
ThreadError MleRouter::HandleParentRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
ThreadError error = kThreadError_None;
const ThreadMessageInfo *threadMessageInfo = reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo);
Mac::ExtAddress macAddr;
VersionTlv version;
ScanMaskTlv scanMask;
@@ -1401,11 +1410,12 @@ ThreadError MleRouter::HandleParentRequest(const Message &aMessage, const Ip6::M
// MAC Address
memcpy(&child->mMacAddr, &macAddr, sizeof(child->mMacAddr));
child->mLinkInfo.Clear();
child->mLinkInfo.AddRss(threadMessageInfo->mRss);
child->mState = Neighbor::kStateParentRequest;
child->mDataRequest = false;
child->mPreviousKey = false;
child->mRssi = reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo)->mLinkMargin;
child->mTimeout = Timer::SecToMsec(2 * kParentRequestChildTimeout);
SuccessOrExit(error = SendParentResponse(child, challenge));
@@ -1479,8 +1489,8 @@ void MleRouter::HandleStateUpdateTimer(void)
if ((Timer::GetNow() - mRouters[i].mLastHeard) >= Timer::SecToMsec(kMaxNeighborAge))
{
mRouters[i].mState = Neighbor::kStateInvalid;
mRouters[i].mLinkInfo.Clear();
mRouters[i].mNextHop = kMaxRouterId;
mRouters[i].mLinkQualityIn = 0;
mRouters[i].mLinkQualityOut = 0;
mRouters[i].mLastHeard = Timer::GetNow();
}
@@ -1534,7 +1544,7 @@ ThreadError MleRouter::SendParentResponse(Child *aChild, const ChallengeTlv &cha
}
SuccessOrExit(error = AppendChallenge(*message, aChild->mPending.mChallenge, sizeof(aChild->mPending.mChallenge)));
SuccessOrExit(error = AppendLinkMargin(*message, aChild->mRssi));
SuccessOrExit(error = AppendLinkMargin(*message, aChild->mLinkInfo.GetLinkMargin()));
SuccessOrExit(error = AppendConnectivity(*message));
SuccessOrExit(error = AppendVersion(*message));
@@ -1589,6 +1599,7 @@ ThreadError MleRouter::HandleChildIdRequest(const Message &aMessage, const Ip6::
uint32_t aKeySequence)
{
ThreadError error = kThreadError_None;
const ThreadMessageInfo *threadMessageInfo = reinterpret_cast<const ThreadMessageInfo *>(aMessageInfo.mLinkInfo);
Mac::ExtAddress macAddr;
ResponseTlv response;
LinkFrameCounterTlv linkFrameCounter;
@@ -1664,6 +1675,7 @@ ThreadError MleRouter::HandleChildIdRequest(const Message &aMessage, const Ip6::
child->mValid.mLinkFrameCounter = linkFrameCounter.GetFrameCounter();
child->mValid.mMleFrameCounter = mleFrameCounter.GetFrameCounter();
child->mMode = mode.GetMode();
child->mLinkInfo.AddRss(threadMessageInfo->mRss);
child->mTimeout = timeout.GetTimeout();
if (mode.GetMode() & ModeTlv::kModeFullNetworkData)
@@ -2713,7 +2725,7 @@ ThreadError MleRouter::AppendConnectivity(Message &aMessage)
break;
case kDeviceStateChild:
switch (mParent.mLinkQualityIn)
switch (mParent.mLinkInfo.GetLinkQuality())
{
case 1:
tlv.SetLinkQuality1(tlv.GetLinkQuality1() + 1);
@@ -2728,7 +2740,7 @@ ThreadError MleRouter::AppendConnectivity(Message &aMessage)
break;
}
cost += LqiToCost(mParent.mLinkQualityIn);
cost += LqiToCost(mParent.mLinkInfo.GetLinkQuality());
break;
case kDeviceStateRouter:
@@ -2747,7 +2759,7 @@ ThreadError MleRouter::AppendConnectivity(Message &aMessage)
continue;
}
lqi = mRouters[i].mLinkQualityIn;
lqi = mRouters[i].mLinkInfo.GetLinkQuality();
if (lqi > mRouters[i].mLinkQualityOut)
{
@@ -2865,7 +2877,7 @@ ThreadError MleRouter::AppendRoute(Message &aMessage)
}
tlv.SetRouteCost(routeCount, cost);
tlv.SetLinkQualityIn(routeCount, mRouters[i].mLinkQualityIn);
tlv.SetLinkQualityIn(routeCount, mRouters[i].mLinkInfo.GetLinkQuality());
tlv.SetLinkQualityOut(routeCount, mRouters[i].mLinkQualityOut);
}
+1 -1
View File
@@ -225,7 +225,7 @@ private:
*/
struct ThreadMessageInfo
{
uint8_t mLinkMargin; ///< The Link Margin for a received message in dB.
int8_t mRss; ///< The Received Signal Strength in dBm.
uint8_t mLqi; ///< The Link Quality Indicator for a received message.
bool mLinkSecurity; ///< Indicates whether or not link security is enabled.
};
+6 -6
View File
@@ -38,6 +38,7 @@
#include <mac/mac_frame.hpp>
#include <net/ip6.hpp>
#include <thread/mle_tlvs.hpp>
#include <thread/link_quality.hpp>
namespace Thread {
@@ -77,11 +78,11 @@ public:
kStateLinkRequest, ///< Sent a MLE Link Request message
kStateValid, ///< Link is valid
};
State mState : 3; ///< The link state
uint8_t mMode : 4; ///< The MLE device mode
bool mPreviousKey : 1; ///< Indicates whether or not the neighbor is still using a previous key
bool mDataRequest : 1; ///< Indicates whether or not a Data Poll was received
int8_t mRssi; ///< Received Signal Strength Indicator
State mState : 3; ///< The link state
uint8_t mMode : 4; ///< The MLE device mode
bool mPreviousKey : 1; ///< Indicates whether or not the neighbor is still using a previous key
bool mDataRequest : 1; ///< Indicates whether or not a Data Poll was received
LinkQualityInfo mLinkInfo; ///< Link quality info (contains average RSS, link margin and link quality).
};
/**
@@ -111,7 +112,6 @@ class Router : public Neighbor
public:
uint8_t mNextHop; ///< The next hop towards this router
uint8_t mLinkQualityOut : 2; ///< The link quality out for this router
uint8_t mLinkQualityIn : 2; ///< The link quality in for this router
uint8_t mCost : 4; ///< The cost to this router
bool mAllocated : 1; ///< Indicates whether or not this entry is allocated
bool mReclaimDelay : 1; ///< Indicates whether or not this entry is waiting to be reclaimed
+4
View File
@@ -68,6 +68,7 @@ check_PROGRAMS = \
test-aes \
test-hmac-sha256 \
test-lowpan \
test-link-quality \
test-mac-frame \
test-message \
test-timer \
@@ -95,6 +96,9 @@ test_aes_SOURCES = test_aes.cpp
test_hmac_sha256_LDADD = $(COMMON_LDADD)
test_hmac_sha256_SOURCES = test_hmac_sha256.cpp
test_link_quality_LDADD = $(COMMON_LDADD)
test_link_quality_SOURCES = test_link_quality.cpp
test_lowpan_LDADD = $(COMMON_LDADD)
test_lowpan_SOURCES = test_lowpan.cpp test_util.cpp test_vector.c
+348
View File
@@ -0,0 +1,348 @@
/*
* Copyright (c) 2016, Nest Labs, Inc.
* 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.
*/
#include "test_util.h"
#include <openthread.h>
#include <thread/link_quality.hpp>
#include <string.h>
namespace Thread {
extern"C" void otSignalTaskletPending(void)
{
}
enum
{
kMaxRssValue = 0,
kMinRssValue = -128,
kStringBuffferSize = 80,
kRssAverageMaxDiff = 16,
kNumRssAdds = 300,
kEncodedAverageBitShift = 3,
kEncodedAverageMultiple = (1 << kEncodedAverageBitShift),
kEncodedAverageBitMask = (1 << kEncodedAverageBitShift) - 1,
};
#define MIN_RSS(_rss1, _rss2) (((_rss1) < (_rss2)) ? (_rss1) : (_rss2))
#define MAX_RSS(_rss1, _rss2) (((_rss1) < (_rss2)) ? (_rss2) : (_rss1))
#define ABS(value) (((value) >= 0) ? (value) : -(value))
// This struct contains RSS values and test data for checking link quality info calss.
struct RssTestData
{
const int8_t *mRssList; // Array of RSS values.
size_t mRssListSize; // Size of RSS list.
uint8_t mExpectedLinkQuality; // Expected final link quality value.
};
// Checks the encoded average RSS value to match the value from GetAverageRss().
void VerifyEncodedRssValue(LinkQualityInfo &aLinkInfo)
{
int8_t rss = aLinkInfo.GetAverageRss();
uint16_t encodedRss = aLinkInfo.GetAverageRssAsEncodedWord();
if (rss != LinkQualityInfo::kUnknownRss)
{
VerifyOrQuit(rss == -static_cast<int16_t>((encodedRss + (kEncodedAverageMultiple / 2)) >> kEncodedAverageBitShift),
"TestLinkQualityInfo failed - Ecoded RSS does not match the value from GetAverageRss().");
}
else
{
VerifyOrQuit(encodedRss == 0,
"TestLinkQualityInfo failed - Ecoded RSS does not match the value from GetAverageRss().");
}
}
// This function prints the values in the passed in link info instance. It is invoked as the final step in test-case.
void PrintOutcome(LinkQualityInfo &aLinkInfo)
{
char stringBuf[kStringBuffferSize];
SuccessOrQuit(aLinkInfo.GetAverageRssAsString(stringBuf, sizeof(stringBuf)),
"TestLinkQualityInfo failed - GetAverageRssAsString() failed.");
printf("AveRss = %-4d, \"%-14s\", ", aLinkInfo.GetAverageRss(), stringBuf);
printf("LinkMargin = %-4d, LinkQuality = %d", aLinkInfo.GetLinkMargin(), aLinkInfo.GetLinkQuality());
// This test-case succeeded.
printf(" -> PASS\n");
}
void TestLinkQualityData(RssTestData anRssData)
{
LinkQualityInfo linkInfo;
int8_t rss, ave, min, max;
size_t i;
printf("- - - - - - - - - - - - - - - - - -\n");
min = kMinRssValue;
max = kMaxRssValue;
for (i = 0; i < anRssData.mRssListSize; i++)
{
rss = anRssData.mRssList[i];
min = MIN_RSS(rss, min);
max = MAX_RSS(rss, max);
linkInfo.AddRss(rss);
ave = linkInfo.GetAverageRss();
VerifyOrQuit(ave >= min,
"TestLinkQualityInfo failed - GetAverageRss() is smaller than min value.");
VerifyOrQuit(ave <= max,
"TestLinkQualityInfo failed - GetAverageRss() is larger than min value");
VerifyEncodedRssValue(linkInfo);
printf("%02u) AddRss(%4d): ", (unsigned int)i, rss);
PrintOutcome(linkInfo);
}
VerifyOrQuit(linkInfo.GetLinkQuality() == anRssData.mExpectedLinkQuality,
"TestLinkQualityInfo failed - GetLinkQuality() is incorrect");
}
void TestRssAveraging(void)
{
LinkQualityInfo linkInfo;
int8_t rss, rss2, ave;
int16_t diff;
size_t i, j, k;
const int8_t rssValues[] = { kMinRssValue, -70, -40, -41, -10, kMaxRssValue};
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Values after initialization.
printf("\nAfter Initialization: ");
VerifyOrQuit(linkInfo.GetAverageRss() == LinkQualityInfo::kUnknownRss,
"TestLinkQualityInfo failed - Inital value from GetAverageRss() is incorrect.");
VerifyOrQuit(linkInfo.GetLinkMargin() == 0,
"TestLinkQualityInfo failed - Inital value for link margin is incorrect.");
VerifyEncodedRssValue(linkInfo);
PrintOutcome(linkInfo);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Adding a single value
rss = -70;
printf("AddRss(%d): ", rss);
linkInfo.AddRss(rss);
VerifyOrQuit(linkInfo.GetAverageRss() == rss,
"TestLinkQualityInfo - GetAverageRss() failed after a single AddRss().");
VerifyEncodedRssValue(linkInfo);
PrintOutcome(linkInfo);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Clear
printf("Clear(): ");
linkInfo.Clear();
VerifyOrQuit(linkInfo.GetAverageRss() == LinkQualityInfo::kUnknownRss,
"TestLinkQualityInfo failed - GetAverageRss() after Clear() is incorrect.");
VerifyOrQuit(linkInfo.GetLinkMargin() == 0,
"TestLinkQualityInfo failed - link margin value after Clear() is incorrect.");
VerifyEncodedRssValue(linkInfo);
PrintOutcome(linkInfo);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Adding the same value many times.
printf("- - - - - - - - - - - - - - - - - -\n");
for (j = 0; j < sizeof(rssValues); j++)
{
linkInfo.Clear();
rss = rssValues[j];
printf("AddRss(%4d) %d times: ", rss, kNumRssAdds);
for (i = 0; i < kNumRssAdds; i++)
{
linkInfo.AddRss(rss);
VerifyOrQuit(linkInfo.GetAverageRss() == rss,
"TestLinkQualityInfo failed - GetAverageRss() returned incorrect value.");
VerifyEncodedRssValue(linkInfo);
}
PrintOutcome(linkInfo);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Adding two RSS values:
printf("- - - - - - - - - - - - - - - - - -\n");
for (j = 0; j < sizeof(rssValues); j++)
{
rss = rssValues[j];
for (k = 0; k < sizeof(rssValues); k++)
{
if (k == j)
{
continue;
}
rss2 = rssValues[k];
linkInfo.Clear();
linkInfo.AddRss(rss);
linkInfo.AddRss(rss2);
printf("AddRss(%4d), AddRss(%4d): ", rss, rss2);
VerifyOrQuit(linkInfo.GetAverageRss() == ((rss + rss2) >> 1),
"TestLinkQualityInfo failed - GetAverageRss() returned incorrect value.");
VerifyEncodedRssValue(linkInfo);
PrintOutcome(linkInfo);
}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Adding one value many times and a different value once:
printf("- - - - - - - - - - - - - - - - - -\n");
for (j = 0; j < sizeof(rssValues); j++)
{
rss = rssValues[j];
for (k = 0; k < sizeof(rssValues); k++)
{
if (k == j)
{
continue;
}
rss2 = rssValues[k];
linkInfo.Clear();
for (i = 0; i < kNumRssAdds; i++)
{
linkInfo.AddRss(rss);
}
linkInfo.AddRss(rss2);
printf("AddRss(%4d) %d times, AddRss(%4d): ", rss, kNumRssAdds, rss2);
ave = linkInfo.GetAverageRss();
VerifyOrQuit(ave >= MIN_RSS(rss, rss2),
"TestLinkQualityInfo failed - GetAverageRss() returned incorrect value.");
VerifyOrQuit(ave <= MAX_RSS(rss, rss2),
"TestLinkQualityInfo failed - GetAverageRss() returned incorrect value.");
VerifyEncodedRssValue(linkInfo);
PrintOutcome(linkInfo);
}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Adding two alteraing values many times:
printf("- - - - - - - - - - - - - - - - - -\n");
for (j = 0; j < sizeof(rssValues); j++)
{
rss = rssValues[j];
for (k = 0; k < sizeof(rssValues); k++)
{
if (k == j)
{
continue;
}
rss2 = rssValues[k];
linkInfo.Clear();
for (i = 0; i < kNumRssAdds; i++)
{
linkInfo.AddRss(rss);
linkInfo.AddRss(rss2);
ave = linkInfo.GetAverageRss();
VerifyOrQuit(ave >= MIN_RSS(rss, rss2),
"TestLinkQualityInfo failed - GetAverageRss() is smaller than min value.");
VerifyOrQuit(ave <= MAX_RSS(rss, rss2),
"TestLinkQualityInfo failed - GetAverageRss() is larger than min value.");
diff = ave;
diff -= (rss + rss2) >> 1;
VerifyOrQuit(ABS(diff) <= kRssAverageMaxDiff,
"TestLinkQualityInfo failed - GetAverageRss() is incorrect");
VerifyEncodedRssValue(linkInfo);
}
printf("[AddRss(%4d), AddRss(%4d)] %d times: ", rss, rss2, kNumRssAdds);
PrintOutcome(linkInfo);
}
}
}
void TestLinkQualityCalculations(void)
{
const int8_t rssList1[] = { -81, -80, -79, -78, -76, -80, -77, -75, -77, -76, -77, -74};
const RssTestData rssData1 =
{
rssList1, // mRssList
sizeof(rssList1), // mRssListSize
3 // mExpectedLinkQuality
};
const int8_t rssList2[] = { -90, -80, -85 };
const RssTestData rssData2 =
{
rssList2, // mRssList
sizeof(rssList2), // mRssListSize
2 // mExpectedLinkQuality
};
const int8_t rssList3[] = { -95, -96, -98, -99, -100, -100, -98, -99, -100, -100, -100, -100, -100 };
const RssTestData rssData3 =
{
rssList3, // mRssList
sizeof(rssList3), // mRssListSize
0 // mExpectedLinkQuality
};
const int8_t rssList4[] = { -75, -100, -100, -100, -100, -100, -95, -92, -93, -94, -93, -93 };
const RssTestData rssData4 =
{
rssList4, // mRssList
sizeof(rssList4), // mRssListSize
1 // mExpectedLinkQuality
};
TestLinkQualityData(rssData1);
TestLinkQualityData(rssData2);
TestLinkQualityData(rssData3);
TestLinkQualityData(rssData4);
}
} // namespace Thread
int main(void)
{
Thread::TestRssAveraging();
Thread::TestLinkQualityCalculations();
printf("All tests passed\n");
return 0;
}