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 #include "test_platform.h"
30 #include "test_util.h"
31
32 #include "common/array.hpp"
33 #include "common/code_utils.hpp"
34 #include "thread/link_quality.hpp"
35
36 namespace ot {
37
38 static ot::Instance *sInstance;
39
40 enum
41 {
42 kMaxRssValue = 0,
43 kMinRssValue = -128,
44
45 kStringBuffferSize = 80,
46
47 kRssAverageMaxDiff = 16,
48 kNumRssAdds = 300,
49
50 kRawAverageBitShift = 3,
51 kRawAverageMultiple = (1 << kRawAverageBitShift),
52 kRawAverageBitMask = (1 << kRawAverageBitShift) - 1,
53 };
54
55 #define MIN_RSS(_rss1, _rss2) (((_rss1) < (_rss2)) ? (_rss1) : (_rss2))
56 #define MAX_RSS(_rss1, _rss2) (((_rss1) < (_rss2)) ? (_rss2) : (_rss1))
57 #define ABS(value) (((value) >= 0) ? (value) : -(value))
58
59 // This struct contains RSS values and test data for checking link quality info calss.
60 struct RssTestData
61 {
62 const int8_t *mRssList; // Array of RSS values.
63 size_t mRssListSize; // Size of RSS list.
64 uint8_t mExpectedLinkQuality; // Expected final link quality value.
65 };
66
67 int8_t sNoiseFloor = -100; // dBm
68
69 // Check and verify the raw average RSS value to match the value from GetAverage().
VerifyRawRssValue(int8_t aAverage,uint16_t aRawValue)70 void VerifyRawRssValue(int8_t aAverage, uint16_t aRawValue)
71 {
72 if (aAverage != OT_RADIO_RSSI_INVALID)
73 {
74 VerifyOrQuit(aAverage == -static_cast<int16_t>((aRawValue + (kRawAverageMultiple / 2)) >> kRawAverageBitShift),
75 "Raw value does not match the average.");
76 }
77 else
78 {
79 VerifyOrQuit(aRawValue == 0, "Raw value does not match the average.");
80 }
81 }
82
83 // This function prints the values in the passed in link info instance. It is invoked as the final step in test-case.
PrintOutcome(LinkQualityInfo & aLinkInfo)84 void PrintOutcome(LinkQualityInfo &aLinkInfo)
85 {
86 printf("%s -> PASS \n", aLinkInfo.ToInfoString().AsCString());
87 }
88
TestLinkQualityData(RssTestData aRssData)89 void TestLinkQualityData(RssTestData aRssData)
90 {
91 LinkQualityInfo linkInfo;
92 int8_t rss, ave, min, max;
93 size_t i;
94
95 sInstance = testInitInstance();
96 VerifyOrQuit(sInstance != nullptr);
97 linkInfo.Init(*sInstance);
98
99 printf("- - - - - - - - - - - - - - - - - -\n");
100 linkInfo.Clear();
101 min = kMinRssValue;
102 max = kMaxRssValue;
103
104 for (i = 0; i < aRssData.mRssListSize; i++)
105 {
106 rss = aRssData.mRssList[i];
107 min = MIN_RSS(rss, min);
108 max = MAX_RSS(rss, max);
109 linkInfo.AddRss(rss);
110 VerifyOrQuit(linkInfo.GetLastRss() == rss);
111 ave = linkInfo.GetAverageRss();
112 VerifyOrQuit(ave >= min, "GetAverageRss() is smaller than min value.");
113 VerifyOrQuit(ave <= max, "GetAverageRss() is larger than min value");
114 VerifyRawRssValue(linkInfo.GetAverageRss(), linkInfo.GetAverageRssRaw());
115 printf("%02u) AddRss(%4d): ", static_cast<unsigned int>(i), rss);
116 PrintOutcome(linkInfo);
117 }
118
119 VerifyOrQuit(linkInfo.GetLinkQuality() == aRssData.mExpectedLinkQuality);
120 }
121
122 // Check and verify the raw average RSS value to match the value from GetAverage().
VerifyRawRssValue(RssAverager & aRssAverager)123 void VerifyRawRssValue(RssAverager &aRssAverager)
124 {
125 int8_t average = aRssAverager.GetAverage();
126 uint16_t rawValue = aRssAverager.GetRaw();
127
128 if (average != OT_RADIO_RSSI_INVALID)
129 {
130 VerifyOrQuit(average == -static_cast<int16_t>((rawValue + (kRawAverageMultiple / 2)) >> kRawAverageBitShift),
131 "Raw value does not match the average.");
132 }
133 else
134 {
135 VerifyOrQuit(rawValue == 0, "Raw value does not match the average.");
136 }
137 }
138
139 // This function prints the values in the passed link info instance. It is invoked as the final step in test-case.
PrintOutcome(RssAverager & aRssAverager)140 void PrintOutcome(RssAverager &aRssAverager)
141 {
142 printf("%s -> PASS\n", aRssAverager.ToString().AsCString());
143 }
144
GetRandomRss(void)145 int8_t GetRandomRss(void)
146 {
147 uint32_t value;
148
149 value = rand() % 128;
150 return -static_cast<int8_t>(value);
151 }
152
TestRssAveraging(void)153 void TestRssAveraging(void)
154 {
155 RssAverager rssAverager;
156 int8_t rss, rss2, ave;
157 int16_t diff;
158 size_t i, j, k;
159 const int8_t rssValues[] = {kMinRssValue, -70, -40, -41, -10, kMaxRssValue};
160 int16_t sum;
161
162 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
163 // Values after initialization/reset.
164
165 rssAverager.Clear();
166
167 printf("\nAfter Clear: ");
168 VerifyOrQuit(rssAverager.GetAverage() == OT_RADIO_RSSI_INVALID, "Initial value from GetAverage() is incorrect.");
169 VerifyRawRssValue(rssAverager);
170 PrintOutcome(rssAverager);
171
172 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
173 // Adding a single value
174 rss = -70;
175 printf("AddRss(%d): ", rss);
176 IgnoreError(rssAverager.Add(rss));
177 VerifyOrQuit(rssAverager.GetAverage() == rss, "GetAverage() failed after a single AddRss().");
178 VerifyRawRssValue(rssAverager);
179 PrintOutcome(rssAverager);
180
181 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
182 // Clear
183
184 printf("Clear(): ");
185 rssAverager.Clear();
186 VerifyOrQuit(rssAverager.GetAverage() == OT_RADIO_RSSI_INVALID, "GetAverage() after Clear() is incorrect.");
187 VerifyRawRssValue(rssAverager);
188 PrintOutcome(rssAverager);
189
190 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
191 // Adding the same value many times.
192
193 printf("- - - - - - - - - - - - - - - - - -\n");
194
195 for (j = 0; j < sizeof(rssValues); j++)
196 {
197 rssAverager.Clear();
198 rss = rssValues[j];
199 printf("AddRss(%4d) %d times: ", rss, kNumRssAdds);
200
201 for (i = 0; i < kNumRssAdds; i++)
202 {
203 IgnoreError(rssAverager.Add(rss));
204 VerifyOrQuit(rssAverager.GetAverage() == rss, "GetAverage() returned incorrect value.");
205 VerifyRawRssValue(rssAverager);
206 }
207
208 PrintOutcome(rssAverager);
209 }
210
211 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
212 // Adding two RSS values:
213
214 printf("- - - - - - - - - - - - - - - - - -\n");
215
216 for (j = 0; j < sizeof(rssValues); j++)
217 {
218 rss = rssValues[j];
219
220 for (k = 0; k < sizeof(rssValues); k++)
221 {
222 if (k == j)
223 {
224 continue;
225 }
226
227 rss2 = rssValues[k];
228 rssAverager.Clear();
229 IgnoreError(rssAverager.Add(rss));
230 IgnoreError(rssAverager.Add(rss2));
231 printf("AddRss(%4d), AddRss(%4d): ", rss, rss2);
232 VerifyOrQuit(rssAverager.GetAverage() == ((rss + rss2) >> 1), "GetAverage() returned incorrect value.");
233 VerifyRawRssValue(rssAverager);
234 PrintOutcome(rssAverager);
235 }
236 }
237
238 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
239 // Adding one value many times and a different value once:
240
241 printf("- - - - - - - - - - - - - - - - - -\n");
242
243 for (j = 0; j < sizeof(rssValues); j++)
244 {
245 rss = rssValues[j];
246
247 for (k = 0; k < sizeof(rssValues); k++)
248 {
249 if (k == j)
250 {
251 continue;
252 }
253
254 rss2 = rssValues[k];
255 rssAverager.Clear();
256
257 for (i = 0; i < kNumRssAdds; i++)
258 {
259 IgnoreError(rssAverager.Add(rss));
260 }
261
262 IgnoreError(rssAverager.Add(rss2));
263 printf("AddRss(%4d) %d times, AddRss(%4d): ", rss, kNumRssAdds, rss2);
264 ave = rssAverager.GetAverage();
265 VerifyOrQuit(ave >= MIN_RSS(rss, rss2), "GetAverage() returned incorrect value.");
266 VerifyOrQuit(ave <= MAX_RSS(rss, rss2), "GetAverage() returned incorrect value.");
267 VerifyRawRssValue(rssAverager);
268 PrintOutcome(rssAverager);
269 }
270 }
271
272 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
273 // Adding two alternating values many times:
274
275 printf("- - - - - - - - - - - - - - - - - -\n");
276
277 for (j = 0; j < sizeof(rssValues); j++)
278 {
279 rss = rssValues[j];
280
281 for (k = 0; k < sizeof(rssValues); k++)
282 {
283 if (k == j)
284 {
285 continue;
286 }
287
288 rss2 = rssValues[k];
289 rssAverager.Clear();
290
291 for (i = 0; i < kNumRssAdds; i++)
292 {
293 IgnoreError(rssAverager.Add(rss));
294 IgnoreError(rssAverager.Add(rss2));
295 ave = rssAverager.GetAverage();
296 VerifyOrQuit(ave >= MIN_RSS(rss, rss2), "GetAverage() is smaller than min value.");
297 VerifyOrQuit(ave <= MAX_RSS(rss, rss2), "GetAverage() is larger than min value.");
298 diff = ave;
299 diff -= (rss + rss2) >> 1;
300 VerifyOrQuit(ABS(diff) <= kRssAverageMaxDiff, "GetAverage() is incorrect");
301 VerifyRawRssValue(rssAverager);
302 }
303
304 printf("[AddRss(%4d), AddRss(%4d)] %d times: ", rss, rss2, kNumRssAdds);
305 PrintOutcome(rssAverager);
306 }
307 }
308
309 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
310 // For the first 8 values the average should be the arithmetic mean.
311
312 printf("- - - - - - - - - - - - - - - - - -\n");
313
314 for (i = 0; i < 1000; i++)
315 {
316 double mean;
317
318 rssAverager.Clear();
319 sum = 0;
320
321 printf("\n");
322
323 for (j = 1; j <= 8; j++)
324 {
325 rss = GetRandomRss();
326 IgnoreError(rssAverager.Add(rss));
327 sum += rss;
328 mean = static_cast<double>(sum) / static_cast<double>(j);
329 VerifyOrQuit(ABS(rssAverager.GetAverage() - mean) < 1, "Average does not match the arithmetic mean!");
330 VerifyRawRssValue(rssAverager);
331 printf("AddRss(%4d) sum=%-5d, mean=%-8.2f RssAverager=", rss, sum, mean);
332 PrintOutcome(rssAverager);
333 }
334 }
335 }
336
TestLinkQualityCalculations(void)337 void TestLinkQualityCalculations(void)
338 {
339 const int8_t rssList1[] = {-81, -80, -79, -78, -76, -80, -77, -75, -77, -76, -77, -74};
340 const RssTestData rssData1 = {
341 rssList1, // mRssList
342 sizeof(rssList1), // mRssListSize
343 3 // mExpectedLinkQuality
344 };
345
346 const int8_t rssList2[] = {-90, -80, -85};
347 const RssTestData rssData2 = {
348 rssList2, // mRssList
349 sizeof(rssList2), // mRssListSize
350 2 // mExpectedLinkQuality
351 };
352
353 const int8_t rssList3[] = {-95, -96, -98, -99, -100, -100, -98, -99, -100, -100, -100, -100, -100};
354 const RssTestData rssData3 = {
355 rssList3, // mRssList
356 sizeof(rssList3), // mRssListSize
357 0 // mExpectedLinkQuality
358 };
359
360 const int8_t rssList4[] = {-75, -100, -100, -100, -100, -100, -95, -92, -93, -94, -93, -93};
361 const RssTestData rssData4 = {
362 rssList4, // mRssList
363 sizeof(rssList4), // mRssListSize
364 1 // mExpectedLinkQuality
365 };
366
367 TestLinkQualityData(rssData1);
368 TestLinkQualityData(rssData2);
369 TestLinkQualityData(rssData3);
370 TestLinkQualityData(rssData4);
371 }
372
TestSuccessRateTracker(void)373 void TestSuccessRateTracker(void)
374 {
375 SuccessRateTracker rateTracker;
376 uint16_t sampleCount;
377
378 const uint16_t kMaxSamples = 5000;
379
380 const uint16_t kMaxRate = SuccessRateTracker::kMaxRateValue;
381 const double kMaxError = 1.0; // Max permitted error in percentage
382 const uint16_t kWeightLimit[] = {64, 128, 256, 300, 512, 810, 900};
383
384 printf("\nTesting SuccessRateTracker\n");
385
386 rateTracker.Clear();
387
388 VerifyOrQuit(rateTracker.GetSuccessRate() == kMaxRate, "SuccessRateTracker: Initial value incorrect");
389 VerifyOrQuit(rateTracker.GetFailureRate() == 0, "SuccessRateTracker: Initial value incorrect");
390
391 // Adding all success
392 for (sampleCount = 1; sampleCount < kMaxSamples; sampleCount++)
393 {
394 rateTracker.AddSample(true, sampleCount);
395
396 VerifyOrQuit(rateTracker.GetSuccessRate() == kMaxRate, "SuccessRateTracker: incorrect rate all success case");
397 VerifyOrQuit(rateTracker.GetFailureRate() == 0, "SuccessRateTracker: incorrect rate in all success case");
398 }
399
400 rateTracker.Clear();
401 VerifyOrQuit(rateTracker.GetSuccessRate() == kMaxRate, "SuccessRateTracker: Rate incorrect after reset");
402 VerifyOrQuit(rateTracker.GetFailureRate() == 0, "SuccessRateTracker: Rate incorrect after reset");
403
404 // Adding all failures
405 for (sampleCount = 1; sampleCount < kMaxRate; sampleCount++)
406 {
407 rateTracker.AddSample(false, sampleCount);
408
409 VerifyOrQuit(rateTracker.GetSuccessRate() == 0, "SuccessRateTracker: rate incorrect all failure case");
410 VerifyOrQuit(rateTracker.GetFailureRate() == kMaxRate,
411 "SuccessRateTracker: rate incorrect in all failure case");
412 }
413
414 // Adding success/failure at different rates and checking the RateTracker rate for every sample
415
416 for (uint16_t testRound = 0; testRound < GetArrayLength(kWeightLimit) * 2; testRound++)
417 {
418 uint16_t weightLimit;
419 bool reverseLogic;
420 double maxDiff = 0;
421
422 // Reverse the logic (add success instead of failure) on even test rounds
423 reverseLogic = ((testRound % 2) == 0);
424
425 // Select a different weight limit based on the current test round
426 weightLimit = kWeightLimit[testRound / 2];
427
428 printf("TestRound %02d, weightLimit %3d, reverseLogic %d ", testRound, weightLimit, reverseLogic);
429
430 for (uint16_t period = 1; period < 101; period++)
431 {
432 uint16_t failureCount = 0;
433
434 rateTracker.Clear();
435
436 for (sampleCount = 1; sampleCount < kMaxSamples; sampleCount++)
437 {
438 double expectedRate;
439 double failureRate;
440 double diff;
441 bool isSuccess = ((sampleCount % period) == 0);
442 uint16_t weight;
443
444 if (reverseLogic)
445 {
446 isSuccess = !isSuccess;
447 }
448
449 weight = sampleCount;
450
451 if (weight > weightLimit)
452 {
453 weight = weightLimit;
454 }
455
456 rateTracker.AddSample(isSuccess, weight);
457
458 if (!isSuccess)
459 {
460 failureCount++;
461 }
462
463 // Calculate the failure rate from rateTracker and expected rate.
464
465 failureRate = static_cast<double>(rateTracker.GetFailureRate()) * 100.0 / kMaxRate; // in percent
466 expectedRate = static_cast<double>(failureCount) * 100.0 / sampleCount; // in percent
467
468 diff = failureRate - expectedRate;
469 diff = ABS(diff);
470
471 VerifyOrQuit(diff <= kMaxError, "SuccessRateTracker: rate does not match expected value");
472
473 if (diff > maxDiff)
474 {
475 maxDiff = diff;
476 }
477 }
478 }
479
480 printf(" MaxDiff = %.3f%%-> PASS\n", maxDiff);
481 }
482 }
483
484 } // namespace ot
485
main(void)486 int main(void)
487 {
488 ot::TestRssAveraging();
489 ot::TestLinkQualityCalculations();
490 ot::TestSuccessRateTracker();
491 printf("\nAll tests passed\n");
492 return 0;
493 }
494