EMANE  1.0.1
frameworkphy.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2014,2016 - Adjacent Link LLC, Bridgewater,
3  * New Jersey
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  * * Neither the name of Adjacent Link LLC nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "frameworkphy.h"
35 
36 #include "emane/commonphyheader.h"
39 
41 
48 
56 
60 
64 
65 namespace
66 {
67  const std::uint16_t DROP_CODE_OUT_OF_BAND = 1;
68  const std::uint16_t DROP_CODE_RX_SENSITIVITY = 2;
69  const std::uint16_t DROP_CODE_PROPAGATIONMODEL = 3;
70  const std::uint16_t DROP_CODE_GAINMANAGER_LOCATION = 4;
71  const std::uint16_t DROP_CODE_GAINMANAGER_HORIZON = 5;
72  const std::uint16_t DROP_CODE_GAINMANAGER_ANTENNAPROFILE = 6;
73  const std::uint16_t DROP_CODE_NOT_FOI = 7;
74  const std::uint16_t DROP_CODE_SPECTRUM_CLAMP = 8;
75 
76  EMANE::StatisticTableLabels STATISTIC_TABLE_LABELS{"Out-of-Band",
77  "Rx Sensitivity",
78  "Propagation Model",
79  "Gain Location",
80  "Gain Horizon",
81  "Gain Profile",
82  "Not FOI",
83  "Spectrum Clamp"};
84 }
85 
87  PlatformServiceProvider * pPlatformService,
88  SpectrumMonitor * pSpectrumMonitor):
89  PHYLayerImplementor{id, pPlatformService},
90  pSpectrumMonitor_{pSpectrumMonitor},
91  gainManager_{id},
92  locationManager_{id},
93  u64BandwidthHz_{},
94  dTxPowerdBm_{},
95  u64TxFrequencyHz_{},
96  dReceiverSensitivitydBm_{},
97  noiseMode_{},
98  u16SubId_{},
99  u16TxSequenceNumber_{},
100  commonLayerStatistics_{STATISTIC_TABLE_LABELS,{},"0"},
101  eventTablePublisher_{id},
102  noiseBinSize_{},
103  maxSegmentOffset_{},
104  maxMessagePropagation_{},
105  maxSegmentDuration_{},
106  timeSyncThreshold_{},
107  bNoiseMaxClamp_{},
108  dSystemNoiseFiguredB_{}{}
109 
111 
113 {
114  auto & configRegistrar = registrar.configurationRegistrar();
115 
116  configRegistrar.registerNumeric<double>("fixedantennagain",
119  {0},
120  "Defines the antenna gain in dBi and is valid only when"
121  " fixedantennagainenable is enabled.");
122 
123  configRegistrar.registerNumeric<bool>("fixedantennagainenable",
125  {true},
126  "Defines whether fixed antenna gain is used or whether"
127  " antenna profiles are in use.");
128 
129  configRegistrar.registerNumeric<std::uint64_t>("bandwidth",
131  {1000000},
132  "Defines receiver bandwidth in Hz and also serves as the"
133  " default bandwidth for OTA transmissions when not provided"
134  " by the MAC.",
135  1);
136 
137  configRegistrar.registerNumeric<std::uint64_t>("frequency",
139  {2347000000},
140  "Defines the default transmit center frequency in Hz when not"
141  " provided by the MAC. This value is included in the Common PHY"
142  " Header of all transmitted OTA packets.",
143  1);
144 
145  configRegistrar.registerNumeric<std::uint64_t>("frequencyofinterest",
147  {2347000000},
148  "Defines a set of center frequencies in Hz that are monitored"
149  " for reception as either in-band or out-of-band.",
150  std::numeric_limits<std::uint64_t>::min(),
151  std::numeric_limits<std::uint64_t>::max(),
152  1,
153  std::numeric_limits<std::size_t>::max());
154 
155  configRegistrar.registerNonNumeric<std::string>("noisemode",
157  {"all"},
158  "Defines the noise processing mode of operation:"
159  " none, all or outofband.",
160  1,
161  1,
162  "^(none|all|outofband)$");
163 
164 
165  configRegistrar.registerNumeric<std::uint64_t>("noisebinsize",
167  {20},
168  "Defines the noise bin size in microseconds and translates"
169  " into timing accuracy associated with aligning the start and"
170  " end of reception times of multiple packets for modeling of"
171  " interference effects.",
172  1);
173 
174  configRegistrar.registerNumeric<bool>("noisemaxclampenable",
176  {false},
177  "Defines whether segment offset, segment duration and message"
178  " propagation associated with a received packet will be clamped"
179  " to their respective maximums defined by noisemaxsegmentoffset,"
180  " noisemaxsegmentduration and noisemaxmessagepropagation. When"
181  " disabled, any packet with an above max value will be dropped.");
182 
183 
184  configRegistrar.registerNumeric<std::uint64_t>("noisemaxsegmentoffset",
186  {300000},
187  "Noise maximum segment offset in microseconds.",
188  1);
189 
190  configRegistrar.registerNumeric<std::uint64_t>("noisemaxmessagepropagation",
192  {200000},
193  "Noise maximum message propagation in microseconds.",
194  1);
195 
196  configRegistrar.registerNumeric<std::uint64_t>("noisemaxsegmentduration",
198  {1000000},
199  "Noise maximum segment duration in microseconds.",
200  1);
201 
202  configRegistrar.registerNumeric<std::uint64_t>("timesyncthreshold",
204  {10000},
205  "Defines the time sync detection threshold in microseconds."
206  " If a received OTA message is more than this threshold, the"
207  " message reception time will be used as the source transmission"
208  " time instead of the time contained in the Common PHY Header."
209  " This allows the emulator to be used across distributed nodes"
210  " without time sync.",
211  1);
212 
213  configRegistrar.registerNonNumeric<std::string>("propagationmodel",
215  {"precomputed"},
216  "Defines the pathloss mode of operation:"
217  " precomputed, 2ray or freespace.",
218  1,
219  1,
220  "^(precomputed|2ray|freespace)$");
221 
222  configRegistrar.registerNumeric<double>("systemnoisefigure",
224  {4.0},
225  "Defines the system noise figure in dB and is used to determine the"
226  " receiver sensitivity.");
227 
228  configRegistrar.registerNumeric<std::uint16_t>("subid",
230  {},
231  "Defines the emulator PHY subid used by multiple NEM"
232  " definitions. Once instantiated, these NEMs may be using the"
233  " same frequency. In order to differentiate between emulator"
234  " PHY instances for different waveforms, the subid is used as"
235  " part of the unique waveform identifying tuple: PHY Layer"
236  " Registration Id, emulator PHY subid and packet center"
237  " frequency.",
238  1);
239 
240  configRegistrar.registerNumeric<double>("txpower",
241  EMANE::ConfigurationProperties::DEFAULT |
243  {0.0},
244  "Defines the transmit power in dBm.");
246  auto & eventRegistrar = registrar.eventRegistrar();
247 
249 
250  eventRegistrar.registerEvent(Events::LocationEvent::IDENTIFIER);
251 
252  eventRegistrar.registerEvent(Events::AntennaProfileEvent::IDENTIFIER);
255  auto & statisticRegistrar = registrar.statisticRegistrar();
256 
257  commonLayerStatistics_.registerStatistics(statisticRegistrar);
258 
259  eventTablePublisher_.registerStatistics(statisticRegistrar);
260 
261  receivePowerTablePublisher_.registerStatistics(statisticRegistrar);
262 
263  pTimeSyncThresholdRewrite_ =
264  statisticRegistrar.registerNumeric<std::uint64_t>("numTimeSyncThresholdRewrite",
266 }
267 
269 {
270  FrequencySet foi{};
271 
272  for(const auto & item : update)
273  {
274  if(item.first == "bandwidth")
275  {
276  u64BandwidthHz_ = item.second[0].asUINT64();
277 
279  INFO_LEVEL,
280  "PHYI %03hu FrameworkPHY::%s: %s = %ju Hz",
281  id_,
282  __func__,
283  item.first.c_str(),
284  u64BandwidthHz_);
285  }
286  else if(item.first == "fixedantennagain")
287  {
288  optionalFixedAntennaGaindBi_.first = item.second[0].asDouble();
289 
291  INFO_LEVEL,
292  "PHYI %03hu FrameworkPHY::%s: %s = %3.2f dBi",
293  id_,
294  __func__,
295  item.first.c_str(),
296  optionalFixedAntennaGaindBi_.first);
297 
298  }
299  else if(item.first == "fixedantennagainenable")
300  {
301  optionalFixedAntennaGaindBi_.second = item.second[0].asBool();
302 
304  INFO_LEVEL,
305  "PHYI %03hu FrameworkPHY::%s: %s = %s",
306  id_,
307  __func__,
308  item.first.c_str(),
309  optionalFixedAntennaGaindBi_.second ? "on" : "off");
310  }
311  else if(item.first == "propagationmodel")
312  {
313  std::string sPropagationModel{item.second[0].asString()};
314 
315  // regex has already validated values
316  if(sPropagationModel == "precomputed")
317  {
318  pPropagationModelAlgorithm_.reset(new PrecomputedPropagationModelAlgorithm{id_});
319  }
320  else if(sPropagationModel == "2ray")
321  {
322  pPropagationModelAlgorithm_.reset(new TwoRayPropagationModelAlgorithm{id_});
323  }
324  else
325  {
326  pPropagationModelAlgorithm_.reset(new FreeSpacePropagationModelAlgorithm{id_});
327  }
328 
330  INFO_LEVEL,
331  "PHYI %03hu FrameworkPHY::%s: %s = %s",
332  id_,
333  __func__,
334  item.first.c_str(),
335  sPropagationModel.c_str());
336  }
337  else if(item.first == "noisemode")
338  {
339  std::string sNoiseMode{item.second[0].asString()};
340 
341  // regex has already validated values
342  if(sNoiseMode == "all")
343  {
344  noiseMode_ = SpectrumMonitor::NoiseMode::ALL;
345  }
346  else if(sNoiseMode == "none")
347  {
349  }
350  else
351  {
353  }
354 
356  INFO_LEVEL,
357  "PHYI %03hu FrameworkPHY::%s: %s = %s",
358  id_,
359  __func__,
360  item.first.c_str(),
361  sNoiseMode.c_str());
362  }
363  else if(item.first == "noisebinsize")
364  {
365  noiseBinSize_ = Microseconds{item.second[0].asUINT64()};
366 
368  INFO_LEVEL,
369  "PHYI %03hu FrameworkPHY::%s: %s = %ju usec",
370  id_,
371  __func__,
372  item.first.c_str(),
373  noiseBinSize_.count());
374  }
375  else if(item.first == "noisemaxclampenable")
376  {
377  bNoiseMaxClamp_ = item.second[0].asBool();
378 
380  INFO_LEVEL,
381  "PHYI %03hu FrameworkPHY::%s: %s = %s",
382  id_,
383  __func__,
384  item.first.c_str(),
385  bNoiseMaxClamp_ ? "on" : "off");
386  }
387  else if(item.first == "noisemaxsegmentoffset")
388  {
389  maxSegmentOffset_ = Microseconds{item.second[0].asUINT64()};
390 
392  INFO_LEVEL,
393  "PHYI %03hu FrameworkPHY::%s: %s = %ju usec",
394  id_,
395  __func__,
396  item.first.c_str(),
397  maxSegmentOffset_.count());
398  }
399  else if(item.first == "noisemaxmessagepropagation")
400  {
401  maxMessagePropagation_ = Microseconds{item.second[0].asUINT64()};
402 
404  INFO_LEVEL,
405  "PHYI %03hu FrameworkPHY::%s: %s = %ju usec",
406  id_,
407  __func__,
408  item.first.c_str(),
409  maxMessagePropagation_.count());
410  }
411  else if(item.first == "noisemaxsegmentduration")
412  {
413  maxSegmentDuration_ = Microseconds{item.second[0].asUINT64()};
414 
416  INFO_LEVEL,
417  "PHYI %03hu FrameworkPHY::%s: %s = %ju usec",
418  id_,
419  __func__,
420  item.first.c_str(),
421  maxSegmentDuration_.count());
422  }
423  else if(item.first == "timesyncthreshold")
424  {
425  timeSyncThreshold_ = Microseconds{item.second[0].asUINT64()};
426 
428  INFO_LEVEL,
429  "PHYI %03hu FrameworkPHY::%s: %s = %ju usec",
430  id_,
431  __func__,
432  item.first.c_str(),
433  timeSyncThreshold_.count());
434  }
435  else if(item.first == "systemnoisefigure")
436  {
437  dSystemNoiseFiguredB_ = item.second[0].asDouble();
438 
440  INFO_LEVEL,
441  "PHYI %03hu FrameworkPHY::%s: %s = %3.2f dB",
442  id_,
443  __func__,
444  item.first.c_str(),
445  dSystemNoiseFiguredB_);
446  }
448  else if(item.first == "frequencyofinterest")
449  {
450  for(const auto & any : item.second)
451  {
452  std::uint64_t u64Value{any.asUINT64()};
453 
454  // try to add value to foi set
455  if(foi.insert(u64Value).second)
456  {
458  INFO_LEVEL,
459  "PHYI %03hu FrameworkPHY::%s: %s = %ju Hz",
460  id_,
461  __func__,
462  item.first.c_str(),
463  u64Value);
464  }
465  else
466  {
467  throw makeException<ConfigureException>("FrameworkPHY duplicate frequency of interest found: %ju",
468  u64Value);
469 
470  }
471  }
472  }
475  else if(item.first == "txpower")
476  {
477  dTxPowerdBm_ = item.second[0].asDouble();
478 
480  INFO_LEVEL,
481  "PHYI %03hu FrameworkPHY::%s: %s = %3.2f dBm",
482  id_,
483  __func__,
484  item.first.c_str(),
485  dTxPowerdBm_);
486  }
488  else if(item.first == "frequency")
489  {
490  u64TxFrequencyHz_ = item.second[0].asUINT64();
491 
493  INFO_LEVEL,
494  "PHYI %03hu FrameworkPHY::%s: %s = %ju Hz",
495  id_,
496  __func__,
497  item.first.c_str(),
498  u64TxFrequencyHz_);
499  }
500  else if(item.first == "subid")
501  {
502  u16SubId_ = item.second[0].asUINT16();
503 
505  INFO_LEVEL,
506  "PHYI %03hu FrameworkPHY::%s: %s = %hu",
507  id_,
508  __func__,
509  item.first.c_str(),
510  u16SubId_);
511  }
512  else
513  {
514  throw makeException<ConfigureException>("FrameworkPHY: Unexpected configuration item %s",
515  item.first.c_str());
516  }
517  }
518 
519  dReceiverSensitivitydBm_ = THERMAL_NOISE_DB + dSystemNoiseFiguredB_ + 10.0 * log10(u64BandwidthHz_);
520 
521  if((maxSegmentOffset_ + maxMessagePropagation_ + 2 * maxSegmentDuration_) % noiseBinSize_ !=
522  Microseconds::zero())
523  {
524  throw makeException<ConfigureException>("noisemaxsegmentoffset + noisemaxmessagepropagation"
525  " + 2 * noisemaxsegmentduration not evenly divisible by the"
526  " noisebinsize");
527  }
528 
529  pSpectrumMonitor_->initialize(foi,
530  u64BandwidthHz_,
531  Utils::DB_TO_MILLIWATT(dReceiverSensitivitydBm_),
532  noiseMode_,
533  noiseBinSize_,
534  maxSegmentOffset_,
535  maxMessagePropagation_,
536  maxSegmentDuration_,
537  timeSyncThreshold_,
538  bNoiseMaxClamp_);
539 }
540 
542 {
544  DEBUG_LEVEL,
545  "PHYI %03hu FrameworkPHY::%s",
546  id_,
547  __func__);
548 }
549 
551 {
553  DEBUG_LEVEL,
554  "PHYI %03hu FrameworkPHY::%s",
555  id_,
556  __func__);
557 }
558 
560 {
562  DEBUG_LEVEL,
563  "PHYI %03hu FrameworkPHY::%s",
564  id_,
565  __func__);
566 
567 }
568 
570 {
571  for(const auto & item : update)
572  {
573  if(item.first == "fixedantennagain")
574  {
575  optionalFixedAntennaGaindBi_.first = item.second[0].asDouble();
576 
578  INFO_LEVEL,
579  "PHYI %03hu FrameworkPHY::%s: %s = %3.2f dBi",
580  id_,
581  __func__,
582  item.first.c_str(),
583  optionalFixedAntennaGaindBi_.first);
584 
585  }
586  else if(item.first == "txpower")
587  {
588  dTxPowerdBm_ = item.second[0].asDouble();
589 
591  INFO_LEVEL,
592  "PHYI %03hu FrameworkPHY::%s: %s = %3.2f dBm",
593  id_,
594  __func__,
595  item.first.c_str(),
596  dTxPowerdBm_);
597  }
598  }
599 }
600 
602 {
604  DEBUG_LEVEL,
605  "PHYI %03hu FrameworkPHY::%s",
606  id_,
607  __func__);
608 
609  for(const auto & pMessage : msgs)
610  {
611  switch(pMessage->getId())
612  {
614  {
615  const auto pAntennaProfileControlMessage =
616  reinterpret_cast<const Controls::AntennaProfileControlMessage *>(pMessage);
617 
619  Events::AntennaProfiles profiles{{id_,
620  pAntennaProfileControlMessage->getAntennaProfileId(),
621  pAntennaProfileControlMessage->getAntennaAzimuthDegrees(),
622  pAntennaProfileControlMessage->getAntennaElevationDegrees()}};
623 
624  gainManager_.update(profiles);
625 
628  }
629  break;
630 
632  {
633  const auto pFrequencyOfInterestControlMessage =
634  reinterpret_cast<const Controls::FrequencyOfInterestControlMessage *>(pMessage);
635 
637  DEBUG_LEVEL,
638  Controls::FrequencyOfInterestControlMessageFormatter(pFrequencyOfInterestControlMessage),
639  "PHYI %03hu FrameworkPHY::%s Frequency of Interest Control Message",
640  id_,
641  __func__);
642 
643  u64BandwidthHz_ = pFrequencyOfInterestControlMessage->getBandwidthHz();
644 
645  dReceiverSensitivitydBm_ =
646  THERMAL_NOISE_DB + dSystemNoiseFiguredB_ + 10.0 * log10(u64BandwidthHz_);
647 
648  pSpectrumMonitor_->initialize(pFrequencyOfInterestControlMessage->getFrequencySet(),
649  u64BandwidthHz_,
650  Utils::DB_TO_MILLIWATT(dReceiverSensitivitydBm_),
651  noiseMode_,
652  noiseBinSize_,
653  maxSegmentOffset_,
654  maxMessagePropagation_,
655  maxSegmentDuration_,
656  timeSyncThreshold_,
657  bNoiseMaxClamp_);
658 
659  }
660 
661  break;
662 
663  default:
665  DEBUG_LEVEL,
666  "PHYI %03hu FrameworkPHY::%s, unexpected contoll message, ignore",
667  id_,
668  __func__);
669  }
670  }
671 }
672 
674  const ControlMessages & msgs)
675 {
676  auto now = Clock::now();
677 
678  const PacketInfo & pktInfo{pkt.getPacketInfo()};
679 
681  DEBUG_LEVEL,
682  "PHYI %03hu FrameworkPHY::%s src %hu, dst %hu",
683  id_,
684  __func__,
685  pktInfo.getSource(),
686  pktInfo.getDestination());
687 
688  commonLayerStatistics_.processInbound(pkt);
689 
690  // use the default unless provided below
691  std::uint64_t u64BandwidthHz{u64BandwidthHz_};
692 
693  // use the default unless provided below
694  Transmitters transmitters{{id_, dTxPowerdBm_}};
695 
696  // use the default unless provided below
697  FrequencySegments frequencySegments{{u64TxFrequencyHz_,Microseconds::zero()}};
698 
699  Controls::OTATransmitters otaTransmitters;
700 
701  TimePoint txTimeStamp{now};
702 
703  for(const auto & pMessage : msgs)
704  {
705  switch(pMessage->getId())
706  {
708  {
709  const auto pTransmitterControlMessage =
710  static_cast<const Controls::TransmitterControlMessage *>(pMessage);
711 
712 
714  DEBUG_LEVEL,
715  Controls::TransmitterControlMessageFormatter(pTransmitterControlMessage),
716  "PHYI %03hu FrameworkPHY::%s Transmitter Control Message",
717  id_,
718  __func__);
719 
720  transmitters = pTransmitterControlMessage->getTransmitters();
721 
722  // build the list of OTA transmitters
723  std::for_each(transmitters.begin(),
724  transmitters.end(),
725  [&otaTransmitters](const Transmitter & transmitter)
726  {
727  otaTransmitters.insert(transmitter.getNEMId());
728  });
729  }
730 
731  break;
732 
734  {
735  const auto pFrequencyControlMessage =
736  static_cast<const Controls::FrequencyControlMessage *>(pMessage);
737 
738  // the mac will be supplying frequency segment info so lets clear the defaults
739  frequencySegments.clear();
740 
741  for(const auto & segment : pFrequencyControlMessage->getFrequencySegments())
742  {
743  // for convience a mac can set duration and offset without specifying frequency
744  // by using 0 Hz.
745  if(segment.getFrequencyHz() == 0)
746  {
747  if(segment.getPowerdBm().second)
748  {
749  frequencySegments.push_back({u64TxFrequencyHz_, // use our frequency
750  segment.getPowerdBm().first,
751  segment.getDuration(), // duration
752  segment.getOffset()}); // offset
753  }
754  else
755  {
756  frequencySegments.push_back({u64TxFrequencyHz_, // use our frequency
757  segment.getDuration(), // duration
758  segment.getOffset()}); // offset
759  }
760  }
761  else
762  {
763  frequencySegments.push_back(segment);
764  }
765  }
766 
767  // if bandwidth provided is not 0, use the provided value,
768  // otherwise keep our default value from above
769  if(pFrequencyControlMessage->getBandwidthHz() != 0)
770  {
771  u64BandwidthHz = pFrequencyControlMessage->getBandwidthHz();
772  }
773 
775  DEBUG_LEVEL,
776  Controls::FrequencyControlMessageFormatter(pFrequencyControlMessage),
777  "PHYI %03hu FrameworkPHY::%s Frequency Control Message",
778  id_,
779  __func__);
780  }
781 
782  break;
783 
785  {
786  const auto pAntennaProfileControlMessage =
787  static_cast<const Controls::AntennaProfileControlMessage *>(pMessage);
788 
790  DEBUG_LEVEL,
791  "PHYI %03hu FrameworkPHY::%s Antenna Profile Control Message "
792  "profile %hu azimuth %lf elevation %lf",
793  id_,
794  __func__,
795  pAntennaProfileControlMessage->getAntennaProfileId(),
796  pAntennaProfileControlMessage->getAntennaAzimuthDegrees(),
797  pAntennaProfileControlMessage->getAntennaElevationDegrees());
798 
800  Events::AntennaProfiles profiles{{id_,
801  pAntennaProfileControlMessage->getAntennaProfileId(),
802  pAntennaProfileControlMessage->getAntennaAzimuthDegrees(),
803  pAntennaProfileControlMessage->getAntennaElevationDegrees()}};
804 
805  gainManager_.update(profiles);
806 
807  pkt.attachEvent(0,Events::AntennaProfileEvent{profiles});
809  }
810 
811  break;
812 
814  {
815  const auto pTimestampControlMessage =
816  static_cast<const Controls::TimeStampControlMessage *>(pMessage);
817 
818 
819  txTimeStamp = pTimestampControlMessage->getTimeStamp();
820 
822  DEBUG_LEVEL,
823  "PHYI %03hu FrameworkPHY::%s Time Stamp Control Message "
824  "timestamp %.6f",
825  id_,
826  __func__,
827  std::chrono::duration_cast<DoubleSeconds>(txTimeStamp.time_since_epoch()).count());
828  }
829 
830  break;
831 
833  {
834  const auto pFrequencyOfInterestControlMessage =
835  reinterpret_cast<const Controls::FrequencyOfInterestControlMessage *>(pMessage);
836 
838  DEBUG_LEVEL,
839  Controls::FrequencyOfInterestControlMessageFormatter(pFrequencyOfInterestControlMessage),
840  "PHYI %03hu FrameworkPHY::%s Frequency of Interest Control Message",
841  id_,
842  __func__);
843 
844  u64BandwidthHz_ = pFrequencyOfInterestControlMessage->getBandwidthHz();
845 
846  dReceiverSensitivitydBm_ =
847  THERMAL_NOISE_DB + dSystemNoiseFiguredB_ + 10.0 * log10(u64BandwidthHz_);
848 
849  pSpectrumMonitor_->initialize(pFrequencyOfInterestControlMessage->getFrequencySet(),
850  u64BandwidthHz_,
851  Utils::DB_TO_MILLIWATT(dReceiverSensitivitydBm_),
852  noiseMode_,
853  noiseBinSize_,
854  maxSegmentOffset_,
855  maxMessagePropagation_,
856  maxSegmentDuration_,
857  timeSyncThreshold_,
858  bNoiseMaxClamp_);
859 
860 
861  }
862 
863  break;
864  }
865  }
866 
867 
868  // verify the transmitters list include this nem
869  if(std::find_if(transmitters.begin(),
870  transmitters.end(),
871  [this](const Transmitter & transmitter)
872  {
873  return transmitter.getNEMId() == this->id_;
874  }) == transmitters.end())
875  {
876  transmitters.push_back({id_,dTxPowerdBm_});
877  }
878 
879 
880  CommonPHYHeader phyHeader{REGISTERED_EMANE_PHY_FRAMEWORK, // phy registration id
881  u16SubId_,
882  u16TxSequenceNumber_++,
883  u64BandwidthHz,
884  txTimeStamp,
885  frequencySegments,
886  transmitters,
887  optionalFixedAntennaGaindBi_};
888 
890  DEBUG_LEVEL,
891  std::bind(&CommonPHYHeader::format, std::ref(phyHeader)),
892  "PHYI %03hu FrameworkPHY::%s Common PHY Header",
893  id_,
894  __func__);
895 
896  ControlMessages downstreamControlMessages{};
897 
898  if(!otaTransmitters.empty())
899  {
900  downstreamControlMessages.
901  push_back(Controls::OTATransmitterControlMessage::create(otaTransmitters));
902  }
903 
904  commonLayerStatistics_.processOutbound(pkt,
905  std::chrono::duration_cast<Microseconds>(Clock::now() - now));
906 
907 
908  sendDownstreamPacket(std::move(phyHeader),pkt,std::move(downstreamControlMessages));
909 }
910 
911 
913  UpstreamPacket & pkt,
914  const ControlMessages &)
915 {
916  processUpstreamPacket_i(Clock::now(),commonPHYHeader,pkt,{});
917 }
918 
920  const CommonPHYHeader & commonPHYHeader,
921  UpstreamPacket & pkt,
922  const ControlMessages &)
923 {
925  DEBUG_LEVEL,
926  std::bind(&CommonPHYHeader::format, std::ref(commonPHYHeader)),
927  "PHYI %03hu FrameworkPHY::%s Common PHY Header",
928  id_,
929  __func__);
930 
931  commonLayerStatistics_.processInbound(pkt);
932 
933  const auto & pktInfo = pkt.getPacketInfo();
934 
935  bool bInBand{commonPHYHeader.getRegistrationId() == REGISTERED_EMANE_PHY_FRAMEWORK &&
936  u16SubId_ == commonPHYHeader.getSubId()};
937 
938  // unless this is an in-band packet, it will only be processed if
939  // noise processing is on
940  if(bInBand || noiseMode_ != SpectrumMonitor::NoiseMode::NONE)
941  {
942  bool bDrop{};
943 
944  const auto & frequencySegments = commonPHYHeader.getFrequencySegments();
945 
946  std::vector<double> rxPowerSegments(frequencySegments.size(),0);
947 
948  Microseconds propagation{};
949 
950  bool bHavePropagationDelay{};
951 
952  std::vector<NEMId> transmitters{};
953 
954  for(const auto & transmitter : commonPHYHeader.getTransmitters())
955  {
956  transmitters.push_back(transmitter.getNEMId());
957 
958  // get the location info for a pair of nodes
959  auto locationPairInfoRet = locationManager_.getLocationInfo(transmitter.getNEMId());
960 
961  // get the propagation model pathloss between a pair of nodes for *each* segment
962  auto pathlossInfo = (*pPropagationModelAlgorithm_)(transmitter.getNEMId(),
963  locationPairInfoRet.first,
964  frequencySegments);
965 
966  // if pathloss is available
967  if(pathlossInfo.second)
968  {
969  // calculate the combined gain (Tx + Rx antenna gain) dBi
970  // note: gain manager accesses antenna profiles, knows self node profile info
971  // if available, and is updated with all nodes profile info
972  auto gainInfodBi = gainManager_.determineGain(transmitter.getNEMId(),
973  locationPairInfoRet.first,
974  optionalFixedAntennaGaindBi_,
975  commonPHYHeader.getOptionalFixedAntennaGaindBi());
976 
977  // if gain is available
978  if(gainInfodBi.second == EMANE::GainManager::GainStatus::SUCCESS)
979  {
980  using ReceivePowerPubisherUpdate = std::tuple<NEMId,std::uint64_t,double>;
981 
982  // set to prevent multiple ReceivePowerTablePublisher updates
983  // for the same NEM frequency pair in a frequency segment list
984  std::set<ReceivePowerPubisherUpdate> receivePowerTableUpdate{};
985 
986  // frequency segment iterator to map pathloss per segment to
987  // the associated segment
988  FrequencySegments::const_iterator freqIter{frequencySegments.begin()};
989 
990  std::size_t i{};
991 
992  // sum up the rx power for each segment
993  for(const auto & dPathlossdB : pathlossInfo.first)
994  {
995  auto optionalSegmentPowerdBm = freqIter->getPowerdBm();
996 
997  double powerdBm{(optionalSegmentPowerdBm.second ?
998  optionalSegmentPowerdBm.first :
999  transmitter.getPowerdBm()) +
1000  gainInfodBi.first -
1001  dPathlossdB};
1002 
1003  receivePowerTableUpdate.insert(ReceivePowerPubisherUpdate{transmitter.getNEMId(),
1004  freqIter->getFrequencyHz(),
1005  powerdBm});
1006 
1007  ++freqIter;
1008 
1009  rxPowerSegments[i++] += Utils::DB_TO_MILLIWATT(powerdBm);
1010  }
1011 
1012 
1013  for(const auto & entry : receivePowerTableUpdate)
1014  {
1015  receivePowerTablePublisher_.update(std::get<0>(entry),
1016  std::get<1>(entry),
1017  std::get<2>(entry),
1018  commonPHYHeader.getTxTime());
1019  }
1020 
1021 
1022  // calculate propagation delay from 1 of the transmitters
1023  // note: these are collaborative (constructive) transmissions, all
1024  // the messages are arriving at or near the same time. Destructive
1025  // transmission should be sent as multiple messages
1026  if(locationPairInfoRet.second && !bHavePropagationDelay)
1027  {
1028  if(locationPairInfoRet.first.getDistanceMeters() > 0.0)
1029  {
1030  propagation =
1031  Microseconds{static_cast<std::uint64_t>(std::round(locationPairInfoRet.first.getDistanceMeters() / SOL_MPS * 1000000))};
1032  }
1033 
1034  bHavePropagationDelay = true;
1035  }
1036  }
1037  else
1038  {
1039  // drop due to GainManager not enough info
1040  bDrop = true;
1041 
1042  std::uint16_t u16Code{};
1043 
1044  const char * pzReason{};
1045 
1046  switch(gainInfodBi.second)
1047  {
1049  pzReason = "missing location info";
1050  u16Code = DROP_CODE_GAINMANAGER_LOCATION;
1051  break;
1053  pzReason = "missing profile info";
1054  u16Code = DROP_CODE_GAINMANAGER_ANTENNAPROFILE;
1055  break;
1057  pzReason = "below the horizon";
1058  u16Code = DROP_CODE_GAINMANAGER_HORIZON;
1059  break;
1060  default:
1061  pzReason = "unknown";
1062  break;
1063  }
1064 
1065  commonLayerStatistics_.processOutbound(pkt,
1066  std::chrono::duration_cast<Microseconds>(Clock::now() - now),
1067  u16Code);
1068 
1070  DEBUG_LEVEL,
1071  "PHYI %03hu FrameworkPHY::%s transmitter %hu, src %hu, dst %hu, drop %s",
1072  id_,
1073  __func__,
1074  transmitter.getNEMId(),
1075  pktInfo.getSource(),
1076  pktInfo.getDestination(),
1077  pzReason);
1078 
1079 
1080  break;
1081  }
1082  }
1083  else
1084  {
1085  // drop due to PropagationModelAlgorithm not enough info
1086  bDrop = true;
1087 
1088  commonLayerStatistics_.processOutbound(pkt,
1089  std::chrono::duration_cast<Microseconds>(Clock::now() - now),
1090  DROP_CODE_PROPAGATIONMODEL);
1091 
1093  DEBUG_LEVEL,
1094  "PHYI %03hu FrameworkPHY::%s transmitter %hu, src %hu, dst %hu,"
1095  " drop propagation model missing info",
1096  id_,
1097  __func__,
1098  transmitter.getNEMId(),
1099  pktInfo.getSource(),
1100  pktInfo.getDestination());
1101  break;
1102  }
1103  }
1104 
1105  if(!bDrop)
1106  {
1107 
1108  // update the spectrum monitor with the signal information
1109  // note: spectrum monitor will remove any frequency segments that are
1110  // below the receiver sensitivity. Any frequency segment not in
1111  // the foi will cause the entire message to be treated as out-of-band
1112  // regardless of the subid. Spectrum monitor will adjust SoT, propagation
1113  // delay, offset and duration if out of acceptable value range.
1114  try
1115  {
1116  auto spectrumInfo = pSpectrumMonitor_->update(now,
1117  commonPHYHeader.getTxTime(),
1118  propagation,
1119  frequencySegments,
1120  commonPHYHeader.getBandwidthHz(),
1121  rxPowerSegments,
1122  bInBand,
1123  transmitters);
1124 
1125  TimePoint sot{};
1126  Microseconds propagationDelay{};
1127  Microseconds span{};
1128  FrequencySegments resultingFrequencySegments{};
1129  bool bTreatAsInBand{};
1130 
1131  std::tie(sot,
1132  propagationDelay,
1133  span,
1134  resultingFrequencySegments,
1135  bTreatAsInBand) = spectrumInfo;
1136 
1137  if(bTreatAsInBand)
1138  {
1139  if(!resultingFrequencySegments.empty())
1140  {
1141  commonLayerStatistics_.processOutbound(pkt,
1142  std::chrono::duration_cast<Microseconds>(Clock::now() - now));
1143 
1144 
1146  DEBUG_LEVEL,
1147  "PHYI %03hu FrameworkPHY::%s src %hu, dst %hu,"
1148  " pass",
1149  id_,
1150  __func__,
1151  pktInfo.getSource(),
1152  pktInfo.getDestination());
1153 
1154  if(sot != commonPHYHeader.getTxTime())
1155  {
1156  ++*pTimeSyncThresholdRewrite_;
1157  }
1158 
1160  // send to mac with associated control messages
1161  sendUpstreamPacket(pkt,
1163  resultingFrequencySegments),
1165  propagationDelay,
1166  span,
1167  dReceiverSensitivitydBm_)});
1169  }
1170  else
1171  {
1172  commonLayerStatistics_.processOutbound(pkt,
1173  std::chrono::duration_cast<Microseconds>(Clock::now() - now),
1174  DROP_CODE_RX_SENSITIVITY);
1175 
1177  DEBUG_LEVEL,
1178  "PHYI %03hu FrameworkPHY::%s src %hu, dst %hu,"
1179  " drop below receiver sensitivity",
1180  id_,
1181  __func__,
1182  pktInfo.getSource(),
1183  pktInfo.getDestination());
1184 
1185  }
1186  }
1187  else
1188  {
1189  // was this packet actually in-band, if so at least 1 freq was not in the foi
1190  if(bInBand)
1191  {
1192  commonLayerStatistics_.processOutbound(pkt,
1193  std::chrono::duration_cast<Microseconds>(Clock::now() - now),
1194  DROP_CODE_NOT_FOI);
1195 
1197  DEBUG_LEVEL,
1198  "PHYI %03hu FrameworkPHY::%s src %hu, dst %hu,"
1199  " drop one or more frequencies not in frequency of interest list",
1200  id_,
1201  __func__,
1202  pktInfo.getSource(),
1203  pktInfo.getDestination());
1204  }
1205  else
1206  {
1207  // must be subid
1208  commonLayerStatistics_.processOutbound(pkt,
1209  std::chrono::duration_cast<Microseconds>(Clock::now() - now),
1210  DROP_CODE_OUT_OF_BAND);
1211 
1213  DEBUG_LEVEL,
1214  "PHYI %03hu FrameworkPHY::%s src %hu, dst %hu,"
1215  " drop out of band",
1216  id_,
1217  __func__,
1218  pktInfo.getSource(),
1219  pktInfo.getDestination());
1220  }
1221 
1222  }
1223  }
1224  catch(SpectrumServiceException & exp)
1225  {
1226  commonLayerStatistics_.processOutbound(pkt,
1227  std::chrono::duration_cast<Microseconds>(Clock::now() - now),
1228  DROP_CODE_SPECTRUM_CLAMP);
1229 
1231  DEBUG_LEVEL,
1232  "PHYI %03hu FrameworkPHY::%s src %hu, dst %hu,"
1233  " drop spectrum out of bound %s",
1234  id_,
1235  __func__,
1236  pktInfo.getSource(),
1237  pktInfo.getDestination(),
1238  exp.what());
1239  }
1240  }
1241  }
1242  else
1243  {
1244  commonLayerStatistics_.processOutbound(pkt,
1245  std::chrono::duration_cast<Microseconds>(Clock::now() - now),
1246  DROP_CODE_OUT_OF_BAND);
1247 
1249  DEBUG_LEVEL,
1250  "PHYI %03hu FrameworkPHY::%s src %hu, dst %hu,"
1251  " drop out of band",
1252  id_,
1253  __func__,
1254  pktInfo.getSource(),
1255  pktInfo.getDestination());
1256  }
1257 }
1258 
1261  const Serialization & serialization)
1262 {
1264  DEBUG_LEVEL,
1265  "PHYI %03hu FrameworkPHY::%s event id: %hu",
1266  id_,
1267  __func__,
1268  eventId);
1269 
1270  switch(eventId)
1271  {
1273  {
1274  Events::AntennaProfileEvent antennaProfile{serialization};
1275  gainManager_.update(antennaProfile.getAntennaProfiles());
1276  eventTablePublisher_.update(antennaProfile.getAntennaProfiles());
1277 
1279  DEBUG_LEVEL,
1280  Events::AntennaProfileEventFormatter(antennaProfile),
1281  "PHYI %03hu FrameworkPHY::%s antenna profile event: ",
1282  id_,
1283  __func__);
1284 
1285  }
1286  break;
1287 
1289  {
1290  Events::LocationEvent locationEvent{serialization};
1291  locationManager_.update(locationEvent.getLocations());
1292  eventTablePublisher_.update(locationEvent.getLocations());
1293 
1295  DEBUG_LEVEL,
1296  Events::LocationEventFormatter(locationEvent),
1297  "PHYI %03hu FrameworkPHY::%s location event: ",
1298  id_,
1299  __func__);
1300  }
1301  break;
1302 
1304  {
1305  Events::PathlossEvent pathlossEvent{serialization};
1306  pPropagationModelAlgorithm_->update(pathlossEvent.getPathlosses());
1307  eventTablePublisher_.update(pathlossEvent.getPathlosses());
1308 
1310  DEBUG_LEVEL,
1311  Events::PathlossEventFormatter(pathlossEvent),
1312  "PHYI %03hu FrameworkPHY::%s pathloss event: ",
1313  id_,
1314  __func__);
1315  }
1316  break;
1317  }
1318 }
Callable formatter object for FrequencyOfInterestControlMessage instances.
std::set< NEMId > OTATransmitters
std::string Serialization
Definition: serializable.h:42
A Packet class that allows upstream processing to strip layer headers as the packet travels up the st...
const PacketInfo & getPacketInfo() const
void attachEvent(NEMId nemId, const Event &event)
The Registrar interface provides access to all of the emulator registrars.
Definition: registrar.h:50
The Transmitter Control Message is sent to the emulator physical layer to specify the NEM Id of the s...
void configure(const ConfigurationUpdate &update) override
virtual ConfigurationRegistrar & configurationRegistrar()=0
An antenna profile event is used to set the antenna profile selection and pointing information for on...
std::pair< double, GainStatus > determineGain(NEMId transmitterId, const LocationInfo &locationPairInfo, const std::pair< double, bool > &optionalRxFixedGaindBi, const std::pair< double, bool > &optionalTxFixedGaindBi) const
Definition: gainmanager.cc:85
Callable formatter object for LocationEvent instances.
static OTATransmitterControlMessage * create(const Serialization &serialization)
#define LOGGER_VERBOSE_LOGGING(logger, level, fmt, args...)
void start() override
Holds transmitter id and power level.
Definition: transmitter.h:49
void update(const Events::Locations &locations)
virtual StatisticRegistrar & statisticRegistrar()=0
A pathloss event is used to set the pathloss from one or more transmitting NEMs to a receiving NEM...
Definition: pathlossevent.h:52
void processOutbound(const UpstreamPacket &pkt, Microseconds delay, size_t dropCode={})
Interface used to create a PHY layer plugin implementation.
Definition: phylayerimpl.h:53
void destroy() override
std::list< const ControlMessage * > ControlMessages
void processUpstreamPacket(const CommonPHYHeader &hdr, UpstreamPacket &pkt, const ControlMessages &msgs) override
const std::pair< double, bool > & getOptionalFixedAntennaGaindBi() const
const double SOL_MPS
Definition: constants.h:58
void update(NEMId nemId, std::uint64_t u64Frequency, double dReceivePower, const TimePoint &rxTime)
Antenna Profile Control Message is sent to the emulator physical layer to update antenna profile sele...
SpectrumServiceException is thrown when an exception occurs during spectrum service processing...
void processEvent(const EventId &eventId, const Serialization &serialization) override
void update(const Events::Locations &locations)
void sendDownstreamPacket(const CommonPHYHeader &hdr, DownstreamPacket &pkt, const ControlMessages &msgs=DownstreamTransport::empty)
std::uint64_t getBandwidthHz() const
A location event is usd to set the position, orientation and velocity information for one or more NEM...
Definition: locationevent.h:52
std::uint16_t EventId
Definition: types.h:53
The PlatformServiceProvider interface provides access to emulator services.
virtual EventRegistrar & eventRegistrar()=0
The Frequency of Interest Control Message is sent to the emulator physical layer to specify receive f...
void registerStatistics(StatisticRegistrar &statisticRegistrar)
Store source, destination, creation time and priority information for a packet.
Definition: packetinfo.h:50
static FrequencyControlMessage * create(std::uint64_t u64BandwidthHz, const FrequencySegments &frequencySegments)
void processConfiguration(const ConfigurationUpdate &update) override
void initialize(Registrar &registrar) override
Callable formatter object for PathlossEvent instances.
std::set< std::uint64_t > FrequencySet
const TimePoint & getTxTime() const
virtual EventServiceProvider & eventService()=0
const char * what() const
Definition: exception.h:62
The Frequency Control Message is sent to the emulator physical layer to specify the frequency segment...
The common physical layer header used to facilitate heterogeneous radio model experimentation.
#define LOGGER_VERBOSE_LOGGING_FN_VARGS(logger, level, fn, fmt, args...)
std::vector< std::string > StatisticTableLabels
const PacketInfo & getPacketInfo() const
void registerStatistics(StatisticRegistrar &registrar)
Specialized packet the allows downstream processing to add layer specific headers as the packet trave...
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)
void processDownstreamPacket(DownstreamPacket &pkt, const ControlMessages &msgs) override
const RegistrationId REGISTERED_EMANE_PHY_FRAMEWORK
Definition: phytypes.h:90
const FrequencySegments & getFrequencySegments() const
void stop() override
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
std::pair< LocationInfo, bool > getLocationInfo(NEMId remoteNEMId)
PlatformServiceProvider * pPlatformService_
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)
void processUpstreamPacket_i(const TimePoint &now, const CommonPHYHeader &hdr, UpstreamPacket &pkt, const ControlMessages &msgs)
void processDownstreamControl(const ControlMessages &msgs) override
std::uint16_t getSubId() const
Callable formatter object for TransmitterControlMessage instances.
const Transmitters & getTransmitters() const
virtual LogServiceProvider & logService()=0
std::list< FrequencySegment > FrequencySegments
Callable formatter object for FrequencyControlMessage instances.
std::vector< ConfigurationNameAnyValues > ConfigurationUpdate
Callable formatter object for AntennaProfileEvent instances.
void update(const Events::AntennaProfiles &antennaProfiles)
Definition: gainmanager.cc:50
double DB_TO_MILLIWATT(double ddB)
static ReceivePropertiesControlMessage * create(const TimePoint &txTime, const Microseconds &propagation, const Microseconds &span, double dReceiverSensitivitydBm)
virtual void registerEvent(EventId eventId)=0
Time Stamp Control Message is sent to the emulator physical layer to specify the time that should be ...
void registerNumeric(const std::string &sName, const ConfigurationProperties &properties=ConfigurationProperties::NONE, const std::initializer_list< T > &values={}, const std::string &sUsage="", T minValue=std::numeric_limits< T >::lowest(), T maxValue=std::numeric_limits< T >::max(), std::size_t minOccurs=1, std::size_t maxOccurs=1, const std::string &sRegexPattern={})
Clock::time_point TimePoint
Definition: types.h:50
std::list< Transmitter > Transmitters
Definition: transmitter.h:85
const double THERMAL_NOISE_DB
Definition: constants.h:42
void sendUpstreamPacket(UpstreamPacket &pkt, const ControlMessages &msgs=empty)
#define LOGGER_STANDARD_LOGGING(logger, level, fmt, args...)
#define LOGGER_STANDARD_LOGGING_FN_VARGS(logger, level, fn, fmt, args...)
FrameworkPHY(NEMId id, PlatformServiceProvider *pPlatformService, SpectrumMonitor *pSpectrumMonitor)
Definition: frameworkphy.cc:86
virtual void sendEvent(NEMId nemId, const Event &event)=0
std::list< AntennaProfile > AntennaProfiles
void processInbound(const UpstreamPacket &pkt)
void registerStatistics(StatisticRegistrar &registrar)
RegistrationId getRegistrationId() const