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