1 /* 2 * Copyright (c) 2016, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @file 31 * This file includes definitions for storing and processing link quality information. 32 */ 33 34 #ifndef LINK_QUALITY_HPP_ 35 #define LINK_QUALITY_HPP_ 36 37 #include "openthread-core-config.h" 38 39 #include <openthread/platform/radio.h> 40 41 #include "common/clearable.hpp" 42 #include "common/locator.hpp" 43 #include "common/string.hpp" 44 #include "thread/mle_types.hpp" 45 46 namespace ot { 47 48 /** 49 * @addtogroup core-link-quality 50 * 51 * @brief 52 * This module includes definitions for Thread link quality metrics. 53 * 54 * @{ 55 */ 56 57 /** 58 * Implements an operation Success Rate Tracker. 59 * 60 * This can be used to track different link quality related metrics, e.g., CCA failure rate, frame tx success rate). 61 * The success rate is maintained using an exponential moving IIR averaging filter with a `uint16_t` as the storage. 62 */ 63 class SuccessRateTracker : public Clearable<SuccessRateTracker> 64 { 65 public: 66 static constexpr uint16_t kMaxRateValue = 0xffff; ///< Value corresponding to max (failure/success) rate of 100%. 67 68 /** 69 * Adds a sample (success or failure) to `SuccessRateTracker`. 70 * 71 * @param[in] aSuccess The sample status be added, `true` for success, `false` for failure. 72 * @param[in] aWeight The weight coefficient used for adding the new sample into average. 73 */ 74 void AddSample(bool aSuccess, uint16_t aWeight = kDefaultWeight); 75 76 /** 77 * Returns the average failure rate. 78 * 79 * @retval the average failure rate `[0-kMaxRateValue]` with `kMaxRateValue` corresponding to 100%. 80 */ GetFailureRate(void) const81 uint16_t GetFailureRate(void) const { return mFailureRate; } 82 83 /** 84 * Returns the average success rate. 85 * 86 * @retval the average success rate as [0-kMaxRateValue] with `kMaxRateValue` corresponding to 100%. 87 */ GetSuccessRate(void) const88 uint16_t GetSuccessRate(void) const { return kMaxRateValue - mFailureRate; } 89 90 private: 91 static constexpr uint16_t kDefaultWeight = 64; 92 93 uint16_t mFailureRate; 94 }; 95 96 /** 97 * Implements a Received Signal Strength (RSS) averager. 98 * 99 * The average is maintained using an adaptive exponentially weighted moving filter. 100 */ 101 class RssAverager : public Clearable<RssAverager> 102 { 103 public: 104 static constexpr uint16_t kStringSize = 10; ///< Max string size for average (@sa ToString()). 105 106 /** 107 * Defines the fixed-length `String` object returned from `ToString()`. 108 */ 109 typedef String<kStringSize> InfoString; 110 111 /** 112 * Indicates whether the averager contains an average (i.e., at least one RSS value has been added). 113 * 114 * @retval true If the average value is available (at least one RSS value has been added). 115 * @retval false Averager is empty (no RSS value added yet). 116 */ HasAverage(void) const117 bool HasAverage(void) const { return (mCount != 0); } 118 119 /** 120 * Adds a received signal strength (RSS) value to the average. 121 * 122 * If @p aRss is `Radio::kInvalidRssi`, it is ignored and error status kErrorInvalidArgs is returned. 123 * The value of RSS is capped at 0dBm (i.e., for any given RSS value higher than 0dBm, 0dBm is used instead). 124 * 125 * @param[in] aRss Received signal strength value (in dBm) to be added to the average. 126 * 127 * @retval kErrorNone New RSS value added to average successfully. 128 * @retval kErrorInvalidArgs Value of @p aRss is `Radio::kInvalidRssi`. 129 */ 130 Error Add(int8_t aRss); 131 132 /** 133 * Returns the current average signal strength value maintained by the averager. 134 * 135 * @returns The current average value (in dBm) or `Radio::kInvalidRssi` if no average is available. 136 */ 137 int8_t GetAverage(void) const; 138 139 /** 140 * Returns an raw/encoded version of current average signal strength value. The raw value is the 141 * average multiplied by a precision factor (currently set as -8). 142 * 143 * @returns The current average multiplied by precision factor or zero if no average is available. 144 */ GetRaw(void) const145 uint16_t GetRaw(void) const { return mAverage; } 146 147 /** 148 * Converts the current average RSS value to a human-readable string (e.g., "-80.375"). If the 149 * average is unknown, empty string is returned. 150 * 151 * @returns An `InfoString` object containing the string representation of average RSS. 152 */ 153 InfoString ToString(void) const; 154 155 private: 156 /* 157 * The RssAverager uses an adaptive exponentially weighted filter to maintain the average. It keeps track of 158 * current average and the number of added RSS values (up to a 8). 159 * 160 * For the first 8 added RSS values, the average is the arithmetic mean of the added values (e.g., if 5 values are 161 * added, the average is sum of the 5 added RSS values divided by 5. After the 8th RSS value, a weighted filter is 162 * used with coefficients (1/8, 7/8), i.e., newAverage = 1/8 * newRss + 7/8 * oldAverage. 163 * 164 * To add to accuracy of the averaging process, the RSS values and the maintained average are multiplied by a 165 * precision factor of -8. 166 */ 167 static constexpr uint8_t kPrecisionBitShift = 3; // Precision multiple for RSS average (1 << PrecisionBitShift). 168 static constexpr uint8_t kPrecision = (1 << kPrecisionBitShift); 169 static constexpr uint8_t kPrecisionBitMask = (kPrecision - 1); 170 static constexpr uint8_t kCoeffBitShift = 3; // Coeff for exp weighted filter (1 << kCoeffBitShift). 171 172 // Member variables fit into two bytes. 173 174 uint16_t mAverage : 11; // The raw average signal strength value (stored as RSS times precision multiple). 175 uint16_t mCount : 5; // Number of RSS values added to averager so far (limited to 2^kCoeffBitShift-1). 176 }; 177 178 /** 179 * Implements a Link Quality Indicator (LQI) averager. 180 * 181 * It maintains the exponential moving average value of LQI. 182 */ 183 class LqiAverager : public Clearable<LqiAverager> 184 { 185 public: 186 /** 187 * Adds a link quality indicator (LQI) value to the average. 188 * 189 * @param[in] aLqi Link Quality Indicator value to be added to the average. 190 */ 191 void Add(uint8_t aLqi); 192 193 /** 194 * Returns the current average link quality value maintained by the averager. 195 * 196 * @returns The current average value. 197 */ GetAverage(void) const198 uint8_t GetAverage(void) const { return mAverage; } 199 200 /** 201 * Returns the count of frames calculated so far. 202 * 203 * @returns The count of frames calculated. 204 */ GetCount(void) const205 uint8_t GetCount(void) const { return mCount; } 206 207 private: 208 static constexpr uint8_t kCoeffBitShift = 3; // Coeff used for exp weighted filter (1 << kCoeffBitShift). 209 210 uint8_t mAverage; // The average link quality indicator value. 211 uint8_t mCount; // Number of LQI values added to averager so far. 212 }; 213 214 /** 215 * Represents the link quality constants. 216 * 217 * Link Quality is an integer in [0, 3]. A higher link quality indicates a more usable link, with 0 indicating that the 218 * link is non-existent or unusable. 219 */ 220 enum LinkQuality : uint8_t 221 { 222 kLinkQuality0 = 0, ///< Link quality 0 (non-existent link) 223 kLinkQuality1 = 1, ///< Link quality 1 224 kLinkQuality2 = 2, ///< Link quality 2 225 kLinkQuality3 = 3, ///< Link quality 3 226 }; 227 228 constexpr uint8_t kCostForLinkQuality0 = Mle::kMaxRouteCost; ///< Link Cost for Link Quality 0. 229 constexpr uint8_t kCostForLinkQuality1 = 4; ///< Link Cost for Link Quality 1. 230 constexpr uint8_t kCostForLinkQuality2 = 2; ///< Link Cost for Link Quality 2. 231 constexpr uint8_t kCostForLinkQuality3 = 1; ///< Link Cost for Link Quality 3. 232 233 /** 234 * Converts link quality to route cost. 235 * 236 * @param[in] aLinkQuality The link quality to convert. 237 * 238 * @returns The route cost corresponding to @p aLinkQuality. 239 */ 240 uint8_t CostForLinkQuality(LinkQuality aLinkQuality); 241 242 /** 243 * Computes the link margin from a given noise floor and received signal strength. 244 * 245 * @param[in] aNoiseFloor The noise floor value (in dBm). 246 * @param[in] aRss The received signal strength value (in dBm). 247 * 248 * @returns The link margin value in dB. 249 */ 250 uint8_t ComputeLinkMargin(int8_t aNoiseFloor, int8_t aRss); 251 252 /** 253 * Converts a link margin value to a link quality value. 254 * 255 * @param[in] aLinkMargin The Link Margin in dB. 256 * 257 * @returns The link quality value (0-3). 258 */ 259 LinkQuality LinkQualityForLinkMargin(uint8_t aLinkMargin); 260 261 /** 262 * Gets the typical received signal strength value for a given link quality. 263 * 264 * @param[in] aNoiseFloor The noise floor value (in dBm). 265 * @param[in] aLinkQuality The link quality value in [0, 3]. 266 * 267 * @returns The typical platform RSSI in dBm. 268 */ 269 int8_t GetTypicalRssForLinkQuality(int8_t aNoiseFloor, LinkQuality aLinkQuality); 270 271 /** 272 * Encapsulates/stores all relevant information about quality of a link, including average received signal 273 * strength (RSS), last RSS, link margin, and link quality. 274 */ 275 class LinkQualityInfo : public InstanceLocatorInit 276 { 277 friend LinkQuality LinkQualityForLinkMargin(uint8_t aLinkMargin); 278 friend int8_t GetTypicalRssForLinkQuality(int8_t aNoiseFloor, LinkQuality aLinkQuality); 279 280 public: 281 static constexpr uint16_t kInfoStringSize = 50; ///< `InfoString` size (@sa ToInfoString()). 282 283 /** 284 * Defines the fixed-length `String` object returned from `ToInfoString()`. 285 */ 286 typedef String<kInfoStringSize> InfoString; 287 288 /** 289 * Initializes the `LinkQualityInfo` object. 290 * 291 * @param[in] aInstance A reference to the OpenThread instance. 292 */ Init(Instance & aInstance)293 void Init(Instance &aInstance) { InstanceLocatorInit::Init(aInstance); } 294 295 /** 296 * Clears the all the data in the object. 297 */ 298 void Clear(void); 299 300 /** 301 * Clears the average RSS value. 302 */ ClearAverageRss(void)303 void ClearAverageRss(void) { mRssAverager.Clear(); } 304 305 /** 306 * Adds a new received signal strength (RSS) value to the average. 307 * 308 * @param[in] aRss A new received signal strength value (in dBm) to be added to the average. 309 */ 310 void AddRss(int8_t aRss); 311 312 /** 313 * Returns the current average received signal strength value. 314 * 315 * @returns The current average value or `Radio::kInvalidRssi` if no average is available. 316 */ GetAverageRss(void) const317 int8_t GetAverageRss(void) const { return mRssAverager.GetAverage(); } 318 319 /** 320 * Returns an encoded version of current average signal strength value. The encoded value is the 321 * average multiplied by a precision factor (currently -8). 322 * 323 * @returns The current average multiplied by precision factor or zero if no average is available. 324 */ GetAverageRssRaw(void) const325 uint16_t GetAverageRssRaw(void) const { return mRssAverager.GetRaw(); } 326 327 /** 328 * Converts the link quality info to info/debug human-readable string. 329 * 330 * @returns An `InfoString` representing the link quality info. 331 */ 332 InfoString ToInfoString(void) const; 333 334 /** 335 * Returns the link margin. The link margin is calculated using the link's current average received 336 * signal strength (RSS) and average noise floor. 337 * 338 * @returns Link margin derived from average received signal strength and average noise floor. 339 */ 340 uint8_t GetLinkMargin(void) const; 341 342 /** 343 * Returns the current one-way link quality value. The link quality value is a number 0-3. 344 * 345 * The link quality is calculated by comparing the current link margin with a set of thresholds (per Thread spec). 346 * More specifically, link margin > 20 dB gives link quality 3, link margin > 10 dB gives link quality 2, 347 * link margin > 2 dB gives link quality 1, and link margin below or equal to 2 dB yields link quality of 0. 348 * 349 * In order to ensure that a link margin near the boundary of two different link quality values does not cause 350 * frequent changes, a hysteresis of 2 dB is applied when determining the link quality. For example, the average 351 * link margin must be at least 12 dB to change a quality 1 link to a quality 2 link. 352 * 353 * @returns The current link quality value (value 0-3 as per Thread specification). 354 */ GetLinkQualityIn(void) const355 LinkQuality GetLinkQualityIn(void) const { return static_cast<LinkQuality>(mLinkQualityIn); } 356 357 /** 358 * Returns the most recent RSS value. 359 * 360 * @returns The most recent RSS 361 */ GetLastRss(void) const362 int8_t GetLastRss(void) const { return mLastRss; } 363 364 /** 365 * Adds a MAC frame transmission status (success/failure) and updates the frame tx error rate. 366 * 367 * @param[in] aTxStatus Success/Failure of MAC frame transmission (`true` -> success, `false` -> failure). 368 */ AddFrameTxStatus(bool aTxStatus)369 void AddFrameTxStatus(bool aTxStatus) 370 { 371 mFrameErrorRate.AddSample(aTxStatus, OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW); 372 } 373 374 /** 375 * Adds a message transmission status (success/failure) and updates the message error rate. 376 * 377 * @param[in] aTxStatus Success/Failure of message (`true` -> success, `false` -> message tx failed). 378 * A larger (IPv6) message may be fragmented and sent as multiple MAC frames. The message 379 * transmission is considered a failure, if any of its fragments fail after all MAC retry 380 * attempts. 381 */ AddMessageTxStatus(bool aTxStatus)382 void AddMessageTxStatus(bool aTxStatus) 383 { 384 mMessageErrorRate.AddSample(aTxStatus, OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW); 385 } 386 387 /** 388 * Returns the MAC frame transmission error rate for the link. 389 * 390 * The rate is maintained over a window of (roughly) last `OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW` 391 * frame transmissions. 392 * 393 * @returns The error rate with maximum value `0xffff` corresponding to 100% failure rate. 394 */ GetFrameErrorRate(void) const395 uint16_t GetFrameErrorRate(void) const { return mFrameErrorRate.GetFailureRate(); } 396 397 /** 398 * Returns the message error rate for the link. 399 * 400 * The rate is maintained over a window of (roughly) last `OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW` 401 * (IPv6) messages. 402 * 403 * Note that a larger (IPv6) message can be fragmented and sent as multiple MAC frames. The message transmission is 404 * considered a failure, if any of its fragments fail after all MAC retry attempts. 405 * 406 * @returns The error rate with maximum value `0xffff` corresponding to 100% failure rate. 407 */ GetMessageErrorRate(void) const408 uint16_t GetMessageErrorRate(void) const { return mMessageErrorRate.GetFailureRate(); } 409 410 /** 411 * Gets the link quality out value. 412 * 413 * This indicates the Link Quality from the perspective of the neighbor. 414 * 415 * @returns The link quality out value. 416 */ GetLinkQualityOut(void) const417 LinkQuality GetLinkQualityOut(void) const { return static_cast<LinkQuality>(mLinkQualityOut); } 418 419 /** 420 * Sets the link quality out value. 421 * 422 * @param[in] aLinkQuality The link quality out value. 423 */ SetLinkQualityOut(LinkQuality aLinkQuality)424 void SetLinkQualityOut(LinkQuality aLinkQuality) { mLinkQualityOut = aLinkQuality; } 425 426 private: 427 // Constants for obtaining link quality from link margin: 428 429 static constexpr uint8_t kThreshold3 = 20; // Link margin threshold for quality 3 link. 430 static constexpr uint8_t kThreshold2 = 10; // Link margin threshold for quality 2 link. 431 static constexpr uint8_t kThreshold1 = 2; // Link margin threshold for quality 1 link. 432 static constexpr uint8_t kHysteresisThreshold = 2; // Link margin hysteresis threshold. 433 434 static constexpr int8_t kLinkQuality3LinkMargin = 50; // link margin for Link Quality 3 (21 - 255) 435 static constexpr int8_t kLinkQuality2LinkMargin = 15; // link margin for Link Quality 3 (21 - 255) 436 static constexpr int8_t kLinkQuality1LinkMargin = 5; // link margin for Link Quality 3 (21 - 255) 437 static constexpr int8_t kLinkQuality0LinkMargin = 0; // link margin for Link Quality 3 (21 - 255) 438 439 static constexpr uint8_t kNoLinkQuality = 0xff; // Indicate that there is no previous/last link quality. 440 SetLinkQualityIn(LinkQuality aLinkQuality)441 void SetLinkQualityIn(LinkQuality aLinkQuality) { mLinkQualityIn = aLinkQuality; } 442 443 static LinkQuality CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality); 444 445 RssAverager mRssAverager; 446 uint8_t mLinkQualityIn : 2; 447 uint8_t mLinkQualityOut : 2; 448 int8_t mLastRss; 449 SuccessRateTracker mFrameErrorRate; 450 SuccessRateTracker mMessageErrorRate; 451 }; 452 453 /** 454 * @} 455 */ 456 457 } // namespace ot 458 459 #endif // LINK_QUALITY_HPP_ 460