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 45 namespace ot { 46 47 /** 48 * @addtogroup core-link-quality 49 * 50 * @brief 51 * This module includes definitions for Thread link quality metrics. 52 * 53 * @{ 54 */ 55 56 /** 57 * This class implements an operation Success Rate Tracker. 58 * 59 * This can be used to track different link quality related metrics, e.g., CCA failure rate, frame tx success rate). 60 * The success rate is maintained using an exponential moving IIR averaging filter with a `uint16_t` as the storage. 61 * 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 * This method 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 */ 75 void AddSample(bool aSuccess, uint16_t aWeight = kDefaultWeight); 76 77 /** 78 * This method returns the average failure rate. 79 * 80 * @retval the average failure rate `[0-kMaxRateValue]` with `kMaxRateValue` corresponding to 100%. 81 * 82 */ GetFailureRate(void) const83 uint16_t GetFailureRate(void) const { return mFailureRate; } 84 85 /** 86 * This method returns the average success rate. 87 * 88 * @retval the average success rate as [0-kMaxRateValue] with `kMaxRateValue` corresponding to 100%. 89 * 90 */ GetSuccessRate(void) const91 uint16_t GetSuccessRate(void) const { return kMaxRateValue - mFailureRate; } 92 93 private: 94 static constexpr uint16_t kDefaultWeight = 64; 95 96 uint16_t mFailureRate; 97 }; 98 99 /** 100 * This class implements a Received Signal Strength (RSS) averager. 101 * 102 * The average is maintained using an adaptive exponentially weighted moving filter. 103 * 104 */ 105 class RssAverager : public Clearable<RssAverager> 106 { 107 public: 108 static constexpr uint16_t kStringSize = 10; ///< Max string size for average (@sa ToString()). 109 110 /** 111 * This type defines the fixed-length `String` object returned from `ToString()`. 112 * 113 */ 114 typedef String<kStringSize> InfoString; 115 116 /** 117 * This method indicates whether the averager contains an average (i.e., at least one RSS value has been added). 118 * 119 * @retval true If the average value is available (at least one RSS value has been added). 120 * @retval false Averager is empty (no RSS value added yet). 121 * 122 */ HasAverage(void) const123 bool HasAverage(void) const { return (mCount != 0); } 124 125 /** 126 * This method adds a received signal strength (RSS) value to the average. 127 * 128 * If @p aRss is OT_RADIO_RSSI_INVALID, it is ignored and error status kErrorInvalidArgs is returned. 129 * The value of RSS is capped at 0dBm (i.e., for any given RSS value higher than 0dBm, 0dBm is used instead). 130 * 131 * @param[in] aRss Received signal strength value (in dBm) to be added to the average. 132 * 133 * @retval kErrorNone New RSS value added to average successfully. 134 * @retval kErrorInvalidArgs Value of @p aRss is OT_RADIO_RSSI_INVALID. 135 * 136 */ 137 Error Add(int8_t aRss); 138 139 /** 140 * This method returns the current average signal strength value maintained by the averager. 141 * 142 * @returns The current average value (in dBm) or OT_RADIO_RSSI_INVALID if no average is available. 143 * 144 */ 145 int8_t GetAverage(void) const; 146 147 /** 148 * This method returns an raw/encoded version of current average signal strength value. The raw value is the 149 * average multiplied by a precision factor (currently set as -8). 150 * 151 * @returns The current average multiplied by precision factor or zero if no average is available. 152 * 153 */ GetRaw(void) const154 uint16_t GetRaw(void) const { return mAverage; } 155 156 /** 157 * This method converts the current average RSS value to a human-readable string (e.g., "-80.375"). If the 158 * average is unknown, empty string is returned. 159 * 160 * @returns An `InfoString` object containing the string representation of average RSS. 161 * 162 */ 163 InfoString ToString(void) const; 164 165 private: 166 /* 167 * The RssAverager uses an adaptive exponentially weighted filter to maintain the average. It keeps track of 168 * current average and the number of added RSS values (up to a 8). 169 * 170 * For the first 8 added RSS values, the average is the arithmetic mean of the added values (e.g., if 5 values are 171 * added, the average is sum of the 5 added RSS values divided by 5. After the 8th RSS value, a weighted filter is 172 * used with coefficients (1/8, 7/8), i.e., newAverage = 1/8 * newRss + 7/8 * oldAverage. 173 * 174 * To add to accuracy of the averaging process, the RSS values and the maintained average are multiplied by a 175 * precision factor of -8. 176 * 177 */ 178 static constexpr uint8_t kPrecisionBitShift = 3; // Precision multiple for RSS average (1 << PrecisionBitShift). 179 static constexpr uint8_t kPrecision = (1 << kPrecisionBitShift); 180 static constexpr uint8_t kPrecisionBitMask = (kPrecision - 1); 181 static constexpr uint8_t kCoeffBitShift = 3; // Coeff for exp weighted filter (1 << kCoeffBitShift). 182 183 // Member variables fit into two bytes. 184 185 uint16_t mAverage : 11; // The raw average signal strength value (stored as RSS times precision multiple). 186 uint16_t mCount : 5; // Number of RSS values added to averager so far (limited to 2^kCoeffBitShift-1). 187 }; 188 189 /** 190 * This class implements a Link Quality Indicator (LQI) averager. 191 * 192 * It maintains the exponential moving average value of LQI. 193 * 194 */ 195 class LqiAverager : public Clearable<LqiAverager> 196 { 197 public: 198 /** 199 * This method adds a link quality indicator (LQI) value to the average. 200 * 201 * @param[in] aLqi Link Quality Indicator value to be added to the average. 202 * 203 */ 204 void Add(uint8_t aLqi); 205 206 /** 207 * This method returns the current average link quality value maintained by the averager. 208 * 209 * @returns The current average value. 210 * 211 */ GetAverage(void) const212 uint8_t GetAverage(void) const { return mAverage; } 213 214 /** 215 * This method returns the count of frames calculated so far. 216 * 217 * @returns The count of frames calculated. 218 * 219 */ GetCount(void) const220 uint8_t GetCount(void) const { return mCount; } 221 222 private: 223 static constexpr uint8_t kCoeffBitShift = 3; // Coeff used for exp weighted filter (1 << kCoeffBitShift). 224 225 uint8_t mAverage; // The average link quality indicator value. 226 uint8_t mCount; // Number of LQI values added to averager so far. 227 }; 228 229 /** 230 * This enumeration represents the link quality constants. 231 * 232 * Link Quality is an integer in [0, 3]. A higher link quality indicates a more usable link, with 0 indicating that the 233 * link is non-existent or unusable. 234 * 235 */ 236 enum LinkQuality : uint8_t 237 { 238 kLinkQuality0 = 0, ///< Link quality 0 (non-existent link) 239 kLinkQuality1 = 1, ///< Link quality 1 240 kLinkQuality2 = 2, ///< Link quality 2 241 kLinkQuality3 = 3, ///< Link quality 3 242 }; 243 244 /** 245 * This class encapsulates/stores all relevant information about quality of a link, including average received signal 246 * strength (RSS), last RSS, link margin, and link quality. 247 * 248 */ 249 class LinkQualityInfo : public InstanceLocatorInit 250 { 251 public: 252 static constexpr uint16_t kInfoStringSize = 50; ///< `InfoString` size (@sa ToInfoString()). 253 254 /** 255 * This type defines the fixed-length `String` object returned from `ToInfoString()`. 256 * 257 */ 258 typedef String<kInfoStringSize> InfoString; 259 260 /** 261 * This method initializes the `LinkQualityInfo` object. 262 * 263 * @param[in] aInstance A reference to the OpenThread instance. 264 * 265 */ Init(Instance & aInstance)266 void Init(Instance &aInstance) { InstanceLocatorInit::Init(aInstance); } 267 268 /** 269 * This method clears the all the data in the object. 270 * 271 */ 272 void Clear(void); 273 274 /** 275 * This method adds a new received signal strength (RSS) value to the average. 276 * 277 * @param[in] aRss A new received signal strength value (in dBm) to be added to the average. 278 * 279 */ 280 void AddRss(int8_t aRss); 281 282 /** 283 * This method returns the current average received signal strength value. 284 * 285 * @returns The current average value or @c OT_RADIO_RSSI_INVALID if no average is available. 286 * 287 */ GetAverageRss(void) const288 int8_t GetAverageRss(void) const { return mRssAverager.GetAverage(); } 289 290 /** 291 * This method returns an encoded version of current average signal strength value. The encoded value is the 292 * average multiplied by a precision factor (currently -8). 293 * 294 * @returns The current average multiplied by precision factor or zero if no average is available. 295 * 296 */ GetAverageRssRaw(void) const297 uint16_t GetAverageRssRaw(void) const { return mRssAverager.GetRaw(); } 298 299 /** 300 * This method converts the link quality info to info/debug human-readable string. 301 * 302 * @returns An `InfoString` representing the link quality info. 303 * 304 */ 305 InfoString ToInfoString(void) const; 306 307 /** 308 * This method returns the link margin. The link margin is calculated using the link's current average received 309 * signal strength (RSS) and average noise floor. 310 * 311 * @returns Link margin derived from average received signal strength and average noise floor. 312 * 313 */ 314 uint8_t GetLinkMargin(void) const; 315 316 /** 317 * Returns the current one-way link quality value. The link quality value is a number 0-3. 318 * 319 * The link quality is calculated by comparing the current link margin with a set of thresholds (per Thread spec). 320 * More specifically, link margin > 20 dB gives link quality 3, link margin > 10 dB gives link quality 2, 321 * link margin > 2 dB gives link quality 1, and link margin below or equal to 2 dB yields link quality of 0. 322 * 323 * In order to ensure that a link margin near the boundary of two different link quality values does not cause 324 * frequent changes, a hysteresis of 2 dB is applied when determining the link quality. For example, the average 325 * link margin must be at least 12 dB to change a quality 1 link to a quality 2 link. 326 * 327 * @returns The current link quality value (value 0-3 as per Thread specification). 328 * 329 */ GetLinkQuality(void) const330 LinkQuality GetLinkQuality(void) const { return mLinkQuality; } 331 332 /** 333 * Returns the most recent RSS value. 334 * 335 * @returns The most recent RSS 336 * 337 */ GetLastRss(void) const338 int8_t GetLastRss(void) const { return mLastRss; } 339 340 /** 341 * This method adds a MAC frame transmission status (success/failure) and updates the frame tx error rate. 342 * 343 * @param[in] aTxStatus Success/Failure of MAC frame transmission (`true` -> success, `false` -> failure). 344 * 345 */ AddFrameTxStatus(bool aTxStatus)346 void AddFrameTxStatus(bool aTxStatus) 347 { 348 mFrameErrorRate.AddSample(aTxStatus, OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW); 349 } 350 351 /** 352 * This method adds a message transmission status (success/failure) and updates the message error rate. 353 * 354 * @param[in] aTxStatus Success/Failure of message (`true` -> success, `false` -> message tx failed). 355 * A larger (IPv6) message may be fragmented and sent as multiple MAC frames. The message 356 * transmission is considered a failure, if any of its fragments fail after all MAC retry 357 * attempts. 358 * 359 */ AddMessageTxStatus(bool aTxStatus)360 void AddMessageTxStatus(bool aTxStatus) 361 { 362 mMessageErrorRate.AddSample(aTxStatus, OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW); 363 } 364 365 /** 366 * This method returns the MAC frame transmission error rate for the link. 367 * 368 * The rate is maintained over a window of (roughly) last `OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW` 369 * frame transmissions. 370 * 371 * @returns The error rate with maximum value `0xffff` corresponding to 100% failure rate. 372 * 373 */ GetFrameErrorRate(void) const374 uint16_t GetFrameErrorRate(void) const { return mFrameErrorRate.GetFailureRate(); } 375 376 /** 377 * This method returns the message error rate for the link. 378 * 379 * The rate is maintained over a window of (roughly) last `OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW` 380 * (IPv6) messages. 381 * 382 * Note that a larger (IPv6) message can be fragmented and sent as multiple MAC frames. The message transmission is 383 * considered a failure, if any of its fragments fail after all MAC retry attempts. 384 * 385 * @returns The error rate with maximum value `0xffff` corresponding to 100% failure rate. 386 * 387 */ GetMessageErrorRate(void) const388 uint16_t GetMessageErrorRate(void) const { return mMessageErrorRate.GetFailureRate(); } 389 390 /** 391 * This method converts a received signal strength value to a link margin value. 392 * 393 * @param[in] aNoiseFloor The noise floor value (in dBm). 394 * @param[in] aRss The received signal strength value (in dBm). 395 * 396 * @returns The link margin value. 397 * 398 */ 399 static uint8_t ConvertRssToLinkMargin(int8_t aNoiseFloor, int8_t aRss); 400 401 /** 402 * This method converts a link margin value to a link quality value. 403 * 404 * @param[in] aLinkMargin The Link Margin in dB. 405 * 406 * @returns The link quality value (0-3). 407 * 408 */ 409 static LinkQuality ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin); 410 411 /** 412 * This method converts a received signal strength value to a link quality value. 413 * 414 * @param[in] aNoiseFloor The noise floor value (in dBm). 415 * @param[in] aRss The received signal strength value (in dBm). 416 * 417 * @returns The link quality value (0-3). 418 * 419 */ 420 static LinkQuality ConvertRssToLinkQuality(int8_t aNoiseFloor, int8_t aRss); 421 422 /** 423 * This method converts a link quality value to a typical received signal strength value. 424 * 425 * @note only for test. 426 * 427 * @param[in] aNoiseFloor The noise floor value (in dBm). 428 * @param[in] aLinkQuality The link quality value in [0, 3]. 429 * 430 * @returns The typical platform RSSI. 431 * 432 */ 433 static int8_t ConvertLinkQualityToRss(int8_t aNoiseFloor, LinkQuality aLinkQuality); 434 435 private: 436 // Constants for obtaining link quality from link margin: 437 438 static constexpr uint8_t kThreshold3 = 20; // Link margin threshold for quality 3 link. 439 static constexpr uint8_t kThreshold2 = 10; // Link margin threshold for quality 2 link. 440 static constexpr uint8_t kThreshold1 = 2; // Link margin threshold for quality 1 link. 441 static constexpr uint8_t kHysteresisThreshold = 2; // Link margin hysteresis threshold. 442 443 static constexpr int8_t kLinkQuality3LinkMargin = 50; // link margin for Link Quality 3 (21 - 255) 444 static constexpr int8_t kLinkQuality2LinkMargin = 15; // link margin for Link Quality 3 (21 - 255) 445 static constexpr int8_t kLinkQuality1LinkMargin = 5; // link margin for Link Quality 3 (21 - 255) 446 static constexpr int8_t kLinkQuality0LinkMargin = 0; // link margin for Link Quality 3 (21 - 255) 447 448 static constexpr uint8_t kNoLinkQuality = 0xff; // Indicate that there is no previous/last link quality. 449 SetLinkQuality(LinkQuality aLinkQuality)450 void SetLinkQuality(LinkQuality aLinkQuality) { mLinkQuality = aLinkQuality; } 451 452 static LinkQuality CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality); 453 454 RssAverager mRssAverager; 455 LinkQuality mLinkQuality; 456 int8_t mLastRss; 457 458 SuccessRateTracker mFrameErrorRate; 459 SuccessRateTracker mMessageErrorRate; 460 }; 461 462 /** 463 * @} 464 */ 465 466 } // namespace ot 467 468 #endif // LINK_QUALITY_HPP_ 469