mirror of
https://github.com/espressif/openthread.git
synced 2026-09-14 21:20:11 +00:00
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:
committed by
Jonathan Hui
parent
3635bb9691
commit
cb33898827
@@ -95,6 +95,7 @@ extern "C" {
|
||||
* @defgroup core-tasklet Tasklet
|
||||
* @defgroup core-timer Timer
|
||||
* @defgroup core-udp UDP
|
||||
* @defgroup core-link-quality Link Quality
|
||||
*
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
@@ -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());
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user