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 implements link quality information processing and storage.
32 */
33
34 #include "link_quality.hpp"
35
36 #include <stdio.h>
37
38 #include "common/code_utils.hpp"
39 #include "common/instance.hpp"
40 #include "common/locator_getters.hpp"
41
42 namespace ot {
43
44 // This array gives the decimal point digits representing 0/8, 1/8, ..., 7/8 (does not include the '.').
45 static const char *const kDigitsString[8] = {
46 // 0/8, 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8
47 "0", "125", "25", "375", "5", "625", "75", "875"};
48
AddSample(bool aSuccess,uint16_t aWeight)49 void SuccessRateTracker::AddSample(bool aSuccess, uint16_t aWeight)
50 {
51 uint32_t oldAverage = mFailureRate;
52 uint32_t newValue = (aSuccess) ? 0 : kMaxRateValue;
53 uint32_t n = aWeight;
54
55 // `n/2` is added to the sum to ensure rounding the value to the nearest integer when dividing by `n`
56 // (e.g., 1.2 -> 1, 3.5 -> 4).
57
58 mFailureRate = static_cast<uint16_t>(((oldAverage * (n - 1)) + newValue + (n / 2)) / n);
59 }
60
Add(int8_t aRss)61 Error RssAverager::Add(int8_t aRss)
62 {
63 Error error = kErrorNone;
64 uint16_t newValue;
65
66 VerifyOrExit(aRss != OT_RADIO_RSSI_INVALID, error = kErrorInvalidArgs);
67
68 // Restrict the RSS value to the closed range [0, -128] so the RSS times precision multiple can fit in 11 bits.
69 if (aRss > 0)
70 {
71 aRss = 0;
72 }
73
74 // Multiply the RSS value by a precision multiple (currently -8).
75
76 newValue = static_cast<uint16_t>(-aRss);
77 newValue <<= kPrecisionBitShift;
78
79 mCount += (mCount < (1 << kCoeffBitShift));
80 // Maintain arithmetic mean.
81 // newAverage = newValue * (1/mCount) + oldAverage * ((mCount -1)/mCount)
82 mAverage = static_cast<uint16_t>(((mAverage * (mCount - 1)) + newValue) / mCount);
83
84 exit:
85 return error;
86 }
87
GetAverage(void) const88 int8_t RssAverager::GetAverage(void) const
89 {
90 int8_t average;
91
92 VerifyOrExit(mCount != 0, average = OT_RADIO_RSSI_INVALID);
93
94 average = -static_cast<int8_t>(mAverage >> kPrecisionBitShift);
95
96 // Check for possible round up (e.g., average of -71.5 --> -72)
97
98 if ((mAverage & kPrecisionBitMask) >= (kPrecision >> 1))
99 {
100 average--;
101 }
102
103 exit:
104 return average;
105 }
106
ToString(void) const107 RssAverager::InfoString RssAverager::ToString(void) const
108 {
109 InfoString string;
110
111 VerifyOrExit(mCount != 0);
112 string.Append("%d.%s", -(mAverage >> kPrecisionBitShift), kDigitsString[mAverage & kPrecisionBitMask]);
113
114 exit:
115 return string;
116 }
117
Add(uint8_t aLqi)118 void LqiAverager::Add(uint8_t aLqi)
119 {
120 uint8_t count;
121
122 if (mCount < UINT8_MAX)
123 {
124 mCount++;
125 }
126 count = OT_MIN((1 << kCoeffBitShift), mCount);
127
128 mAverage = static_cast<uint8_t>(((mAverage * (count - 1)) + aLqi) / count);
129 }
130
Clear(void)131 void LinkQualityInfo::Clear(void)
132 {
133 mRssAverager.Clear();
134 SetLinkQuality(0);
135 mLastRss = OT_RADIO_RSSI_INVALID;
136
137 mFrameErrorRate.Clear();
138 mMessageErrorRate.Clear();
139 }
140
AddRss(int8_t aRss)141 void LinkQualityInfo::AddRss(int8_t aRss)
142 {
143 uint8_t oldLinkQuality = kNoLinkQuality;
144
145 VerifyOrExit(aRss != OT_RADIO_RSSI_INVALID);
146
147 mLastRss = aRss;
148
149 if (mRssAverager.HasAverage())
150 {
151 oldLinkQuality = GetLinkQuality();
152 }
153
154 SuccessOrExit(mRssAverager.Add(aRss));
155
156 SetLinkQuality(CalculateLinkQuality(GetLinkMargin(), oldLinkQuality));
157
158 exit:
159 return;
160 }
161
GetLinkMargin(void) const162 uint8_t LinkQualityInfo::GetLinkMargin(void) const
163 {
164 return ConvertRssToLinkMargin(Get<Mac::SubMac>().GetNoiseFloor(), GetAverageRss());
165 }
166
ToInfoString(void) const167 LinkQualityInfo::InfoString LinkQualityInfo::ToInfoString(void) const
168 {
169 InfoString string;
170
171 string.Append("aveRss:%s, lastRss:%d, linkQuality:%d", mRssAverager.ToString().AsCString(), GetLastRss(),
172 GetLinkQuality());
173
174 return string;
175 }
176
ConvertRssToLinkMargin(int8_t aNoiseFloor,int8_t aRss)177 uint8_t LinkQualityInfo::ConvertRssToLinkMargin(int8_t aNoiseFloor, int8_t aRss)
178 {
179 int8_t linkMargin = aRss - aNoiseFloor;
180
181 if (linkMargin < 0 || aRss == OT_RADIO_RSSI_INVALID)
182 {
183 linkMargin = 0;
184 }
185
186 return static_cast<uint8_t>(linkMargin);
187 }
188
ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin)189 uint8_t LinkQualityInfo::ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin)
190 {
191 return CalculateLinkQuality(aLinkMargin, kNoLinkQuality);
192 }
193
ConvertRssToLinkQuality(int8_t aNoiseFloor,int8_t aRss)194 uint8_t LinkQualityInfo::ConvertRssToLinkQuality(int8_t aNoiseFloor, int8_t aRss)
195 {
196 return ConvertLinkMarginToLinkQuality(ConvertRssToLinkMargin(aNoiseFloor, aRss));
197 }
198
ConvertLinkQualityToRss(int8_t aNoiseFloor,uint8_t aLinkQuality)199 int8_t LinkQualityInfo::ConvertLinkQualityToRss(int8_t aNoiseFloor, uint8_t aLinkQuality)
200 {
201 int8_t linkmargin = 0;
202
203 switch (aLinkQuality)
204 {
205 case 3:
206 linkmargin = kLinkQuality3LinkMargin;
207 break;
208
209 case 2:
210 linkmargin = kLinkQuality2LinkMargin;
211 break;
212
213 case 1:
214 linkmargin = kLinkQuality1LinkMargin;
215 break;
216
217 default:
218 linkmargin = kLinkQuality0LinkMargin;
219 break;
220 }
221
222 return linkmargin + aNoiseFloor;
223 }
224
CalculateLinkQuality(uint8_t aLinkMargin,uint8_t aLastLinkQuality)225 uint8_t LinkQualityInfo::CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality)
226 {
227 // Static private method to calculate the link quality from a given
228 // link margin while taking into account the last link quality
229 // value and adding the hysteresis value to the thresholds. If
230 // there is no previous value for link quality, the constant
231 // kNoLinkQuality should be passed as the second argument.
232
233 uint8_t threshold1, threshold2, threshold3;
234 uint8_t linkQuality = 0;
235
236 threshold1 = kThreshold1;
237 threshold2 = kThreshold2;
238 threshold3 = kThreshold3;
239
240 // Apply the hysteresis threshold based on the last link quality value.
241
242 switch (aLastLinkQuality)
243 {
244 case 0:
245 threshold1 += kHysteresisThreshold;
246
247 OT_FALL_THROUGH;
248
249 case 1:
250 threshold2 += kHysteresisThreshold;
251
252 OT_FALL_THROUGH;
253
254 case 2:
255 threshold3 += kHysteresisThreshold;
256
257 OT_FALL_THROUGH;
258
259 default:
260 break;
261 }
262
263 if (aLinkMargin > threshold3)
264 {
265 linkQuality = 3;
266 }
267 else if (aLinkMargin > threshold2)
268 {
269 linkQuality = 2;
270 }
271 else if (aLinkMargin > threshold1)
272 {
273 linkQuality = 1;
274 }
275
276 return linkQuality;
277 }
278
279 } // namespace ot
280