diff --git a/include/openthread/message.h b/include/openthread/message.h index 9c66b9a12..473251ac4 100644 --- a/include/openthread/message.h +++ b/include/openthread/message.h @@ -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. * diff --git a/src/core/api/message_api.cpp b/src/core/api/message_api.cpp index fb016f118..46c472e8a 100644 --- a/src/core/api/message_api.cpp +++ b/src/core/api/message_api.cpp @@ -87,6 +87,12 @@ void otMessageSetDirectTransmission(otMessage *aMessage, bool aEnabled) } } +int8_t otMessageGetRss(otMessage *aMessage) +{ + Message *message = static_cast(aMessage); + return message->GetAverageRss(); +} + otError otMessageAppend(otMessage *aMessage, const void *aBuf, uint16_t aLength) { Message *message = static_cast(aMessage); diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp index bf68c4769..d17bce088 100644 --- a/src/core/api/thread_api.cpp +++ b/src/core/api/thread_api.cpp @@ -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(TimerMilli::MsecToSec(TimerMilli::GetNow() - parent->GetLastHeard())); diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 876e3e8ce..d30646bc4 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -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. * diff --git a/src/core/thread/link_quality.cpp b/src/core/thread/link_quality.cpp index 7bcdbc6ba..622d04e94 100644 --- a/src/core/thread/link_quality.cpp +++ b/src/core/thread/link_quality.cpp @@ -36,52 +36,34 @@ #include "link_quality.hpp" #include -#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(-aRss); - newValue <<= kRssAveragePrecisionMultipleBitShift; + newValue <<= kPrecisionBitShift; - oldAverage = mRssAverage; - - if (mCount >= kRssCountForWeightCoefficientOneEighth) - { - // New average = old average * 7/8 + new value * 1/8 - mRssAverage = static_cast(((oldAverage << 3) - oldAverage + newValue) >> 3); - } - else if (mCount >= kRssCountForWeightCoefficientOneFourth) - { - // New average = old average * 3/4 + new value * 1/4 - mRssAverage = static_cast(((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(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(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(((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(((oldAverage << kCoeffBitShift) - oldAverage + newValue) >> kCoeffBitShift); } - VerifyOrExit(charsWritten >= 0, error = OT_ERROR_NO_BUFS); - - VerifyOrExit(charsWritten < static_cast(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(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. diff --git a/src/core/thread/link_quality.hpp b/src/core/thread/link_quality.hpp index b01d0febb..0221d58b4 100644 --- a/src/core/thread/link_quality.hpp +++ b/src/core/thread/link_quality.hpp @@ -34,8 +34,6 @@ #ifndef LINK_QUALITY_HPP_ #define LINK_QUALITY_HPP_ -#include - #include #include @@ -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; }; /** diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 15b148b23..0c42a3a9b 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -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) diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index a3decd6b3..8f3964d04 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -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(); diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 2405ad073..685ad791c 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -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(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()) { diff --git a/tests/unit/test_link_quality.cpp b/tests/unit/test_link_quality.cpp index 9f61c3964..2c9d92b92 100644 --- a/tests/unit/test_link_quality.cpp +++ b/tests/unit/test_link_quality.cpp @@ -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((encodedRss + (kEncodedAverageMultiple / 2)) >> kEncodedAverageBitShift), - "TestLinkQualityInfo failed - Ecoded RSS does not match the value from GetAverageRss()."); + VerifyOrQuit(aAverage == -static_cast((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((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(-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(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); } } }