• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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