• 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 #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