EMANE  1.0.1
spectrummonitor.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2015 - 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 "spectrummonitor.h"
34 #include "noiserecorder.h"
35 
38 
39 #include <algorithm>
40 
41 namespace
42 {
43  double frequencyOverlapRatio(std::uint64_t u64FrequencyHz1,
44  std::uint64_t u64BandwidthHz1,
45  std::uint64_t u64FrequencyHz2,
46  std::uint64_t u64BandwidthHz2);
47 }
48 
50  binSize_{},
51  maxOffset_{},
52  maxPropagation_{},
53  maxDuration_{},
54  bMaxClamp_{},
55  timeSyncThreshold_{},
56  u64ReceiverBandwidthHz_{},
57  mode_{NoiseMode::NONE},
58  dReceiverSensitivityMilliWatt_{}{}
59 
61  std::uint64_t u64BandwidthHz,
62  double dReceiverSensitivityMilliWatt,
63  NoiseMode mode,
64  const Microseconds & binSize,
65  const Microseconds & maxOffset,
66  const Microseconds & maxPropagation,
67  const Microseconds & maxDuration,
68  const Microseconds & timeSyncThreshold,
69  bool bMaxClamp)
70 {
71  std::lock_guard<std::mutex> m(mutex_);
72 
73  binSize_ = binSize;
74 
75  mode_ = mode;
76 
77  maxOffset_ = maxOffset;
78 
79  maxPropagation_ = maxPropagation;
80 
81  maxDuration_ = maxDuration;
82 
83  bMaxClamp_ = bMaxClamp;
84 
85  timeSyncThreshold_ = timeSyncThreshold;
86 
87  dReceiverSensitivityMilliWatt_ = dReceiverSensitivityMilliWatt;
88 
89  u64ReceiverBandwidthHz_ = u64BandwidthHz;
90 
91  // (re-)initialize transmitter bandwidth cache
92  transmitterBandwidthCache_.clear();
93 
94  TransmitterBandwidthCache transmitterBandwidthCache_;
95 
96  transmitterBandwidthCache_.insert(std::make_pair(u64BandwidthHz,std::unique_ptr<Cache>(new Cache{})));
97 
98  noiseRecorderMap_.clear();
99 
100  for(const auto & frequency : foi)
101  {
102  noiseRecorderMap_.insert(std::make_pair(frequency,
103  std::unique_ptr<NoiseRecorder>{new NoiseRecorder{binSize,
104  maxOffset,
105  maxPropagation,
106  maxDuration,
107  dReceiverSensitivityMilliWatt}}));
108  }
109 
110 }
111 
112 std::tuple<EMANE::TimePoint,EMANE::Microseconds,EMANE::Microseconds,EMANE::FrequencySegments,bool>
114  const TimePoint & txTime,
115  const Microseconds & propagationDelay,
116  const FrequencySegments & segments,
117  std::uint64_t u64SegmentBandwidthHz,
118  const std::vector<double> & rxPowersMilliWatt,
119  bool bInBand,
120  const std::vector<NEMId> & transmitters)
121 {
122  std::lock_guard<std::mutex> m(mutex_);
123 
124  if(segments.size() != rxPowersMilliWatt.size())
125  {
126  return std::make_tuple(TimePoint{},Microseconds{},Microseconds{},FrequencySegments{},false);
127  }
128 
129  bool bReportAsInBand{};
130 
131  FrequencySegments reportableFrequencySegments;
132 
133  // validate txTime in case of time sync issues
134  auto validTxTime = txTime;
135 
136  if(txTime > now + timeSyncThreshold_ || now - txTime > timeSyncThreshold_)
137  {
138  validTxTime = now;
139  }
140 
141  auto validPropagation = propagationDelay;
142 
143  if(propagationDelay > maxPropagation_)
144  {
145  if(bMaxClamp_)
146  {
147  validPropagation = maxPropagation_;
148  }
149  else
150  {
151  throw makeException<SpectrumServiceException>("Message propagation %ju usec > max propagation %ju usec and max clamp is %s",
152  propagationDelay.count(),
153  maxPropagation_.count(),
154  bMaxClamp_ ? "on" : "off");
155  }
156  }
157 
158  TimePoint maxEoR{};
159  TimePoint minSoR{TimePoint::max()};
160 
161  // if noise processing is disabled just check in-band updates for
162  // frequencies of interest
163  if(mode_ == NoiseMode::NONE || (mode_ == NoiseMode::OUTOFBAND && bInBand))
164  {
165  size_t i{};
166 
167  bReportAsInBand = true;
168 
169  for(const auto & segment : segments)
170  {
171  if(noiseRecorderMap_.find(segment.getFrequencyHz()) != noiseRecorderMap_.end())
172  {
173  if(rxPowersMilliWatt[i] >= dReceiverSensitivityMilliWatt_)
174  {
175  auto validOffset = segment.getOffset();
176 
177  if(validOffset > maxOffset_)
178  {
179  if(bMaxClamp_)
180  {
181  validOffset = maxOffset_;
182  }
183  else
184  {
185  throw makeException<SpectrumServiceException>("Segment offset %ju usec > max offset %ju usec and max clamp is %s",
186  validOffset.count(),
187  maxOffset_.count(),
188  bMaxClamp_ ? "on" : "off");
189  }
190  }
191 
192  auto validDuration = segment.getDuration();
193 
194  if(validDuration > maxDuration_)
195  {
196  if(bMaxClamp_)
197  {
198  validDuration = maxDuration_;
199  }
200  else
201  {
202  throw makeException<SpectrumServiceException>("Segment duration %ju usec > max duration %ju usec and max clamp is %s",
203  validDuration.count(),
204  maxDuration_.count(),
205  bMaxClamp_ ? "on" : "off");
206  }
207  }
208 
209  maxEoR =
210  std::max(maxEoR,validTxTime + validOffset + validDuration + validPropagation);
211 
212  minSoR =
213  std::min(minSoR,validTxTime + validOffset + validPropagation);
214 
215  reportableFrequencySegments.push_back({segment.getFrequencyHz(),
216  Utils::MILLIWATT_TO_DB(rxPowersMilliWatt[i]),
217  validDuration,
218  validOffset});
219  }
220 
221  ++i;
222  }
223  else
224  {
225  bReportAsInBand = false;
226  }
227  }
228  }
229  else
230  {
231  Cache * pCache{};
232 
233  // find the cache for the transmitter bandwidth
234  const auto transmitterBandwidthCacheIter =
235  transmitterBandwidthCache_.find(u64SegmentBandwidthHz);
236 
237  // cache found
238  if(transmitterBandwidthCacheIter != transmitterBandwidthCache_.end())
239  {
240  // get frequency overlap cache
241  pCache = transmitterBandwidthCacheIter->second.get();
242  }
243  else
244  {
245  // none found - create a transmitter bandwidth cache
246  pCache = new Cache{};
247 
248  transmitterBandwidthCache_.insert(std::make_pair(u64SegmentBandwidthHz,
249  std::unique_ptr<Cache>(pCache)));
250  }
251 
252  size_t i{};
253 
254  for(const auto & segment : segments)
255  {
256  bool bFrequencyMatch{};
257  bool bAboveSensitivity{};
258 
259  TimePoint startOfReception{};
260  TimePoint endOfReception{};
261 
262  auto iter = pCache->find(segment.getFrequencyHz());
263 
264  auto validOffset = segment.getOffset();
265 
266  if(validOffset > maxOffset_)
267  {
268  if(bMaxClamp_)
269  {
270  validOffset = maxOffset_;
271  }
272  else
273  {
274  throw makeException<SpectrumServiceException>("Segment offset %ju usec > max offset %ju usec and max clamp is %s",
275  validOffset.count(),
276  maxOffset_.count(),
277  bMaxClamp_ ? "on" : "off");
278  }
279  }
280 
281  auto validDuration = segment.getDuration();
282 
283  if(validDuration > maxDuration_)
284  {
285  if(bMaxClamp_)
286  {
287  validDuration = maxDuration_;
288  }
289  else
290  {
291  throw makeException<SpectrumServiceException>("Segment duration %ju usec > max duration %ju usec and max clamp is %s",
292  validDuration.count(),
293  maxDuration_.count(),
294  bMaxClamp_ ? "on" : "off");
295  }
296  }
297 
298  if(iter != pCache->end())
299  {
300  for(const auto & entry : iter->second)
301  {
302  NoiseRecorder * pNoiseRecorder{};
303  double dOverlapRatio{};
304  std::uint64_t u64RecorderFrequencyHz{};
305 
306  std::tie(pNoiseRecorder,
307  dOverlapRatio,
308  u64RecorderFrequencyHz) = entry;
309 
310  if(dOverlapRatio > 0)
311  {
312  double dOverlapRxPowerMillWatt{rxPowersMilliWatt[i] * dOverlapRatio};
313 
314  if(dOverlapRxPowerMillWatt >= dReceiverSensitivityMilliWatt_)
315  {
316  bAboveSensitivity = true;
317 
318  std::tie(startOfReception,endOfReception) =
319  pNoiseRecorder->update(now,
320  validTxTime,
321  validOffset,
322  validPropagation,
323  validDuration,
324  dOverlapRxPowerMillWatt,
325  transmitters);
326  }
327 
328  if(!bFrequencyMatch)
329  {
330  bFrequencyMatch = (u64RecorderFrequencyHz == segment.getFrequencyHz());
331  }
332  }
333  }
334  }
335  else
336  {
337  std::vector<std::tuple<NoiseRecorder *,double,std::uint64_t>> recorderInfo;
338 
339  recorderInfo.reserve(noiseRecorderMap_.size());
340 
341  for(const auto & entry : noiseRecorderMap_)
342  {
343  double dOverlapRatio{frequencyOverlapRatio(entry.first,
344  u64ReceiverBandwidthHz_,
345  segment.getFrequencyHz(),
346  u64SegmentBandwidthHz)};
347 
348  if(dOverlapRatio > 0)
349  {
350  double dOverlapRxPowerMillWatt{rxPowersMilliWatt[i] * dOverlapRatio};
351 
352  if(dOverlapRxPowerMillWatt >= dReceiverSensitivityMilliWatt_)
353  {
354  bAboveSensitivity = true;
355 
356  std::tie(startOfReception,endOfReception) =
357  entry.second->update(now,
358  validTxTime,
359  validOffset,
360  validPropagation,
361  validDuration,
362  dOverlapRxPowerMillWatt,
363  transmitters);
364  }
365 
366  if(!bFrequencyMatch)
367  {
368  bFrequencyMatch = (entry.first == segment.getFrequencyHz());
369  }
370  }
371 
372  recorderInfo.push_back(std::make_tuple(entry.second.get(),dOverlapRatio,entry.first));
373  }
374 
375  pCache->insert({segment.getFrequencyHz(),std::move(recorderInfo)});
376  }
377 
378  if(bFrequencyMatch)
379  {
380  if(bInBand)
381  {
382  bReportAsInBand = true;
383  }
384 
385  if(bAboveSensitivity)
386  {
387  maxEoR = std::max(maxEoR,endOfReception);
388 
389  minSoR = std::min(minSoR,startOfReception);
390 
391  reportableFrequencySegments.push_back({segment.getFrequencyHz(),
392  Utils::MILLIWATT_TO_DB(rxPowersMilliWatt[i]),
393  validDuration,
394  validOffset});
395  }
396  }
397 
398  ++i;
399  }
400  }
401 
402  return std::make_tuple(validTxTime,
403  validPropagation,
404  std::chrono::duration_cast<Microseconds>(maxEoR - minSoR),
405  reportableFrequencySegments,
406  bReportAsInBand);
407 }
408 
411 {
412  std::lock_guard<std::mutex> m(mutex_);
413 
414  FrequencySet frequencySet;
415 
416  std::transform(noiseRecorderMap_.begin(),
417  noiseRecorderMap_.end(),
418  std::inserter(frequencySet,frequencySet.begin()),
419  std::bind(&NoiseRecorderMap::value_type::first,
420  std::placeholders::_1));
421  return frequencySet;
422 }
423 
425 {
426  std::lock_guard<std::mutex> m(mutex_);
427 
428  return Utils::MILLIWATT_TO_DB(dReceiverSensitivityMilliWatt_);
429 }
430 
433  std::uint64_t u64FrequencyHz,
434  const Microseconds & duration,
435  const TimePoint & timepoint) const
436 {
437  std::lock_guard<std::mutex> m(mutex_);
438 
439  auto validDuration = duration;
440 
441  if(validDuration > maxDuration_)
442  {
443  if(bMaxClamp_)
444  {
445  validDuration = maxDuration_;
446  }
447  else
448  {
449  throw makeException<SpectrumServiceException>("Segment duration %ju usec > max duration %ju usec and max clamp is %s",
450  validDuration.count(),
451  maxDuration_.count(),
452  bMaxClamp_ ? "on" : "off");
453  }
454  }
455 
456  const auto iter = noiseRecorderMap_.find(u64FrequencyHz);
457 
458  if(iter!= noiseRecorderMap_.end())
459  {
460  auto ret = iter->second->get(now,validDuration,timepoint);
461 
462  return std::tuple_cat(std::move(ret),std::make_tuple(binSize_,dReceiverSensitivityMilliWatt_,mode_==NoiseMode::ALL));
463  }
464  else
465  {
466  return SpectrumWindow{{},{},{},{},false};
467  }
468 }
469 
471 EMANE::SpectrumMonitor::request(std::uint64_t u64FrequencyHz,
472  const Microseconds & duration,
473  const TimePoint & timepoint) const
474 {
475  return request_i(Clock::now(),u64FrequencyHz,duration,timepoint);
476 }
477 
478 std::vector<double> EMANE::SpectrumMonitor::dump(std::uint64_t u64FrequencyHz) const
479 {
480  std::lock_guard<std::mutex> m(mutex_);
481 
482  const auto iter = noiseRecorderMap_.find(u64FrequencyHz);
483 
484  if(iter!= noiseRecorderMap_.end())
485  {
486  return iter->second->dump();
487  }
488 
489  return {};
490 }
491 
492 namespace
493 {
494  double frequencyOverlapRatio(std::uint64_t u64FrequencyHz1,
495  std::uint64_t u64BandwidthHz1,
496  std::uint64_t u64FrequencyHz2,
497  std::uint64_t u64BandwidthHz2)
498  {
499  double u64UpperFrequencyHz1{u64FrequencyHz1 + u64BandwidthHz1 / 2.0};
500  double u64LowerFrequencyHz1{u64FrequencyHz1 - u64BandwidthHz1 / 2.0};
501 
502  double u64UpperFrequencyHz2{u64FrequencyHz2 + u64BandwidthHz2 / 2.0};
503  double u64LowerFrequencyHz2{u64FrequencyHz2 - u64BandwidthHz2 / 2.0};
504 
505  // percent in band, defaults to no coverage
506  double dRatio{};
507 
508  // signal is somewhere in band
509  if((u64LowerFrequencyHz2 < u64UpperFrequencyHz1) && (u64UpperFrequencyHz2 > u64LowerFrequencyHz1))
510  {
511  // low is within lower bound
512  if(u64LowerFrequencyHz2 >= u64LowerFrequencyHz1)
513  {
514  // high is within upper bound
515  if(u64UpperFrequencyHz2 <= u64UpperFrequencyHz1)
516  {
517  // full coverage
518  dRatio = 1.0;
519  }
520  // exceeded upper bound
521  else
522  {
523  // partial coverage
524  dRatio = (u64UpperFrequencyHz1 - u64LowerFrequencyHz2) / u64BandwidthHz2;
525  }
526  }
527  // low is below lower bound
528  else
529  {
530  // the signal is at or beyond
531  if( u64UpperFrequencyHz2 <= u64UpperFrequencyHz1)
532  {
533  // partial coverage
534  dRatio = (u64UpperFrequencyHz2 - u64LowerFrequencyHz1) / u64BandwidthHz2;
535  }
536  else
537  {
538  dRatio = (u64UpperFrequencyHz1 - u64LowerFrequencyHz1) / u64BandwidthHz2;
539  }
540  }
541  }
542 
543  // return ratio
544  return dRatio;
545  }
546 }
double MILLIWATT_TO_DB(double dMillWatt)
std::tuple< std::vector< double >, TimePoint, Microseconds, double, bool > SpectrumWindow
double getReceiverSensitivitydBm() const override
std::set< std::uint64_t > FrequencySet
std::tuple< TimePoint, Microseconds, Microseconds, FrequencySegments, bool > update(const TimePoint &now, const TimePoint &txTime, const Microseconds &propagationDelay, const FrequencySegments &segments, std::uint64_t u64SegmentBandwidthHz, const std::vector< double > &rxPowersMilliWatt, bool bInBand, const std::vector< NEMId > &transmitters)
std::chrono::microseconds Microseconds
Definition: types.h:45
void initialize(const FrequencySet &foi, std::uint64_t u64BandwidthHz, double dReceiverSensitivityMilliWatt, NoiseMode mode, const Microseconds &binSize, const Microseconds &maxOffset, const Microseconds &maxPropagation, const Microseconds &maxDuration, const Microseconds &timeSyncThreshold, bool bMaxClamp)
std::list< FrequencySegment > FrequencySegments
std::pair< TimePoint, TimePoint > update(const TimePoint &now, const TimePoint &txTime, const Microseconds &offset, const Microseconds &propagation, const Microseconds &duration, double dRxPower, const std::vector< NEMId > &transmitters)
FrequencySet getFrequencies() const override
Clock::time_point TimePoint
Definition: types.h:50
SpectrumWindow request(std::uint64_t u64FrequencyHz, const Microseconds &duration=Microseconds::zero(), const TimePoint &timepoint=TimePoint::min()) const override
std::vector< double > dump(std::uint64_t u64FrequencyHz) const
SpectrumWindow request_i(const TimePoint &now, std::uint64_t u64FrequencyHz, const Microseconds &duration=Microseconds::zero(), const TimePoint &timepoint=TimePoint::min()) const