EMANE  1.2.1
neighbormetricmanager.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2014 - Adjacent Link LLC, Bridgewater, New Jersey
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
7  * are met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * * Neither the name of Adjacent Link LLC nor the names of its
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "emane/types.h"
35 #include "emane/statistictable.h"
36 
37 #include <cmath>
38 #include <map>
39 #include <mutex>
40 
41 
42 namespace {
43  const EMANE::StatisticTableLabels sNeighborMetricLables {"NEM",
44  "Rx Pkts",
45  "Tx Pkts",
46  "Missed Pkts",
47  "BW Util",
48  "Last Rx",
49  "Last Tx",
50  "SINR Avg",
51  "SINR Stdv",
52  "NF Avg",
53  "NF Stdv",
54  "Rx Rate Avg",
55  "Tx Rate Avg"};
56 
57  const EMANE::StatisticTableLabels sNeighborStatusLables {"NEM",
58  "Rx Pkts",
59  "Tx Pkts",
60  "Missed Pkts",
61  "BW Util Ratio",
62  "SINR Avg",
63  "NF Avg",
64  "Rx Age"};
65 }
66 
67 
68 class EMANE::NeighborMetricManager::Implementation
69  {
70  public:
71  Implementation(EMANE::NEMId nemId) :
72  nemId_{nemId},
73  lastNeighborStatusUpdateTime_{}
74  { }
75 
76  ~Implementation()
77  { }
78 
79 
80  void setNeighborDeleteTimeMicroseconds(const Microseconds & ageMicroseconds)
81  {
82  neighborDeleteAgeMicroseconds_ = ageMicroseconds;
83  }
84 
85 
86  void handleTxActivity(NEMId dst, std::uint64_t u64DataRatebps, const TimePoint & txTime)
87  {
88  // exclude tx broadcast from the r2ri reports
90  {
91  handleR2RITxActivity(dst, u64DataRatebps, txTime);
92  }
93 
94  handleNeighborTxActivity(dst, u64DataRatebps, txTime);
95  }
96 
97 
98  void handleRxActivity(NEMId src,
99  std::uint64_t u64SeqNum,
100  const uuid_t & uuid,
101  const EMANE::TimePoint & rxTime)
102  {
103  handleR2RIRxActivity(src, u64SeqNum, uuid, rxTime);
104 
105  updateNeighborRxActivity(src, u64SeqNum, uuid, rxTime);
106  }
107 
108 
109 
110  void handleRxActivity(NEMId src,
111  std::uint64_t u64SeqNum,
112  const uuid_t & uuid,
113  float fSINR,
114  float fNoiseFloor,
115  const EMANE::TimePoint & rxTime,
116  const EMANE::Microseconds & durationMicroseconds,
117  std::uint64_t u64DataRatebps)
118  {
119  handleR2RIRxActivity(src,
120  u64SeqNum,
121  uuid,
122  fSINR,
123  fNoiseFloor,
124  rxTime,
125  durationMicroseconds,
126  u64DataRatebps);
127 
128  updateNeighborRxActivity(src,
129  u64SeqNum,
130  uuid,
131  fSINR,
132  fNoiseFloor,
133  rxTime,
134  durationMicroseconds,
135  u64DataRatebps);
136  }
137 
138 
139 
140  void handleNeighborStatusUpdate()
141  {
142  EMANE::TimePoint currentTime{Clock::now()};
143 
144  StatisticTable<NEMId> * pTable = pStatisticNeighborStatusTable_;
145 
146  for(auto iter : neighborDataTable_)
147  {
148  NeighborData * pNeighborMetric = iter.second.first;
149 
150  float fBWUtilzationRatio = (lastNeighborStatusUpdateTime_ == EMANE::TimePoint{}) ? 0.0f :
151  std::chrono::duration_cast<EMANE::DoubleSeconds>(pNeighborMetric->rxUtilizationMicroseconds_).count() /
152  std::chrono::duration_cast<EMANE::DoubleSeconds>(currentTime - lastNeighborStatusUpdateTime_).count();
153 
154  float fRxAge = (! pNeighborMetric->bHaveEverHadRxActivity_) ? 0.0f :
155  std::chrono::duration_cast<DoubleSeconds>(
156  currentTime - pNeighborMetric->lastRxTime_).count();
157 
158  float fSINRAvg = getAvg(pNeighborMetric->fSINRSum_, pNeighborMetric->u64NumRxFrames_);
159 
160  float fNoiseFloorAvg = getAvg(pNeighborMetric->fNoiseFloorSum_, pNeighborMetric->u64NumRxFrames_);
161 
162  pTable->setCell(iter.first,
163  NBR_STATUS_TX_FRAMES,
164  Any{pNeighborMetric->u64NumTxFrames_});
165 
166  pTable->setCell(iter.first,
167  NBR_STATUS_RX_FRAMES,
168  Any{pNeighborMetric->u64NumRxFrames_});
169 
170  pTable->setCell(iter.first,
171  NBR_STATUS_MISSED_FRAMES,
172  Any{pNeighborMetric->u64NumRxMissedFrames_});
173 
174  pTable->setCell(iter.first,
175  NBR_STATUS_BW_CONSUMPTION,
176  Any{fBWUtilzationRatio});
177 
178  pTable->setCell(iter.first,
179  NBR_STATUS_SINR_AVG,
180  Any{fSINRAvg});
181 
182  pTable->setCell(iter.first,
183  NBR_STATUS_NOISE_FLOOR_AVG,
184  Any{fNoiseFloorAvg});
185 
186  pTable->setCell(iter.first,
187  NBR_STATUS_RX_AGE,
188  Any{fRxAge});
189 
190  clearData_i(pNeighborMetric);
191  }
192 
193  lastNeighborStatusUpdateTime_ = currentTime;
194  }
195 
196 
197 
199  {
200  EMANE::TimePoint currentTime{Clock::now()};
201 
202  EMANE::Controls::R2RINeighborMetrics neighborMetrics{};
203 
204  for(auto iter = r2riMetricTable_.begin(); iter != r2riMetricTable_.end(); /* bump/erase below */)
205  {
206  // get the age
207  const Microseconds ageMicroseconds{
208  std::chrono::duration_cast<Microseconds>(currentTime - iter->second->lastRxTime_)};
209 
210  // only the entries for the r2ri reporting are checked for age
211  if(ageMicroseconds > neighborDeleteAgeMicroseconds_)
212  {
213  delete (iter->second);
214 
215  // erase and bump
216  r2riMetricTable_.erase(iter++);
217  }
218  else
219  {
220  float fSINRAvg{}, fSINRStd{}, fNoiseFloorAvg{}, fNoiseFloorStdv{};
221 
222  // get sinr avg and standard deviation
223  getAvgAndStd(iter->second->fSINRSum_,
224  iter->second->fSINRSum2_,
225  iter->second->u64NumRxFrames_,
226  fSINRAvg,
227  fSINRStd);
228 
229  // get noise floor avg and standard deviation
230  getAvgAndStd(iter->second->fNoiseFloorSum_,
231  iter->second->fNoiseFloorSum2_,
232  iter->second->u64NumRxFrames_,
233  fNoiseFloorAvg,
234  fNoiseFloorStdv);
235 
237  neighborMetric{iter->first, // nbr id
238  iter->second->u64NumRxFrames_, // num rx frames (samples)
239  iter->second->u64NumTxFrames_, // num tx frames (samples)
240  iter->second->u64NumRxMissedFrames_, // num rx missed frames
241  iter->second->rxUtilizationMicroseconds_, // bandwidth consumption
242  fSINRAvg, // sinr avg
243  fSINRStd, // sinr std deviation
244  fNoiseFloorAvg, // noisefloor avg
245  fNoiseFloorStdv, // noise floor std deviation
246  iter->second->u64RxDataRateAvg_, // avg rx datarate
247  iter->second->u64TxDataRateAvg_}; // avg tx datarate
248 
249  neighborMetrics.push_back(neighborMetric);
250 
251  clearData_i(iter->second);
252 
253  // bump
254  ++iter;
255  }
256  }
257 
258  return neighborMetrics;
259  }
260 
261  void registerStatistics(StatisticRegistrar & statisticRegistrar)
262  {
263  pStatisticNeighborMetricTable_ =
264  statisticRegistrar.registerTable<NEMId>("NeighborMetricTable",
265  sNeighborMetricLables,
267  "Neighbor Metric Table");
268 
269  pStatisticNeighborStatusTable_ =
270  statisticRegistrar.registerTable<NEMId>("NeighborStatusTable",
271  sNeighborStatusLables,
273  "Neighbor Status Table");
274  }
275 
276 
277  private:
278  // keep this is line with the NeighborMetricLabels
279  enum NeighborMetricLables { NBR_METRIC_RX_FRAMES = 1, // num total rx frames
280  NBR_METRIC_TX_FRAMES = 2, // num total tx frames
281  NBR_METRIC_MISSED_FRAMES = 3, // num total missed frames
282  NBR_METRIC_BW_CONSUMPTION = 4, // total bw consumption
283  NBR_METRIC_LAST_RX_TIME = 5, // last rx time
284  NBR_METRIC_LAST_TX_TIME = 6, // last tx time
285  NBR_METRIC_SINR_AVG = 7, // avg sinr rx
286  NBR_METRIC_SINR_STDV = 8, // sinr std dev rx
287  NBR_METRIC_NOISE_FLOOR_AVG = 9, // noise floor avg rx
288  NBR_METRIC_NOISE_FLOOR_STDV = 10, // noise floor std dev rx
289  NBR_METRIC_RX_DATARATE_AVG = 11, // avg rx data rate
290  NBR_METRIC_TX_DATARATE_AVG = 12}; // avg tx data rate
291 
292  // keep this is line with the NeighborStatusLabels
293  enum NeighborStatusLables { NBR_STATUS_RX_FRAMES = 1, // num frames rx this interval
294  NBR_STATUS_TX_FRAMES = 2, // num frames tx this interval
295  NBR_STATUS_MISSED_FRAMES = 3, // num frames missed this interval
296  NBR_STATUS_BW_CONSUMPTION = 4, // bandwidth consumption ratio
297  NBR_STATUS_SINR_AVG = 5, // avg sinr this interval
298  NBR_STATUS_NOISE_FLOOR_AVG = 6, // avg noise floor this interval
299  NBR_STATUS_RX_AGE = 7}; // time elapsed since last heard from
300 
301 
302  // neighbor data used for r2ri and neigbor metric/status
303  struct NeighborData {
304  const NEMId nemId_;
305 
306  std::uint64_t u64LastRxSeqNum_;
307 
308  bool bHaveEverHadRxActivity_;
309 
310  EMANE::TimePoint lastRxTime_;
311  EMANE::TimePoint lastTxTime_;
312 
313  std::uint64_t u64NumRxFrames_;
314  std::uint64_t u64NumRxMissedFrames_;
315  std::uint64_t u64NumTxFrames_;
316 
317  EMANE::Microseconds rxUtilizationMicroseconds_;
318 
319  float fSINRSum_;
320  float fSINRSum2_;
321  float fNoiseFloorSum_;
322  float fNoiseFloorSum2_;
323 
324  std::uint64_t u64RxDataRateMin_;
325  std::uint64_t u64RxDataRateMax_;
326  std::uint64_t u64RxDataRateAvg_;
327 
328  std::uint64_t u64TxDataRateMin_;
329  std::uint64_t u64TxDataRateMax_;
330  std::uint64_t u64TxDataRateAvg_;
331 
332  uuid_t uuid_;
333 
334  NeighborData(NEMId nemId) :
335  nemId_{nemId},
336  u64LastRxSeqNum_{},
337  bHaveEverHadRxActivity_{},
338  lastRxTime_{},
339  lastTxTime_{},
340  u64NumRxFrames_{},
341  u64NumRxMissedFrames_{},
342  u64NumTxFrames_{},
343  rxUtilizationMicroseconds_{},
344  fSINRSum_{},
345  fSINRSum2_{},
346  fNoiseFloorSum_{},
347  fNoiseFloorSum2_{},
348  u64RxDataRateMin_{},
349  u64RxDataRateMax_{},
350  u64RxDataRateAvg_{},
351  u64TxDataRateMin_{},
352  u64TxDataRateMax_{},
353  u64TxDataRateAvg_{}
354  {
355  uuid_clear(uuid_);
356  }
357  };
358 
359 
360  using NeighborDataMap = std::map<EMANE::NEMId, NeighborData *>;
361 
362  // <short term, long term>
363  using NeighborDataPair = std::pair<NeighborData *, NeighborData *>;
364 
365  using NeighborDataPairMap = std::map<EMANE::NEMId, NeighborDataPair>;
366 
367  using Counter = std::uint64_t;
368 
369  EMANE::NEMId nemId_;
370 
371  // neighbor metric data used for r2ri reports, reset after each report
372  NeighborDataMap r2riMetricTable_;
373 
374  // neighbor data used for short and long term statistics
375  NeighborDataPairMap neighborDataTable_;
376 
377  Microseconds neighborDeleteAgeMicroseconds_;
378 
379  // short term table
380  StatisticTable<NEMId> * pStatisticNeighborStatusTable_;
381 
382  // long term table
383  StatisticTable<NEMId> * pStatisticNeighborMetricTable_;
384 
385 
386  EMANE::TimePoint lastNeighborStatusUpdateTime_;
387 
388 
389  NeighborData * lookupR2RIMetric(NEMId nemId)
390  {
391  auto iter = r2riMetricTable_.find(nemId);
392 
393  if(iter == r2riMetricTable_.end())
394  {
395  // add the report data for this nem
396  iter = r2riMetricTable_.insert(std::make_pair(nemId, new NeighborData{nemId})).first;
397  }
398 
399  return iter->second;
400  }
401 
402 
403  NeighborDataPair lookupNeighborData(NEMId nemId)
404  {
405  auto iter = neighborDataTable_.find(nemId);
406 
407  if(iter == neighborDataTable_.end())
408  {
409  // add the data for this nem
410  iter = neighborDataTable_.insert(std::make_pair(nemId,
411  NeighborDataPair{new NeighborData{nemId}, new NeighborData{nemId}})).first;
412 
413  // setup the NeighborMetrics Table
414  // first column is nemid
415  std::vector<Any> v1{Any{nemId}};
416 
417  // next 4 are counters
418  v1.insert(v1.end(), 4, Any{Counter{}});
419 
420  // the rest are floats
421  v1.insert(v1.end(), sNeighborMetricLables.size() - 5, Any{float{}});
422 
423  // add the row for this nem
424  pStatisticNeighborMetricTable_->addRow(nemId, v1);
425 
426 
427  // setup the NeighborStatus Table
428  // first column is nemid
429  std::vector<Any> v2{Any{nemId}};
430 
431  // next 3 are counters
432  v2.insert(v2.end(), 3, Any{Counter{}});
433 
434  // the rest are floats
435  v2.insert(v2.end(), sNeighborStatusLables.size() - 4, Any{float{}});
436 
437  // add the row for this nem
438  pStatisticNeighborStatusTable_->addRow(nemId, v2);
439  }
440 
441  return iter->second;
442  }
443 
444 
445  void handleR2RITxActivity(NEMId dst, std::uint64_t u64DataRatebps, const TimePoint & txTime)
446  {
447  updateTxActivityData_i(lookupR2RIMetric(dst), u64DataRatebps, txTime);
448  }
449 
450 
451  void handleNeighborTxActivity(NEMId dst, std::uint64_t u64DataRatebps, const TimePoint & txTime)
452  {
453  auto neighborDataPair = lookupNeighborData(dst);
454 
455  // update the data
456  updateTxActivityData_i(neighborDataPair.first, u64DataRatebps, txTime);
457 
458  updateTxActivityData_i(neighborDataPair.second, u64DataRatebps, txTime);
459 
460  // update the long term stats
461  updateTxActivityNeighborMetricStatistics_i(neighborDataPair.second);
462  }
463 
464 
465  void handleR2RIRxActivity(NEMId src,
466  std::uint64_t u64SeqNum,
467  const uuid_t & uuid,
468  const EMANE::TimePoint & rxTime)
469  {
470  updateRxActivityData_i(lookupR2RIMetric(src), u64SeqNum, uuid, rxTime);
471  }
472 
473 
474  void handleR2RIRxActivity(NEMId src,
475  std::uint64_t u64SeqNum,
476  const uuid_t & uuid,
477  float fSINR,
478  float fNoiseFloor,
479  const EMANE::TimePoint & rxTime,
480  const EMANE::Microseconds & durationMicroseconds,
481  std::uint64_t u64DataRatebps)
482  {
483  NeighborData * pNeighborMetric = lookupR2RIMetric(src);
484 
485  updateRxActivityData_i(pNeighborMetric, u64SeqNum, uuid, rxTime);
486 
487  updateRxActivityChannelData_i(pNeighborMetric, fSINR, fNoiseFloor, durationMicroseconds, u64DataRatebps);
488  }
489 
490 
491  void updateNeighborRxActivity(NEMId src,
492  std::uint64_t u64SeqNum,
493  const uuid_t & uuid,
494  const EMANE::TimePoint & rxTime)
495  {
496  auto neighborDataPair = lookupNeighborData(src);
497 
498  // update the data
499  updateRxActivityData_i(neighborDataPair.first, u64SeqNum, uuid, rxTime);
500 
501  updateRxActivityData_i(neighborDataPair.second, u64SeqNum, uuid, rxTime);
502 
503  // update the long term stats
504  updateRxActivityNeighborMetricStatistics_i(neighborDataPair.second);
505  }
506 
507 
508 
509  void updateNeighborRxActivity(NEMId src,
510  std::uint64_t u64SeqNum,
511  const uuid_t & uuid,
512  float fSINR,
513  float fNoiseFloor,
514  const EMANE::TimePoint & rxTime,
515  const EMANE::Microseconds & durationMicroseconds,
516  std::uint64_t u64DataRatebps)
517  {
518  auto neighborDataPair = lookupNeighborData(src);
519 
520  // update the data
521  updateRxActivityData_i(neighborDataPair.first, u64SeqNum, uuid, rxTime);
522 
523  updateRxActivityData_i(neighborDataPair.second, u64SeqNum, uuid, rxTime);
524 
525  updateRxActivityChannelData_i(neighborDataPair.first, fSINR, fNoiseFloor, durationMicroseconds, u64DataRatebps);
526 
527  updateRxActivityChannelData_i(neighborDataPair.second, fSINR, fNoiseFloor, durationMicroseconds, u64DataRatebps);
528 
529  // update the long term stats
530  updateRxActivityNeighborMetricStatistics_i(neighborDataPair.second);
531 
532  updateRxActivityChannelStatistics_i(neighborDataPair.second);
533  }
534 
535 
536 
537  void updateTxActivityData_i(NeighborData * pNeighborMetric, std::uint64_t u64DataRatebps, const TimePoint & txTime)
538  {
539  pNeighborMetric->u64NumTxFrames_ += 1;
540 
541  pNeighborMetric->lastTxTime_ = txTime;
542 
543  updateRunningAverage(pNeighborMetric->u64TxDataRateAvg_,
544  pNeighborMetric->u64NumTxFrames_,
545  u64DataRatebps);
546 
547  updateMinMax(pNeighborMetric->u64TxDataRateMin_, pNeighborMetric->u64TxDataRateMax_, u64DataRatebps);
548  }
549 
550 
551 
552 
553 
554  void updateTxActivityNeighborMetricStatistics_i(NeighborData * pNeighborMetric)
555  {
556  StatisticTable<NEMId> * pTable = pStatisticNeighborMetricTable_;
557 
558  // set statistic(s)
559  pTable->setCell(pNeighborMetric->nemId_,
560  NBR_METRIC_TX_FRAMES,
561  Any{pNeighborMetric->u64NumTxFrames_});
562 
563  pTable->setCell(pNeighborMetric->nemId_,
564  NBR_METRIC_TX_DATARATE_AVG,
565  Any{pNeighborMetric->u64TxDataRateAvg_});
566 
567  pTable->setCell(pNeighborMetric->nemId_,
568  NBR_METRIC_LAST_TX_TIME,
569  Any{std::chrono::duration_cast<DoubleSeconds>(
570  pNeighborMetric->lastTxTime_.time_since_epoch()).count()});
571  }
572 
573 
574  void updateRxActivityData_i(NeighborData * pNeighborMetric,
575  std::uint64_t u64SeqNum,
576  const uuid_t & uuid,
577  const EMANE::TimePoint & rxTime)
578  {
579  if(!uuid_compare(uuid,pNeighborMetric->uuid_))
580  {
581  if(u64SeqNum > pNeighborMetric->u64LastRxSeqNum_)
582  {
583  pNeighborMetric->u64NumRxMissedFrames_ +=
584  u64SeqNum - pNeighborMetric->u64LastRxSeqNum_ - 1;
585 
586  pNeighborMetric->u64LastRxSeqNum_ = u64SeqNum;
587  }
588  else
589  {
590  // out of order, previously accounted for as missed
591  if(pNeighborMetric->u64NumRxMissedFrames_)
592  {
593  pNeighborMetric->u64NumRxMissedFrames_ -= 1;
594  }
595  }
596  }
597  else
598  {
599  // first packet or emulator restart
600  uuid_copy(pNeighborMetric->uuid_,uuid);
601 
602  pNeighborMetric->u64LastRxSeqNum_ = u64SeqNum;
603  }
604 
605  pNeighborMetric->u64NumRxFrames_ += 1;
606 
607  pNeighborMetric->lastRxTime_ = rxTime;
608 
609  pNeighborMetric->bHaveEverHadRxActivity_ = true;
610  }
611 
612 
613  void updateRxActivityNeighborMetricStatistics_i(NeighborData * pNeighborMetric)
614  {
615  StatisticTable<NEMId> * pTable = pStatisticNeighborMetricTable_;
616 
617  // update statistic(s)
618  pTable->setCell(pNeighborMetric->nemId_,
619  NBR_METRIC_RX_FRAMES,
620  Any{pNeighborMetric->u64NumRxFrames_});
621 
622  pTable->setCell(pNeighborMetric->nemId_,
623  NBR_METRIC_MISSED_FRAMES,
624  Any{pNeighborMetric->u64NumRxMissedFrames_});
625  }
626 
627 
628  void updateRxActivityChannelData_i(NeighborData * pNeighborMetric,
629  float fSINR,
630  float fNoiseFloor,
631  const Microseconds & durationMicroseconds,
632  std::uint64_t u64DataRatebps)
633  {
634  pNeighborMetric->rxUtilizationMicroseconds_ += durationMicroseconds;
635 
636  pNeighborMetric->fSINRSum_ += fSINR;
637  pNeighborMetric->fSINRSum2_ += (fSINR * fSINR);
638 
639  pNeighborMetric->fNoiseFloorSum_ += fNoiseFloor;
640  pNeighborMetric->fNoiseFloorSum2_ += (fNoiseFloor * fNoiseFloor);
641 
642  updateMinMax(pNeighborMetric->u64RxDataRateMin_, pNeighborMetric->u64RxDataRateMax_, u64DataRatebps);
643 
644  updateRunningAverage(pNeighborMetric->u64RxDataRateAvg_, pNeighborMetric->u64NumRxFrames_, u64DataRatebps);
645  }
646 
647 
648  void updateRxActivityChannelStatistics_i(NeighborData * pNeighborMetric)
649  {
650  float fSINRAvg{}, fSINRStdv{}, fNoiseFloorAvg{}, fNoiseFloorStdv{};
651 
652  // get sinr avg and standard deviation
653  getAvgAndStd(pNeighborMetric->fSINRSum_,
654  pNeighborMetric->fSINRSum2_,
655  pNeighborMetric->u64NumRxFrames_,
656  fSINRAvg,
657  fSINRStdv);
658 
659  // get noise floor avg and standard deviation
660  getAvgAndStd(pNeighborMetric->fNoiseFloorSum_,
661  pNeighborMetric->fNoiseFloorSum2_,
662  pNeighborMetric->u64NumRxFrames_,
663  fNoiseFloorAvg,
664  fNoiseFloorStdv);
665 
666  StatisticTable<NEMId> * pTable = pStatisticNeighborMetricTable_;
667 
668  // update statistic(s)
669  pTable->setCell(pNeighborMetric->nemId_,
670  NBR_METRIC_BW_CONSUMPTION,
671  Any{pNeighborMetric->rxUtilizationMicroseconds_.count()});
672 
673  pTable->setCell(pNeighborMetric->nemId_,
674  NBR_METRIC_LAST_RX_TIME,
675  Any{std::chrono::duration_cast<DoubleSeconds>(
676  pNeighborMetric->lastRxTime_.time_since_epoch()).count()});
677 
678  pTable->setCell(pNeighborMetric->nemId_,
679  NBR_METRIC_SINR_AVG,
680  Any{fSINRAvg});
681 
682  pTable->setCell(pNeighborMetric->nemId_,
683  NBR_METRIC_SINR_STDV,
684  Any{fSINRStdv});
685 
686  pTable->setCell(pNeighborMetric->nemId_,
687  NBR_METRIC_NOISE_FLOOR_AVG,
688  Any{fNoiseFloorAvg});
689 
690  pTable->setCell(pNeighborMetric->nemId_,
691  NBR_METRIC_NOISE_FLOOR_STDV,
692  Any{fNoiseFloorStdv});
693 
694  pTable->setCell(pNeighborMetric->nemId_,
695  NBR_METRIC_RX_DATARATE_AVG,
696  Any{pNeighborMetric->u64RxDataRateAvg_});
697  }
698 
699 
700  void clearData_i(NeighborData * pNeighborMetric)
701  {
702  // clear just about everything but the lastRx seq, time and activity flag
703 
704  // reset missed frames
705  pNeighborMetric->u64NumRxMissedFrames_ = 0;
706 
707  // reset rx/tx frames
708  pNeighborMetric->u64NumRxFrames_ = 0;
709  pNeighborMetric->u64NumTxFrames_ = 0;
710 
711  // reset bw consumption
712  pNeighborMetric->rxUtilizationMicroseconds_ = EMANE::Microseconds::zero();
713 
714  // reset the SINR
715  pNeighborMetric->fSINRSum_ = 0.0f;
716  pNeighborMetric->fSINRSum2_ = 0.0f;
717 
718  // reset the noise floor
719  pNeighborMetric->fNoiseFloorSum_ = 0.0f;
720  pNeighborMetric->fNoiseFloorSum2_ = 0.0f;
721 
722  // reset data rate
723  pNeighborMetric->u64RxDataRateMin_ = 0;
724  pNeighborMetric->u64RxDataRateMax_ = 0;
725  pNeighborMetric->u64RxDataRateAvg_ = 0;
726 
727  pNeighborMetric->u64TxDataRateMin_ = 0;
728  pNeighborMetric->u64TxDataRateMax_ = 0;
729  pNeighborMetric->u64TxDataRateAvg_ = 0;
730  }
731 
732 
733  float getAvg(float fSum, size_t numSamples)
734  {
735  if(numSamples > 1)
736  {
737  return fSum / numSamples;
738  }
739  else if(numSamples == 1)
740  {
741  return fSum;
742  }
743  else
744  {
745  return 0.0f;
746  }
747  }
748 
749 
750  void getAvgAndStd(float fSum, float fSumSquared, size_t numSamples, float & fAvg, float & fStd)
751  {
752  fAvg = getAvg(fSum, numSamples);
753 
754  if(numSamples > 1)
755  {
756  float fDelta{fSumSquared - (fSum * fSum)};
757 
758  if(fDelta > 0.0f)
759  {
760  fStd = sqrt(fDelta / numSamples) / (numSamples - 1.0f);
761  }
762  }
763  }
764 
765  inline void updateMinMax(std::uint64_t & min, std::uint64_t & max, std::uint64_t value)
766  {
767  if(min > value || min == 0)
768  {
769  min = value;
770  }
771 
772  if(max < value || max == 0)
773  {
774  max = value;
775  }
776  }
777 
778  // set the running avg, based on count, and new value
779  inline void updateRunningAverage(std::uint64_t &avg, std::uint64_t count, std::uint64_t val)
780  {
781  avg = (count == 1) ? val : ((avg * count) + val) / (count + 1);
782  }
783  };
784 
785 
786 
788  pImpl_{new Implementation{nemId}}
789 { }
790 
791 
793 { }
794 
795 
796 void
798 {
799  pImpl_->setNeighborDeleteTimeMicroseconds(ageMicroseconds);
800 }
801 
802 
803 void
804 EMANE::NeighborMetricManager::updateNeighborTxMetric(NEMId dst, std::uint64_t u64DataRatebps, const TimePoint & txTime)
805 {
806  // tx activity
807  pImpl_->handleTxActivity(dst, u64DataRatebps, txTime);
808 }
809 
810 
811 void
813  std::uint64_t u64SeqNum,
814  const uuid_t & uuid,
815  const TimePoint & rxTime)
816 {
817  // rx activity (short form)
818  pImpl_->handleRxActivity(src, u64SeqNum, uuid, rxTime);
819 }
820 
821 
823  std::uint64_t u64SeqNum,
824  const uuid_t & uuid,
825  float fSINR,
826  float fNoiseFloor,
827  const TimePoint & rxTime,
828  const Microseconds & durationMicroseconds,
829  std::uint64_t u64DataRatebps)
830 {
831  // rx activity (short form)
832  pImpl_->handleRxActivity(src,
833  u64SeqNum,
834  uuid,
835  fSINR,
836  fNoiseFloor,
837  rxTime,
838  durationMicroseconds,
839  u64DataRatebps);
840 }
841 
842 
845 {
846  // when neighbor metrics are pulled we update the short term statistics too
847  pImpl_->handleNeighborStatusUpdate();
848 
849  return pImpl_->getNeighborMetrics();
850 }
851 
852 
854 {
855  pImpl_->handleNeighborStatusUpdate();
856 }
857 
858 
860 {
861  pImpl_->registerStatistics(statisticRegistrar);
862 }
863 
void updateNeighborRxMetric(NEMId src, std::uint64_t u64SeqNum, const uuid_t &uuid, const TimePoint &rxTime)
Controls::R2RINeighborMetrics getNeighborMetrics()
constexpr NEMId NEM_BROADCAST_MAC_ADDRESS
Definition: types.h:69
union EtherAddr dst
Definition: netutils.h:390
std::list< R2RINeighborMetric > R2RINeighborMetrics
void updateNeighborTxMetric(NEMId dst, std::uint64_t u64DataRatebps, const TimePoint &txTime)
The StatisticRegistrar allows NEM layers to register statistics and statistic tables. Statistics and Statistic tables are owned by the emulator framework and a borrowed reference is returned to the registering NEM layer.
std::vector< std::string > StatisticTableLabels
std::chrono::microseconds Microseconds
Definition: types.h:45
std::chrono::duration< double > DoubleSeconds
Definition: types.h:47
std::uint16_t NEMId
Definition: types.h:52
R2RI neighbor metrics are used in conjunction with the R2RINeighborMetricControlMessage to inform an ...
void registerStatistics(StatisticRegistrar &statisticRegistrar)
void setNeighborDeleteTimeMicroseconds(const Microseconds &ageMicroseconds)
union EtherAddr src
Definition: netutils.h:391
Clock::time_point TimePoint
Definition: types.h:50