[link-quality] introduce RssAverager and keep track of average RSS for a received message (#1961)

This commit  introduces a new class `RssAverager` (which contains the
existing averaging logic from`LinkQualityInfo` class). It also updates
`Message` class to maintain the average RSS for a received message
(note that a message may be composed of multiple 802.15.4 fragments
data frames each received with different signal strength). The
`MeshForwarder::LogIp6Message()` is updated to log the RSS value for
received/dropped messages.
This commit is contained in:
Abtin Keshavarzian
2017-07-11 09:10:54 -07:00
committed by Jonathan Hui
parent 3078359e38
commit 92b0ddca0a
10 changed files with 439 additions and 274 deletions
+8
View File
@@ -163,6 +163,14 @@ bool otMessageIsLinkSecurityEnabled(otMessage *aMessage);
*/
void otMessageSetDirectTransmission(otMessage *aMessage, bool aEnabled);
/**
* This function returns the average RSS (received signal strength) associated with the message.
*
* @returns The average RSS value (in dBm) or OT_RADIO_RSSI_INVALID if no average/RSS is available.
*
*/
int8_t otMessageGetRss(otMessage *aMessage);
/**
* Append bytes to a message.
*
+6
View File
@@ -87,6 +87,12 @@ void otMessageSetDirectTransmission(otMessage *aMessage, bool aEnabled)
}
}
int8_t otMessageGetRss(otMessage *aMessage)
{
Message *message = static_cast<Message *>(aMessage);
return message->GetAverageRss();
}
otError otMessageAppend(otMessage *aMessage, const void *aBuf, uint16_t aLength)
{
Message *message = static_cast<Message *>(aMessage);
+1 -1
View File
@@ -315,7 +315,7 @@ otError otThreadGetParentInfo(otInstance *aInstance, otRouterInfo *aParentInfo)
aParentInfo->mRouterId = Mle::Mle::GetRouterId(parent->GetRloc16());
aParentInfo->mNextHop = parent->GetNextHop();
aParentInfo->mPathCost = parent->GetCost();
aParentInfo->mLinkQualityIn = parent->GetLinkInfo().GetLinkQuality(aInstance->mThreadNetif.GetMac().GetNoiseFloor());
aParentInfo->mLinkQualityIn = parent->GetLinkInfo().GetLinkQuality();
aParentInfo->mLinkQualityOut = parent->GetLinkQualityOut();
aParentInfo->mAge = static_cast<uint8_t>(TimerMilli::MsecToSec(TimerMilli::GetNow() -
parent->GetLastHeard()));
+28
View File
@@ -46,6 +46,7 @@
#include "common/code_utils.hpp"
#include "common/locator.hpp"
#include "mac/mac_frame.hpp"
#include "thread/link_quality.hpp"
namespace ot {
@@ -96,6 +97,7 @@ struct MessageInfo
uint16_t mLength; ///< Number of bytes within the message.
uint16_t mOffset; ///< A byte offset within the message.
uint16_t mDatagramTag; ///< The datagram tag used for 6LoWPAN fragmentation.
RssAverager mRssAverager; ///< The averager maininting the received signal strength (RSS) average.
uint8_t mChildMask[8]; ///< A bit-vector to indicate which sleepy children need to receive this.
uint8_t mTimeout; ///< Seconds remaining before dropping the message.
@@ -617,6 +619,32 @@ public:
*/
void SetLinkSecurityEnabled(bool aEnabled) { mBuffer.mHead.mInfo.mLinkSecurity = aEnabled; }
/**
* This method updates the average RSS (Received Signal Strength) associated with the message by adding the given
* RSS value to the average. Note that a message can be composed of multiple 802.15.4 data frame fragments each
* received with a different signal strength.
*
* @param[in] aRss A new RSS value (in dBm) to be added to average.
*
*/
void AddRss(int8_t aRss) { mBuffer.mHead.mInfo.mRssAverager.Add(aRss); }
/**
* This method returns the average RSS (Received Signal Strength) associated with the message.
*
* @returns The current average RSS value (in dBm) or OT_RADIO_RSSI_INVALID if no average is available.
*
*/
int8_t GetAverageRss(void) const { return mBuffer.mHead.mInfo.mRssAverager.GetAverage(); }
/**
* This method returns a const reference to RssAverager of the message.
*
* @returns A const reference to the RssAverager of the message.
*
*/
const RssAverager &GetRssAverager(void) const { return mBuffer.mHead.mInfo.mRssAverager; }
/**
* This method is used to update a checksum value.
*
+101 -126
View File
@@ -36,52 +36,34 @@
#include "link_quality.hpp"
#include <stdio.h>
#include "utils/wrap_string.h"
#include "common/code_utils.hpp"
#include "utils/wrap_string.h"
namespace ot {
enum
// This array gives the decimal point digits representing 0/8, 1/8, ..., 7/8 (does not include the '.').
static const char *const kDigitsString[8] =
{
kDefaultNoiseFloor = -100, // Default noise floor used if no average value is available.
// 0/8, 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8
"0", "125", "25", "375", "5", "625", "75", "875"
};
// 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] =
void RssAverager::Reset(void)
{
// 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";
//-------------------------------------------------------------------------------
LinkQualityInfo::LinkQualityInfo(void)
{
Clear();
mAverage = 0;
mCount = 0;
}
void LinkQualityInfo::Clear(void)
otError RssAverager::Add(int8_t aRss)
{
mRssAverage = 0;
mCount = 0;
mLinkQuality = 0;
mLastRss = 0;
}
otError error = OT_ERROR_NONE;
uint16_t newValue;
uint16_t oldAverage;
void LinkQualityInfo::AddRss(int8_t aNoiseFloor, int8_t aRss)
{
uint16_t newValue;
uint16_t oldAverage;
VerifyOrExit(aRss != OT_RADIO_RSSI_INVALID);
mLastRss = aRss;
// Restrict/Cap the RSS value to the closed range [0, -128] so the value can fit in 8 bits.
VerifyOrExit(aRss != OT_RADIO_RSSI_INVALID, error = OT_ERROR_INVALID_ARGS);
// Restrict the RSS value to the closed range [0, -128] so the RSS times precision multiple can fit in 11 bits.
if (aRss > 0)
{
aRss = 0;
@@ -90,116 +72,109 @@ void LinkQualityInfo::AddRss(int8_t aNoiseFloor, int8_t aRss)
// Multiply the the RSS value by a precision multiple (currently -8).
newValue = static_cast<uint16_t>(-aRss);
newValue <<= kRssAveragePrecisionMultipleBitShift;
newValue <<= kPrecisionBitShift;
oldAverage = mRssAverage;
if (mCount >= kRssCountForWeightCoefficientOneEighth)
{
// New average = old average * 7/8 + new value * 1/8
mRssAverage = static_cast<uint16_t>(((oldAverage << 3) - oldAverage + newValue) >> 3);
}
else if (mCount >= kRssCountForWeightCoefficientOneFourth)
{
// New average = old average * 3/4 + new value * 1/4
mRssAverage = static_cast<uint16_t>(((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(aNoiseFloor);
exit:
return;
}
int8_t LinkQualityInfo::GetAverageRss(void) const
{
int8_t average = OT_RADIO_RSSI_INVALID;
if (mCount != 0)
{
average = -static_cast<int8_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;
}
otError LinkQualityInfo::GetAverageRssAsString(char *aCharBuffer, size_t aBufferLen) const
{
otError error = OT_ERROR_NONE;
int charsWritten = 0;
oldAverage = mAverage;
if (mCount == 0)
{
charsWritten = static_cast<int>(strlcpy(aCharBuffer, kUnknownRssString, aBufferLen));
mCount++;
mAverage = newValue;
}
else if (mCount < (1 << kCoeffBitShift) - 1)
{
mCount++;
// Maintain arithmetic mean.
// newAverage = newValue * (1/mCount) + oldAverage * ((mCount -1)/mCount)
mAverage = static_cast<uint16_t>(((oldAverage * (mCount - 1)) + newValue) / mCount);
}
else
{
charsWritten = snprintf(aCharBuffer, aBufferLen, "%d.%s dBm",
-(mRssAverage >> kRssAveragePrecisionMultipleBitShift),
kLinkQualityDecimalDigitsString[mRssAverage & kRssAveragePrecisionMultipleBitMask]);
// Maintain exponentially weighted moving average using coefficient of (1/2^kCoeffBitShift).
// newAverage = + newValue * 1/2^j + oldAverage * (1 - 1/2^j), for j = kCoeffBitShift.
mAverage = static_cast<uint16_t>(((oldAverage << kCoeffBitShift) - oldAverage + newValue) >> kCoeffBitShift);
}
VerifyOrExit(charsWritten >= 0, error = OT_ERROR_NO_BUFS);
VerifyOrExit(charsWritten < static_cast<int>(aBufferLen), error = OT_ERROR_NO_BUFS);
exit:
return error;
}
uint8_t LinkQualityInfo::GetLinkMargin(int8_t aNoiseFloor) const
int8_t RssAverager::GetAverage(void) const
{
return ConvertRssToLinkMargin(aNoiseFloor, GetAverageRss());
}
int8_t average;
uint8_t LinkQualityInfo::GetLinkQuality(int8_t aNoiseFloor)
{
UpdateLinkQuality(aNoiseFloor);
VerifyOrExit(mCount != 0, average = OT_RADIO_RSSI_INVALID);
return mLinkQuality;
}
average = -static_cast<int8_t>(mAverage >> kPrecisionBitShift);
int8_t LinkQualityInfo::GetLastRss(void) const
{
return mLastRss;
}
// Check for possible round up (e.g., average of -71.5 --> -72)
void LinkQualityInfo::UpdateLinkQuality(int8_t aNoiseFloor)
{
if (mCount != 0)
if ((mAverage & kPrecisionBitMask) >= (kPrecision >> 1))
{
mLinkQuality = CalculateLinkQuality(GetLinkMargin(aNoiseFloor), mLinkQuality);
average--;
}
exit:
return average;
}
const char *RssAverager::ToString(char *aBuf, uint16_t aSize) const
{
if (mCount == 0)
{
VerifyOrExit(aSize > 0);
*aBuf = 0;
}
else
{
mLinkQuality = CalculateLinkQuality(GetLinkMargin(aNoiseFloor), kNoLastLinkQualityValue);
snprintf(aBuf, aSize, "%d.%s", -(mAverage >> kPrecisionBitShift), kDigitsString[mAverage & kPrecisionBitMask]);
}
exit:
return aBuf;
}
LinkQualityInfo::LinkQualityInfo(void):
mLastRss(OT_RADIO_RSSI_INVALID)
{
mRssAverager.Reset();
SetLinkQuality(0);
}
void LinkQualityInfo::Clear(void)
{
mRssAverager.Reset();
SetLinkQuality(0);
mLastRss = OT_RADIO_RSSI_INVALID;
}
void LinkQualityInfo::AddRss(int8_t aNoiseFloor, int8_t aRss)
{
uint8_t oldLinkQuality = kNoLinkQuality;
if (mRssAverager.HasAverage())
{
oldLinkQuality = GetLinkQuality();
}
SuccessOrExit(mRssAverager.Add(aRss));
SetLinkQuality(CalculateLinkQuality(GetLinkMargin(aNoiseFloor), oldLinkQuality));
exit:
return;
}
const char *LinkQualityInfo::ToInfoString(char *aBuf, uint16_t aSize) const
{
char rssString[RssAverager::kStringSize];
snprintf(aBuf, aSize, "aveRss:%s, lastRss:%d, linkQuality:%d",
mRssAverager.ToString(rssString, sizeof(rssString)), GetLastRss(), GetLinkQuality());
return aBuf;
}
uint8_t LinkQualityInfo::ConvertRssToLinkMargin(int8_t aNoiseFloor, int8_t aRss)
@@ -216,7 +191,7 @@ uint8_t LinkQualityInfo::ConvertRssToLinkMargin(int8_t aNoiseFloor, int8_t aRss)
uint8_t LinkQualityInfo::ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin)
{
return CalculateLinkQuality(aLinkMargin, kNoLastLinkQualityValue);
return CalculateLinkQuality(aLinkMargin, kNoLinkQuality);
}
uint8_t LinkQualityInfo::ConvertRssToLinkQuality(int8_t aNoiseFloor, int8_t aRss)
@@ -229,26 +204,26 @@ uint8_t LinkQualityInfo::CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLast
uint8_t threshold1, threshold2, threshold3;
uint8_t linkQuality = 0;
threshold1 = kLinkMarginThresholdForLinkQuality1;
threshold2 = kLinkMarginThresholdForLinkQuality2;
threshold3 = kLinkMarginThresholdForLinkQuality3;
threshold1 = kThreshold1;
threshold2 = kThreshold2;
threshold3 = kThreshold3;
// Apply the hysteresis threshold based on the last link quality value.
switch (aLastLinkQuality)
{
case 0:
threshold1 += kLinkMarginHysteresisThreshold;
threshold1 += kHysteresisThreshold;
// Intentional fall-through to next case.
case 1:
threshold2 += kLinkMarginHysteresisThreshold;
threshold2 += kHysteresisThreshold;
// Intentional fall-through to next case.
case 2:
threshold3 += kLinkMarginHysteresisThreshold;
threshold3 += kHysteresisThreshold;
// Intentional fall-through to next case.
+134 -49
View File
@@ -34,8 +34,6 @@
#ifndef LINK_QUALITY_HPP_
#define LINK_QUALITY_HPP_
#include <stdio.h>
#include <openthread/platform/radio.h>
#include <openthread/types.h>
@@ -50,23 +48,132 @@ namespace ot {
* @{
*/
/**
* This class implements a Received Signal Strength (RSS) averager.
*
* The average is maintained using an adaptive exponentially weighted moving filter.
*
*/
class RssAverager
{
friend class LinkQualityInfo;
public:
enum
{
kStringSize = 10, ///< Max chars needed for a string representation of average (@sa ToString()).
};
/**
* This method reset the averager and clears the average value.
*
*/
void Reset(void);
/**
* This method indicates whether the averager contains an average (i.e., at least one RSS value has been added).
*
* @retval true If the average value is available (at least one RSS value has been added).
* @retval false Averager is empty (no RSS value added yet).
*
*/
bool HasAverage(void) const { return (mCount != 0); }
/**
* This method adds a received signal strength (RSS) value to the average.
*
* If @p aRss is OT_RADIO_RSSI_INVALID, it is ignored and error status OT_ERROR_INVALID_ARGS is returned.
* The value of RSS is capped at 0dBm (i.e., for any given RSS value higher than 0dBm, 0dBm is used instead).
*
* @param[in] aRss Received signal strength value (in dBm) to be added to the average.
*
* @retval OT_ERROR_NONE New RSS value added to average successfully.
* @retval OT_ERROR_INVALID_ARGS Value of @p aRss is OT_RADIO_RSSI_INVALID.
*
*/
otError Add(int8_t aRss);
/**
* This method returns the current average signal strength value maintained by the averager.
*
* @returns The current average value (in dBm) or OT_RADIO_RSSI_INVALID if no average is available.
*
*/
int8_t GetAverage(void) const;
/**
* This method returns an raw/encoded version of current average signal strength value. The raw value is the
* average multiplied by a precision factor (currently set as -8).
*
* @returns The current average multiplied by precision factor or zero if no average is available.
*
*/
uint16_t GetRaw(void) const { return mAverage; }
/**
* This method converts the current average RSS value to a human-readable string (e.g., "-80.375"). If the
* average is unknown, empty string is returned.
*
* @param[out] aBuf A pointer to the char buffer.
* @param[in] aSize The maximum size of the buffer.
*
* @returns A pointer to the char string buffer.
*
*/
const char *ToString(char *aBuf, uint16_t aSize) const;
private:
/*
* The RssAverager uses an adaptive exponentially weighted filter to maintain the average. It keeps track of
* current average and the number of added RSS values (up to a 8).
*
* For the first 8 added RSS values, the average is the arithmetic mean of the added values (e.g., if 5 values are
* added, the average is sum of the 5 added RSS values divided by 5. After the 8th RSS value, a weighted filter is
* used with coefficients (1/8, 7/8), i.e., newAverage = 1/8 * newRss + 7/8 * oldAverage.
*
* To add to accuracy of the averaging process, the RSS values and the maintained average are multiplied by a
* precision factor of -8.
*
*/
enum
{
kPrecisionBitShift = 3, // Precision multiple for RSS average (1 << PrecisionBitShift).
kPrecision = (1 << kPrecisionBitShift),
kPrecisionBitMask = (kPrecision - 1),
kCoeffBitShift = 3, // Coefficient used for exponentially weighted filter (1 << kCoeffBitShift).
};
// Member variables fit into two bytes.
uint16_t mAverage : 11; // The raw average signal strength value (stored as RSS times precision multiple).
uint8_t mCount : 3; // Number of RSS values added to averager so far (limited to 2^kCoeffBitShift-1).
uint8_t mLinkQuality : 2; // Used by friend class LinkQualityInfo to store LinkQuality (0-3) value.
};
/**
* 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.
* strength (RSS), last RSS, link margin, and link quality.
*
*/
*/
class LinkQualityInfo
{
public:
enum
{
kInfoStringSize = 50, ///< Max chars needed for the info string representation (@sa ToInfoString())
};
/**
* This constructor initializes an instance of the LinkQualityInfo class.
* This constructor initializes the object.
*
*/
LinkQualityInfo(void);
/**
* This method clears the all the data in this instance.
* This method clears the all the data in the object.
*
*/
void Clear(void);
@@ -86,7 +193,7 @@ public:
* @returns The current average value or @c OT_RADIO_RSSI_INVALID if no average is available.
*
*/
int8_t GetAverageRss(void) const;
int8_t GetAverageRss(void) const { return mRssAverager.GetAverage(); }
/**
* This method returns an encoded version of current average signal strength value. The encoded value is the
@@ -95,20 +202,18 @@ public:
* @returns The current average multiplied by precision factor or zero if no average is available.
*
*/
uint16_t GetAverageRssAsEncodedWord(void) const;
uint16_t GetAverageRssRaw(void) const { return mRssAverager.GetRaw(); }
/**
* 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.
* This method converts the link quality info to NULL-terminated info/debug human-readable string.
*
* @param[out] aCharBuffer A char buffer to store the string corresponding to current average value.
* @param[in] aBufferLen The char buffer length.
* @param[out] aBuf A pointer to the string buffer.
* @param[in] aSize The maximum size of the string buffer.
*
* @retval OT_ERROR_NONE Successfully formed the string in the given char buffer.
* @retval OT_ERROR_NO_BUFS The string representation of the average value could not fit in the given buffer.
* @returns A pointer to the char string buffer.
*
*/
otError GetAverageRssAsString(char *aCharBuffer, size_t aBufferLen) const;
const char *ToInfoString(char *aBuf, uint16_t aSize) const;
/**
* This method returns the link margin. The link margin is calculated using the link's current average received
@@ -119,7 +224,7 @@ public:
* @returns Link margin derived from average received signal strength and average noise floor.
*
*/
uint8_t GetLinkMargin(int8_t aNoiseFloor) const;
uint8_t GetLinkMargin(int8_t aNoiseFloor) const { return ConvertRssToLinkMargin(aNoiseFloor, GetAverageRss()); }
/**
* Returns the current one-way link quality value. The link quality value is a number 0-3.
@@ -135,8 +240,9 @@ public:
* @param[in] aNoiseFloor The noise floor value (in dBm).
*
* @returns The current link quality value (value 0-3 as per Thread specification).
*
*/
uint8_t GetLinkQuality(int8_t aNoiseFloor);
uint8_t GetLinkQuality(void) const { return mRssAverager.mLinkQuality; }
/**
* Returns the most recent RSS value.
@@ -144,7 +250,7 @@ public:
* @returns The most recent RSS
*
*/
int8_t GetLastRss(void) const;
int8_t GetLastRss(void) const { return mLastRss; }
/**
* This method converts a received signal strength value to a link margin value.
@@ -183,46 +289,25 @@ private:
{
// 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.
kThreshold3 = 20, // Link margin threshold for quality 3 link.
kThreshold2 = 10, // Link margin threshold for quality 2 link.
kThreshold1 = 2, // Link margin threshold for quality 1 link.
kHysteresisThreshold = 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.
kNoLinkQuality = 0xff, // Used to indicate that there is no previous/last link quality.
};
/* 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(int8_t aNoiseFloor);
void SetLinkQuality(uint8_t aLinkQuality) { mRssAverager.mLinkQuality = aLinkQuality; }
/* 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.
* quality, the constant kNoLinkQuality 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).
int8_t mLastRss;
RssAverager mRssAverager;
int8_t mLastRss;
};
/**
+12 -3
View File
@@ -1946,6 +1946,7 @@ void MeshForwarder::HandleFragment(uint8_t *aFrame, uint8_t aFrameLength,
error = OT_ERROR_NO_BUFS);
message->SetLinkSecurityEnabled(aMessageInfo.mLinkSecurity);
message->SetPanId(aMessageInfo.mPanId);
message->AddRss(aMessageInfo.mRss);
headerLength = netif.GetLowpan().Decompress(*message, aMacSource, aMacDest, aFrame, aFrameLength,
datagramLength);
VerifyOrExit(headerLength > 0, error = OT_ERROR_PARSE);
@@ -2019,6 +2020,7 @@ void MeshForwarder::HandleFragment(uint8_t *aFrame, uint8_t aFrameLength,
// copy Fragment
message->Write(message->GetOffset(), aFrameLength, aFrame);
message->MoveOffset(aFrameLength);
message->AddRss(aMessageInfo.mRss);
}
exit:
@@ -2125,6 +2127,7 @@ void MeshForwarder::HandleLowpanHC(uint8_t *aFrame, uint8_t aFrameLength,
error = OT_ERROR_NO_BUFS);
message->SetLinkSecurityEnabled(aMessageInfo.mLinkSecurity);
message->SetPanId(aMessageInfo.mPanId);
message->AddRss(aMessageInfo.mRss);
headerLength = netif.GetLowpan().Decompress(*message, aMacSource, aMacDest, aFrame, aFrameLength, 0);
VerifyOrExit(headerLength > 0, error = OT_ERROR_PARSE);
@@ -2233,10 +2236,12 @@ void MeshForwarder::LogIp6Message(MessageAction aAction, const Message &aMessage
uint16_t checksum = 0;
Ip6::Header ip6Header;
Ip6::IpProto protocol;
char stringBuffer[Ip6::Address::kIp6AddressStringSize];
const char *actionText;
const char *priorityText;
bool shouldLogRss = false;
bool shouldLogSrcDstAddresses = true;
char stringBuffer[Ip6::Address::kIp6AddressStringSize];
char rssString[RssAverager::kStringSize];
VerifyOrExit(aMessage.GetType() == Message::kTypeIp6);
@@ -2279,6 +2284,7 @@ void MeshForwarder::LogIp6Message(MessageAction aAction, const Message &aMessage
{
case kMessageReceive:
actionText = "Received";
shouldLogRss = true;
break;
case kMessageTransmit:
@@ -2300,6 +2306,7 @@ void MeshForwarder::LogIp6Message(MessageAction aAction, const Message &aMessage
case kMessageDrop:
actionText = "Dropping";
shouldLogRss = true;
break;
default:
@@ -2332,7 +2339,7 @@ void MeshForwarder::LogIp6Message(MessageAction aAction, const Message &aMessage
otLogInfoMac(
GetInstance(),
"%s IPv6 %s msg, len:%d, chksum:%04x%s%s, sec:%s%s%s, prio:%s",
"%s IPv6 %s msg, len:%d, chksum:%04x%s%s, sec:%s%s%s, prio:%s%s%s",
actionText,
Ip6::Ip6::IpProtoToString(protocol),
aMessage.GetLength(),
@@ -2342,7 +2349,9 @@ void MeshForwarder::LogIp6Message(MessageAction aAction, const Message &aMessage
aMessage.IsLinkSecurityEnabled() ? "yes" : "no",
(aError == OT_ERROR_NONE) ? "" : ", error:",
(aError == OT_ERROR_NONE) ? "" : otThreadErrorToString(aError),
priorityText
priorityText,
shouldLogRss ? "" : ", rss:",
shouldLogRss ? "" : aMessage.GetRssAverager().ToString(rssString, sizeof(rssString))
);
if (shouldLogSrcDstAddresses)
+1 -1
View File
@@ -2515,7 +2515,7 @@ bool Mle::IsBetterParent(uint16_t aRloc16, uint8_t aLinkQuality, ConnectivityTlv
{
bool rval = false;
uint8_t candidateLinkQualityIn = mParentCandidate.GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor());
uint8_t candidateLinkQualityIn = mParentCandidate.GetLinkInfo().GetLinkQuality();
uint8_t candidateTwoWayLinkQuality = (candidateLinkQualityIn < mParentCandidate.GetLinkQualityOut())
? candidateLinkQualityIn : mParentCandidate.GetLinkQualityOut();
+11 -13
View File
@@ -1123,7 +1123,7 @@ uint8_t MleRouter::GetLinkCost(uint8_t aRouterId)
// NULL aRouterId indicates non-existing next hop, hence return kMaxRouteCost for it.
VerifyOrExit(aRouterId != mRouterId && router != NULL && router->GetState() == Neighbor::kStateValid);
rval = router->GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor());
rval = router->GetLinkInfo().GetLinkQuality();
if (rval > router->GetLinkQualityOut())
{
@@ -1637,7 +1637,7 @@ void MleRouter::UpdateRoutes(const RouteTlv &aRoute, uint8_t aRouterId)
GetRloc16(i),
GetRloc16(mRouters[i].GetNextHop()),
mRouters[i].GetCost(),
GetLinkCost(i), mRouters[i].GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor()),
GetLinkCost(i), mRouters[i].GetLinkInfo().GetLinkQuality(),
mRouters[i].GetLinkQualityOut());
}
@@ -3624,7 +3624,7 @@ otError MleRouter::GetChildInfo(Child &aChild, otChildInfo &aChildInfo)
aChildInfo.mChildId = GetChildId(aChild.GetRloc16());
aChildInfo.mNetworkDataVersion = aChild.GetNetworkDataVersion();
aChildInfo.mAge = TimerMilli::MsecToSec(TimerMilli::GetNow() - aChild.GetLastHeard());
aChildInfo.mLinkQualityIn = aChild.GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor());
aChildInfo.mLinkQualityIn = aChild.GetLinkInfo().GetLinkQuality();
aChildInfo.mAverageRssi = aChild.GetLinkInfo().GetAverageRss();
aChildInfo.mLastRssi = aChild.GetLinkInfo().GetLastRss();
@@ -3663,7 +3663,7 @@ otError MleRouter::GetRouterInfo(uint16_t aRouterId, otRouterInfo &aRouterInfo)
aRouterInfo.mNextHop = router->GetNextHop();
aRouterInfo.mLinkEstablished = router->GetState() == Neighbor::kStateValid;
aRouterInfo.mPathCost = router->GetCost();
aRouterInfo.mLinkQualityIn = router->GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor());
aRouterInfo.mLinkQualityIn = router->GetLinkInfo().GetLinkQuality();
aRouterInfo.mLinkQualityOut = router->GetLinkQualityOut();
aRouterInfo.mAge = static_cast<uint8_t>(TimerMilli::MsecToSec(TimerMilli::GetNow() -
router->GetLastHeard()));
@@ -3725,7 +3725,7 @@ exit:
aNeighInfo.mRloc16 = neighbor->GetRloc16();
aNeighInfo.mLinkFrameCounter = neighbor->GetLinkFrameCounter();
aNeighInfo.mMleFrameCounter = neighbor->GetMleFrameCounter();
aNeighInfo.mLinkQualityIn = neighbor->GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor());
aNeighInfo.mLinkQualityIn = neighbor->GetLinkInfo().GetLinkQuality();
aNeighInfo.mAverageRssi = neighbor->GetLinkInfo().GetAverageRss();
aNeighInfo.mLastRssi = neighbor->GetLinkInfo().GetLastRss();
aNeighInfo.mRxOnWhenIdle = neighbor->IsRxOnWhenIdle();
@@ -4235,7 +4235,6 @@ void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv)
uint8_t linkQuality;
uint8_t numChildren = 0;
int8_t parentPriority = kParentPriorityMedium;
int8_t noiseFloor = GetNetif().GetMac().GetNoiseFloor();
if (mParentPriority != kParentPriorityUnspecified)
{
@@ -4278,7 +4277,7 @@ void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv)
break;
case OT_DEVICE_ROLE_CHILD:
switch (mParent.GetLinkInfo().GetLinkQuality(noiseFloor))
switch (mParent.GetLinkInfo().GetLinkQuality())
{
case 1:
tlv.SetLinkQuality1(tlv.GetLinkQuality1() + 1);
@@ -4293,7 +4292,7 @@ void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv)
break;
}
cost += LinkQualityToCost(mParent.GetLinkInfo().GetLinkQuality(noiseFloor));
cost += LinkQualityToCost(mParent.GetLinkInfo().GetLinkQuality());
break;
case OT_DEVICE_ROLE_ROUTER:
@@ -4325,7 +4324,7 @@ void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv)
continue;
}
linkQuality = mRouters[i].GetLinkInfo().GetLinkQuality(noiseFloor);
linkQuality = mRouters[i].GetLinkInfo().GetLinkQuality();
if (linkQuality > mRouters[i].GetLinkQualityOut())
{
@@ -4470,8 +4469,7 @@ void MleRouter::FillRouteTlv(RouteTlv &tlv)
}
else
{
tlv.SetLinkQualityIn(routeCount,
mRouters[i].GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor()));
tlv.SetLinkQualityIn(routeCount, mRouters[i].GetLinkInfo().GetLinkQuality());
}
}
@@ -4533,7 +4531,7 @@ bool MleRouter::HasOneNeighborwithComparableConnectivity(const RouteTlv &aRoute,
continue;
}
localLinkQuality = mRouters[i].GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor());
localLinkQuality = mRouters[i].GetLinkInfo().GetLinkQuality();
if (localLinkQuality > mRouters[i].GetLinkQualityOut())
{
@@ -4662,7 +4660,7 @@ uint8_t MleRouter::GetMinDowngradeNeighborRouters(void)
continue;
}
linkQuality = mRouters[i].GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor());
linkQuality = mRouters[i].GetLinkInfo().GetLinkQuality();
if (linkQuality > mRouters[i].GetLinkQualityOut())
{
+137 -81
View File
@@ -31,6 +31,7 @@
#include "thread/link_quality.hpp"
#include "utils/wrap_string.h"
#include "test_platform.h"
#include "test_util.h"
namespace ot {
@@ -45,9 +46,9 @@ enum
kRssAverageMaxDiff = 16,
kNumRssAdds = 300,
kEncodedAverageBitShift = 3,
kEncodedAverageMultiple = (1 << kEncodedAverageBitShift),
kEncodedAverageBitMask = (1 << kEncodedAverageBitShift) - 1,
kRawAverageBitShift = 3,
kRawAverageMultiple = (1 << kRawAverageBitShift),
kRawAverageBitMask = (1 << kRawAverageBitShift) - 1,
};
#define MIN_RSS(_rss1, _rss2) (((_rss1) < (_rss2)) ? (_rss1) : (_rss2))
@@ -64,41 +65,34 @@ struct RssTestData
int8_t sNoiseFloor = -100; // dBm
// Checks the encoded average RSS value to match the value from GetAverageRss().
void VerifyEncodedRssValue(LinkQualityInfo &aLinkInfo)
// Check and verify the raw average RSS value to match the value from GetAverage().
void VerifyRawRssValue(int8_t aAverage, uint16_t aRawValue)
{
int8_t rss = aLinkInfo.GetAverageRss();
uint16_t encodedRss = aLinkInfo.GetAverageRssAsEncodedWord();
if (rss != OT_RADIO_RSSI_INVALID)
if (aAverage != OT_RADIO_RSSI_INVALID)
{
VerifyOrQuit(rss == -static_cast<int16_t>((encodedRss + (kEncodedAverageMultiple / 2)) >> kEncodedAverageBitShift),
"TestLinkQualityInfo failed - Ecoded RSS does not match the value from GetAverageRss().");
VerifyOrQuit(aAverage == -static_cast<int16_t>((aRawValue + (kRawAverageMultiple / 2)) >> kRawAverageBitShift),
"TestLinkQualityInfo failed - Raw value does not match the average.");
}
else
{
VerifyOrQuit(encodedRss == 0,
"TestLinkQualityInfo failed - Ecoded RSS does not match the value from GetAverageRss().");
VerifyOrQuit(aRawValue == 0, "TestLinkQualityInfo failed - Raw value does not match the average.");
}
}
// 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];
char stringBuf[LinkQualityInfo::kInfoStringSize];
SuccessOrQuit(aLinkInfo.GetAverageRssAsString(stringBuf, sizeof(stringBuf)),
"TestLinkQualityInfo failed - GetAverageRssAsString() failed.");
VerifyOrQuit(aLinkInfo.ToInfoString(stringBuf, sizeof(stringBuf)) != NULL, "ToInfoString() returned NULL");
printf("AveRss = %-4d, \"%-14s\", ", aLinkInfo.GetAverageRss(), stringBuf);
printf("LinkMargin = %-4d, LinkQuality = %d", aLinkInfo.GetLinkMargin(sNoiseFloor),
aLinkInfo.GetLinkQuality(sNoiseFloor));
printf("%s", stringBuf);
// This test-case succeeded.
printf(" -> PASS\n");
}
void TestLinkQualityData(RssTestData anRssData)
void TestLinkQualityData(RssTestData aRssData)
{
LinkQualityInfo linkInfo;
int8_t rss, ave, min, max;
@@ -108,66 +102,101 @@ void TestLinkQualityData(RssTestData anRssData)
min = kMinRssValue;
max = kMaxRssValue;
for (i = 0; i < anRssData.mRssListSize; i++)
for (i = 0; i < aRssData.mRssListSize; i++)
{
rss = anRssData.mRssList[i];
rss = aRssData.mRssList[i];
min = MIN_RSS(rss, min);
max = MAX_RSS(rss, max);
linkInfo.AddRss(sNoiseFloor, 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);
VerifyOrQuit(ave >= min, "TestLinkQualityInfo failed - GetAverageRss() is smaller than min value.");
VerifyOrQuit(ave <= max, "TestLinkQualityInfo failed - GetAverageRss() is larger than min value");
VerifyRawRssValue(linkInfo.GetAverageRss(), linkInfo.GetAverageRssRaw());
printf("%02u) AddRss(%4d): ", (unsigned int)i, rss);
PrintOutcome(linkInfo);
}
VerifyOrQuit(linkInfo.GetLinkQuality(sNoiseFloor) == anRssData.mExpectedLinkQuality,
VerifyOrQuit(linkInfo.GetLinkQuality() == aRssData.mExpectedLinkQuality,
"TestLinkQualityInfo failed - GetLinkQuality() is incorrect");
}
// Check and verify the raw average RSS value to match the value from GetAverage().
void VerifyRawRssValue(RssAverager &aRssAverager)
{
int8_t average = aRssAverager.GetAverage();
uint16_t rawValue = aRssAverager.GetRaw();
if (average != OT_RADIO_RSSI_INVALID)
{
VerifyOrQuit(average == -static_cast<int16_t>((rawValue + (kRawAverageMultiple / 2)) >> kRawAverageBitShift),
"TestLinkQualityInfo failed - Raw value does not match the average.");
}
else
{
VerifyOrQuit(rawValue == 0, "TestLinkQualityInfo failed - Raw value does not match the average.");
}
}
// This function prints the values in the passed in link info instance. It is invoked as the final step in test-case.
void PrintOutcome(RssAverager &aRssAverager)
{
char stringBuf[RssAverager::kStringSize];
VerifyOrQuit(aRssAverager.ToString(stringBuf, sizeof(stringBuf)) != NULL, "ToString() returned NULL");
printf("%s", stringBuf);
// This test-case succeeded.
printf(" -> PASS\n");
}
int8_t GetRandomRss(void)
{
uint32_t value;
value = otPlatRandomGet() % 128;
return static_cast<int8_t>(-value);
}
void TestRssAveraging(void)
{
LinkQualityInfo linkInfo;
RssAverager rssAverager;
int8_t rss, rss2, ave;
int16_t diff;
size_t i, j, k;
const int8_t rssValues[] = { kMinRssValue, -70, -40, -41, -10, kMaxRssValue};
int16_t sum;
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Values after initialization.
// Values after initialization/reset.
printf("\nAfter Initialization: ");
VerifyOrQuit(linkInfo.GetAverageRss() == OT_RADIO_RSSI_INVALID,
"TestLinkQualityInfo failed - Inital value from GetAverageRss() is incorrect.");
VerifyOrQuit(linkInfo.GetLinkMargin(sNoiseFloor) == 0,
"TestLinkQualityInfo failed - Inital value for link margin is incorrect.");
VerifyEncodedRssValue(linkInfo);
PrintOutcome(linkInfo);
rssAverager.Reset();
printf("\nAfter Reset: ");
VerifyOrQuit(rssAverager.GetAverage() == OT_RADIO_RSSI_INVALID,
"TestLinkQualityInfo failed - Initial value from GetAverage() is incorrect.");
VerifyRawRssValue(rssAverager);
PrintOutcome(rssAverager);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Adding a single value
rss = -70;
printf("AddRss(%d): ", rss);
linkInfo.AddRss(sNoiseFloor, rss);
VerifyOrQuit(linkInfo.GetAverageRss() == rss,
"TestLinkQualityInfo - GetAverageRss() failed after a single AddRss().");
VerifyEncodedRssValue(linkInfo);
PrintOutcome(linkInfo);
rssAverager.Add(rss);
VerifyOrQuit(rssAverager.GetAverage() == rss,
"TestLinkQualityInfo - GetAverage() failed after a single AddRss().");
VerifyRawRssValue(rssAverager);
PrintOutcome(rssAverager);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Clear
// Reset
printf("Clear(): ");
linkInfo.Clear();
VerifyOrQuit(linkInfo.GetAverageRss() == OT_RADIO_RSSI_INVALID,
"TestLinkQualityInfo failed - GetAverageRss() after Clear() is incorrect.");
VerifyOrQuit(linkInfo.GetLinkMargin(sNoiseFloor) == 0,
"TestLinkQualityInfo failed - link margin value after Clear() is incorrect.");
VerifyEncodedRssValue(linkInfo);
PrintOutcome(linkInfo);
printf("Reset(): ");
rssAverager.Reset();
VerifyOrQuit(rssAverager.GetAverage() == OT_RADIO_RSSI_INVALID,
"TestLinkQualityInfo failed - GetAverage() after Reset() is incorrect.");
VerifyRawRssValue(rssAverager);
PrintOutcome(rssAverager);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Adding the same value many times.
@@ -176,19 +205,19 @@ void TestRssAveraging(void)
for (j = 0; j < sizeof(rssValues); j++)
{
linkInfo.Clear();
rssAverager.Reset();
rss = rssValues[j];
printf("AddRss(%4d) %d times: ", rss, kNumRssAdds);
for (i = 0; i < kNumRssAdds; i++)
{
linkInfo.AddRss(sNoiseFloor, rss);
VerifyOrQuit(linkInfo.GetAverageRss() == rss,
"TestLinkQualityInfo failed - GetAverageRss() returned incorrect value.");
VerifyEncodedRssValue(linkInfo);
rssAverager.Add(rss);
VerifyOrQuit(rssAverager.GetAverage() == rss,
"TestLinkQualityInfo failed - GetAverage() returned incorrect value.");
VerifyRawRssValue(rssAverager);
}
PrintOutcome(linkInfo);
PrintOutcome(rssAverager);
}
@@ -209,14 +238,14 @@ void TestRssAveraging(void)
}
rss2 = rssValues[k];
linkInfo.Clear();
linkInfo.AddRss(sNoiseFloor, rss);
linkInfo.AddRss(sNoiseFloor, rss2);
rssAverager.Reset();
rssAverager.Add(rss);
rssAverager.Add(rss2);
printf("AddRss(%4d), AddRss(%4d): ", rss, rss2);
VerifyOrQuit(linkInfo.GetAverageRss() == ((rss + rss2) >> 1),
"TestLinkQualityInfo failed - GetAverageRss() returned incorrect value.");
VerifyEncodedRssValue(linkInfo);
PrintOutcome(linkInfo);
VerifyOrQuit(rssAverager.GetAverage() == ((rss + rss2) >> 1),
"TestLinkQualityInfo failed - GetAverage() returned incorrect value.");
VerifyRawRssValue(rssAverager);
PrintOutcome(rssAverager);
}
}
@@ -237,22 +266,22 @@ void TestRssAveraging(void)
}
rss2 = rssValues[k];
linkInfo.Clear();
rssAverager.Reset();
for (i = 0; i < kNumRssAdds; i++)
{
linkInfo.AddRss(sNoiseFloor, rss);
rssAverager.Add(rss);
}
linkInfo.AddRss(sNoiseFloor, rss2);
rssAverager.Add(rss2);
printf("AddRss(%4d) %d times, AddRss(%4d): ", rss, kNumRssAdds, rss2);
ave = linkInfo.GetAverageRss();
ave = rssAverager.GetAverage();
VerifyOrQuit(ave >= MIN_RSS(rss, rss2),
"TestLinkQualityInfo failed - GetAverageRss() returned incorrect value.");
"TestLinkQualityInfo failed - GetAverage() returned incorrect value.");
VerifyOrQuit(ave <= MAX_RSS(rss, rss2),
"TestLinkQualityInfo failed - GetAverageRss() returned incorrect value.");
VerifyEncodedRssValue(linkInfo);
PrintOutcome(linkInfo);
"TestLinkQualityInfo failed - GetAverage() returned incorrect value.");
VerifyRawRssValue(rssAverager);
PrintOutcome(rssAverager);
}
}
@@ -273,26 +302,53 @@ void TestRssAveraging(void)
}
rss2 = rssValues[k];
linkInfo.Clear();
rssAverager.Reset();
for (i = 0; i < kNumRssAdds; i++)
{
linkInfo.AddRss(sNoiseFloor, rss);
linkInfo.AddRss(sNoiseFloor, rss2);
ave = linkInfo.GetAverageRss();
rssAverager.Add(rss);
rssAverager.Add(rss2);
ave = rssAverager.GetAverage();
VerifyOrQuit(ave >= MIN_RSS(rss, rss2),
"TestLinkQualityInfo failed - GetAverageRss() is smaller than min value.");
"TestLinkQualityInfo failed - GetAverage() is smaller than min value.");
VerifyOrQuit(ave <= MAX_RSS(rss, rss2),
"TestLinkQualityInfo failed - GetAverageRss() is larger than min value.");
"TestLinkQualityInfo failed - GetAverage() is larger than min value.");
diff = ave;
diff -= (rss + rss2) >> 1;
VerifyOrQuit(ABS(diff) <= kRssAverageMaxDiff,
"TestLinkQualityInfo failed - GetAverageRss() is incorrect");
VerifyEncodedRssValue(linkInfo);
"TestLinkQualityInfo failed - GetAverage() is incorrect");
VerifyRawRssValue(rssAverager);
}
printf("[AddRss(%4d), AddRss(%4d)] %d times: ", rss, rss2, kNumRssAdds);
PrintOutcome(linkInfo);
PrintOutcome(rssAverager);
}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// For the first 8 values the average should be the arithmetic mean.
printf("- - - - - - - - - - - - - - - - - -\n");
for (i = 0; i < 1000; i++)
{
double mean;
rssAverager.Reset();
sum = 0;
printf("\n");
for (j = 1; j <= 8; j++)
{
rss = GetRandomRss();
rssAverager.Add(rss);
sum += rss;
mean = static_cast<double>(sum) / j;
VerifyOrQuit(ABS(rssAverager.GetAverage() - mean) < 1, "Average does not match the arithmetic mean!");
VerifyRawRssValue(rssAverager);
printf("AddRss(%4d) sum=%-5d, mean=%-8.2f RssAverager=", rss, sum, mean);
PrintOutcome(rssAverager);
}
}
}