EMANE  1.2.1
ieee80211abg/pcrmanager.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2014 - Adjacent Link LLC, Bridgewater, New Jersey
3  * Copyright (c) 2010-2012 - DRS CenGen, LLC, Columbia, Maryland
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 DRS CenGen, 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 
35 
36 #include "pcrmanager.h"
37 
38 #include <libxml/parser.h>
39 #include <math.h>
40 
41 #include <sstream>
42 
43 
44 namespace
45 {
46  const int PRECISION_FACTOR{100};
47 
48  inline xmlChar * toXMLChar(const char * p)
49  {
50  return reinterpret_cast<xmlChar *>(const_cast<char *>(p));
51  }
52 }
53 
54 
56 id_{id},
57 pPlatformService_{pPlatformService},
58 tablePacketSize_{}
59 { }
60 
61 
62 
64 { }
65 
66 
67 void
68 EMANE::Models::IEEE80211ABG::PCRManager::openDoc(const std::string & uri,
69  xmlParserCtxtPtr * ppContext,
70  xmlDoc ** ppDocument, xmlNode ** ppRoot)
71 {
72  if((*ppContext = xmlNewParserCtxt()) == NULL)
73  {
74  std::stringstream excString;
75  excString << "IEEE80211ABG::PCRManager::openDoc: Failed to allocate parser context" << std::ends;
76 
77  throw EMANE::ConfigurationException(excString.str());
78  }
79 
80  else if((*ppDocument = xmlCtxtReadFile(*ppContext, uri.c_str(), NULL, XML_PARSE_DTDVALID)) == NULL)
81  {
82  std::stringstream excString;
83  excString << "IEEE80211ABG::PCRManager::openDoc: Failed to parse document " << uri << std::ends;
84 
85  throw EMANE::ConfigurationException(excString.str());
86  }
87 
88  else if((*ppContext)->valid == false)
89  {
90  std::stringstream excString;
91  excString << "IEEE80211ABG::PCRManager::openDoc: Document in " << uri << " is not valid" << std::ends;
92 
93  throw EMANE::ConfigurationException(excString.str());
94  }
95 
96  else if((*ppRoot = xmlDocGetRootElement(*ppDocument)) == NULL)
97  {
98  std::stringstream excString;
99  excString << "IEEE80211ABG::PCRManager::openDoc: Could not get root element" << std::ends;
100 
101  throw EMANE::ConfigurationException(excString.str());
102  }
103 }
104 
105 
106 void
107 EMANE::Models::IEEE80211ABG::PCRManager::closeDoc(xmlParserCtxtPtr * ppContext, xmlDoc ** ppDocument)
108 {
109  if(ppContext)
110  {
111  xmlFreeParserCtxt(*ppContext);
112  *ppContext = NULL;
113  }
114 
115  if(ppDocument)
116  {
117  xmlFreeDoc(*ppDocument);
118  *ppDocument = NULL;
119  }
120 
121  xmlCleanupParser();
122 }
123 
124 
125 
126 void
128 {
129  xmlDoc * doc{};
130  xmlNode * root{};
131  xmlParserCtxtPtr pContext{};
132 
133  if(uri.empty())
134  {
135  std::stringstream excString;
136  excString << "IEEE80211ABG::PCRManager::load: must supply curve file name" << std::ends;
137 
138  throw EMANE::ConfigurationException(excString.str());
139  }
140 
141  // open doc
142  openDoc(uri, &pContext, &doc, &root);
143 
144  // root
145  xmlNodePtr cur {root};
146 
147  // clear pcr entries
148  pcrPorMap_.clear();
149 
150  // get table
151  while(cur)
152  {
153  if((!xmlStrcmp(cur->name, toXMLChar("pcr"))))
154  {
155  getTable(cur->xmlChildrenNode, pcrPorMap_);
156  }
157 
158  cur = cur->next;
159  }
160 
161  // close doc
162  closeDoc(&pContext, &doc);
163 
164  // need at least 1 point
165  for(auto & iter : pcrPorMap_)
166  {
167  if(iter.second.pcr_.size() < 1)
168  {
169  std::stringstream excString;
170  excString << "IEEE80211ABG::PCRManager::load: need at least 1 point to define a pcr curve " << std::ends;
171  throw EMANE::ConfigurationException(excString.str());
172  }
173  }
174 
175  // fill in points
176  interpolate();
177 }
178 
179 
180 
181 void
182 EMANE::Models::IEEE80211ABG::PCRManager::getTable(xmlNodePtr cur, PCRPORMap & map)
183 {
184  while(cur)
185  {
186  if((!xmlStrcmp(cur->name, toXMLChar("table"))))
187  {
188  // save table packet size
189  tablePacketSize_ = Utils::ParameterConvert(getAttribute(cur, toXMLChar("pktsize"))).toUINT32();
190 
191  // get each data rate
192  getDataRate(cur->xmlChildrenNode, map);
193  }
194 
195  cur = cur->next;
196  }
197 }
198 
199 
200 void
201 EMANE::Models::IEEE80211ABG::PCRManager::getDataRate(xmlNodePtr cur, PCRPORMap & map)
202 {
203  while(cur)
204  {
205  if((!xmlStrcmp(cur->name, toXMLChar("datarate"))))
206  {
207  // get data rate index
208  const std::uint16_t u16DataRateIndex{Utils::ParameterConvert(getAttribute(cur, toXMLChar("index"))).toUINT16()};
209 
210  // entry
211  PCRPOR entry;
212 
213  // get pcr each row
214  getRows(cur->xmlChildrenNode, entry.pcr_);
215 
216  if(map.insert(std::make_pair(u16DataRateIndex, entry)).second == false)
217  {
218  std::stringstream excString;
219  excString << "IEEE80211ABG::PCRManager::getDataRate: duplicate datarate index value " << u16DataRateIndex << std::ends;
220  throw EMANE::ConfigurationException(excString.str());
221  }
222  }
223 
224  cur = cur->next;
225  }
226 }
227 
228 
229 void
230 EMANE::Models::IEEE80211ABG::PCRManager::getRows(xmlNodePtr cur, PCREntryVector & vec)
231 {
232  while(cur)
233  {
234  if((!xmlStrcmp(cur->name, toXMLChar("row"))))
235  {
236  // get sinr value
237  const float fSINR{Utils::ParameterConvert(getAttribute(cur, toXMLChar("sinr"))).toFloat(-255.0f, 255.0f)};
238 
239  // get por value, convert from percent to fraction
240  const float
241  fPOR{Utils::ParameterConvert(getAttribute(cur, toXMLChar("por"))).toFloat(0.0f, 100.0f) / 100.0f};
242 
243  for(const auto & iter : vec)
244  {
245  if(fSINR == iter.fSINR_)
246  {
247  std::stringstream excString;
248  excString << "IEEE80211ABG::PCRManager::getRows: duplicate sinr value " << fSINR << std::ends;
249  throw EMANE::ConfigurationException(excString.str());
250  }
251  else if(fSINR < iter.fSINR_)
252  {
253  std::stringstream excString;
254  excString << "IEEE80211ABG::PCRManager::getRows: out of order sinr value, must be in increasing value "
255  << fSINR << std::ends;
256  throw EMANE::ConfigurationException(excString.str());
257  }
258  }
259 
260  // append entry
261  vec.push_back(PCREntry(fSINR, fPOR));
262  }
263 
264  cur = cur->next;
265  }
266 }
267 
268 
269 void
270 EMANE::Models::IEEE80211ABG::PCRManager::interpolate()
271 {
272  // for each datarate
273  for(auto & iter : pcrPorMap_)
274  {
275  // for each sinr/pcr entry
276  for(size_t i = 0; i < (iter.second.pcr_.size() - 1); ++i)
277  {
278  // x1
279  const int x1{static_cast<int>(iter.second.pcr_[i].fSINR_ * PRECISION_FACTOR)};
280 
281  // y1
282  const float y1{iter.second.pcr_[i].fPOR_};
283 
284  // x2
285  const int x2{static_cast<int>(iter.second.pcr_[i + 1].fSINR_ * PRECISION_FACTOR)};
286 
287  // y2
288  const float y2{iter.second.pcr_[i + 1].fPOR_};
289 
290  // slope(m)
291  const float slope{(y2 - y1) / (x2 - x1)};
292 
293  // fill in between points
294  for(int dx = x1; dx < x2; ++dx)
295  {
296  // y = mx + b
297  const float por{(dx - x1) * slope + y1};
298 
299  // save value to end of vector
300  iter.second.por_.push_back(por);
301  }
302  }
303 
304  iter.second.por_.push_back(iter.second.pcr_[iter.second.pcr_.size() - 1].fPOR_);
305  }
306 }
307 
308 
309 
310 
311 float
312 EMANE::Models::IEEE80211ABG::PCRManager::getPCR(float fSINR, size_t packetLen, std::uint16_t u16DataRateIndex)
313 {
314  const auto iter = pcrPorMap_.find(u16DataRateIndex);
315 
316  if(iter != pcrPorMap_.end())
317  {
318  // check low end
319  if(fSINR < iter->second.pcr_.front().fSINR_)
320  {
321  LOGGER_VERBOSE_LOGGING(pPlatformService_->logService(),
322  DEBUG_LEVEL,
323  "MACI %03hu PCRManager::%s: sinr %3.2f, for datarate index %hu, "
324  "is below the low limit sinr of %3.2f",
325  id_,
326  __func__,
327  fSINR,
328  u16DataRateIndex,
329  iter->second.pcr_.front().fSINR_);
330 
331  // full loss
332  return 0.0f;
333  }
334  // check high end
335  else if(fSINR > iter->second.pcr_.back().fSINR_)
336  {
337  LOGGER_VERBOSE_LOGGING(pPlatformService_->logService(),
338  DEBUG_LEVEL,
339  "MACI %03hu PCRManager::%s: sinr %3.2f, for datarate index %hu, "
340  "is above the high limit sinr of %3.2f",
341  id_,
342  __func__,
343  fSINR,
344  u16DataRateIndex,
345  iter->second.pcr_.back().fSINR_);
346  // no loss
347  return 1.0f;
348  }
349  // lookup
350  else
351  {
352  float fPOR{};
353 
354  // single point(front == back)
355  if(fSINR == iter->second.pcr_.front().fSINR_ && fSINR == iter->second.pcr_.back().fSINR_)
356  {
357  // get por
358  fPOR = iter->second.pcr_.front().fPOR_;
359  }
360  else
361  {
362  // sinr adjusted
363  const int sinrScaled{static_cast<int>(fSINR * PRECISION_FACTOR)};
364 
365  // sinr offset from the front
366  const int sinrOffset{static_cast<int>(iter->second.pcr_.front().fSINR_ * PRECISION_FACTOR)};
367 
368  // get the table index
369  int idx{sinrScaled - sinrOffset};
370 
371  // cap max index, low end check covers min index
372  if(idx >= static_cast<int>(iter->second.por_.size()))
373  {
374  idx = iter->second.por_.size() - 1;
375  }
376 
377  // direct por lookup
378  fPOR = iter->second.por_[idx];
379 
380  LOGGER_VERBOSE_LOGGING(pPlatformService_->logService(),
381  DEBUG_LEVEL,
382  "MACI %03hu PCRManager::%s: lookup por %3.2f at table index %d",
383  id_,
384  __func__,
385  fPOR,
386  idx);
387  }
388 
389  // adjust por for packet length
390  if(tablePacketSize_ > 0)
391  {
392  fPOR = powf(fPOR, static_cast<float>(packetLen) / tablePacketSize_);
393  }
394 
395  LOGGER_VERBOSE_LOGGING(pPlatformService_->logService(),
396  DEBUG_LEVEL,
397  "MACI %03hu PCRManager::%s: sinr %3.2f, for datarate index %hu, por %3.2f",
398  id_,
399  __func__,
400  fSINR,
401  u16DataRateIndex,
402  fPOR);
403 
404  // return por
405  return fPOR;
406  }
407  }
408  else
409  {
410  LOGGER_VERBOSE_LOGGING(pPlatformService_->logService(),
411  DEBUG_LEVEL,
412  "MACI %03hu PCRManager::%s: unsupported datarate index %hu, por %3.2f",
413  id_,
414  __func__,
415  u16DataRateIndex,
416  0.0f);
417 
418  // full loss
419  return 0.0;
420  }
421 }
422 
423 
424 
425 
426 std::string EMANE::Models::IEEE80211ABG::PCRManager::getAttribute(xmlNodePtr cur, const xmlChar * id)
427 {
428  std::string str{"0"};
429 
430  if(id)
431  {
432  xmlChar * attr {xmlGetProp(cur, id)};
433 
434  if(attr)
435  {
436  str = reinterpret_cast<const char *>(attr);
437  }
438 
439  xmlFree(attr);
440  }
441 
442  return str;
443 }
444 
445 
446 
447 std::string EMANE::Models::IEEE80211ABG::PCRManager::getContent(xmlNodePtr cur)
448 {
449  std::string str{"0"};
450 
451  xmlChar * attr{xmlNodeGetContent(cur)};
452 
453  if(attr)
454  {
455  str = reinterpret_cast<const char *>(attr);
456  }
457 
458  xmlFree(attr);
459 
460  return str;
461 }
#define LOGGER_VERBOSE_LOGGING(logger, level, fmt, args...)
ConfigurationException is thrown when an exception occurs during configuration processing.
std::uint16_t toUINT16(std::uint16_t u16Min=std::numeric_limits< std::uint16_t >::min(), std::uint16_t u16Max=std::numeric_limits< std::uint16_t >::max()) const
float toFloat(float fMin=std::numeric_limits< float >::lowest(), float fMax=std::numeric_limits< float >::max()) const
std::uint32_t toUINT32(std::uint32_t u32Min=std::numeric_limits< std::uint32_t >::min(), std::uint32_t u32Max=std::numeric_limits< std::uint32_t >::max()) const
The PlatformServiceProvider interface provides access to emulator services.
PCRManager(NEMId id, PlatformServiceProvider *pPlatformService)
std::uint16_t NEMId
Definition: types.h:52
virtual LogServiceProvider & logService()=0
float getPCR(float fSinr, size_t size, std::uint16_t DataRateIndex)
Parameter conversion class with range checks.