EMANE  1.0.1
rfpipe/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 - 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 
34 #include "pcrmanager.h"
36 
37 #include <libxml/parser.h>
38 
39 #include <sstream>
40 
41 #include <cmath>
42 
43 namespace
44 {
45  const int PRECISION_FACTOR = 100;
46 
47  xmlChar* toXmlChar(const char * arg)
48  {
49  return reinterpret_cast<xmlChar*>(const_cast<char *>(arg));
50  }
51 }
52 
53 
55  id_{id},
56  pPlatformService_{pPlatformService},
57  tablePacketSize_{}
58 {}
59 
60 
61 
63 {}
64 
65 
66 void
67 EMANE::Models::RFPipe::PCRManager::Open(const std::string & uri, xmlParserCtxtPtr * ppContext, xmlDoc ** ppDocument, xmlNode ** ppRoot)
68 {
69  if((*ppContext = xmlNewParserCtxt()) == NULL)
70  {
71  std::stringstream excString;
72  excString << "EMANE::Models::RFPipe::PCRManager::Open: Failed to allocate parser context" << std::ends;
73 
74  throw StartException(excString.str());
75  }
76  else if((*ppDocument = xmlCtxtReadFile(*ppContext, uri.c_str(), NULL, XML_PARSE_DTDVALID)) == NULL)
77  {
78  std::stringstream excString;
79  excString << "EMANE::Models::RFPipe::PCRManager::Open: Failed to parse document " << uri << std::ends;
80 
81  throw StartException(excString.str());
82  }
83  else if((*ppContext)->valid == false)
84  {
85  std::stringstream excString;
86  excString << "EMANE::Models::RFPipe::PCRManager::Open: Document in " << uri << " is not valid" << std::ends;
87 
88  throw StartException(excString.str());
89  }
90  else if((*ppRoot = xmlDocGetRootElement(*ppDocument)) == NULL)
91  {
92  std::stringstream excString;
93  excString << "EMANE::Models::RFPipe::PCRManager::Open: Could not get root element" << std::ends;
94 
95  throw StartException(excString.str());
96  }
97 }
98 
99 
100 void
101 EMANE::Models::RFPipe::PCRManager::Close(xmlParserCtxtPtr * ppContext, xmlDoc ** ppDocument)
102 {
103  if(ppContext)
104  {
105  xmlFreeParserCtxt(*ppContext);
106  *ppContext = NULL;
107  }
108 
109  if(ppDocument)
110  {
111  xmlFreeDoc(*ppDocument);
112  *ppDocument = NULL;
113  }
114 
115  xmlCleanupParser();
116 }
117 
118 
119 
120 void EMANE::Models::RFPipe::PCRManager::load(const std::string & uri)
121  throw(StartException)
122 {
123  xmlDoc * doc{};
124  xmlNode * root{};
125  xmlParserCtxtPtr pContext{};
126 
127  if(uri.empty())
128  {
129  std::stringstream excString;
130  excString << "EMANE::Models::RFPipe::PCRManager::load: must supply curve file name" << std::ends;
131 
132  throw StartException(excString.str());
133  }
134 
135  // open doc
136  Open(uri, &pContext, &doc, &root);
137 
138  // root
139  xmlNodePtr cur{root};
140 
141  // clear pcr entries
142  pcrEntryVector_.clear();
143 
144  // clear por entries
145  porVector_.clear();
146 
147  // get table
148  while(cur)
149  {
150  if((!xmlStrcmp(cur->name, toXmlChar("pcr"))))
151  {
152  getTable(cur->xmlChildrenNode);
153  }
154 
155  cur = cur->next;
156  }
157 
158  // close doc
159  Close(&pContext, &doc);
160 
161  // need at least 1 point
162  if(pcrEntryVector_.size() < 1)
163  {
164  std::stringstream excString;
165  excString << "EMANE::Models::RFPipe::PCRManager::getRows: need at least 1 point to define a pcr curve " << std::ends;
166 
167  throw StartException(excString.str());
168  }
169 
170  // fill in points
171  interpolate();
172 }
173 
174 
175 
176 void
177 EMANE::Models::RFPipe::PCRManager::getTable(xmlNodePtr cur)
178 {
179  while(cur)
180  {
181  if((!xmlStrcmp(cur->name, toXmlChar("table"))))
182  {
183  tablePacketSize_ = Utils::ParameterConvert(getAttribute(cur, toXmlChar("pktsize"))).toUINT32();
184 
185  getRows(cur->xmlChildrenNode);
186  }
187 
188  cur = cur->next;
189  }
190 }
191 
192 
193 
194 void
195 EMANE::Models::RFPipe::PCRManager::getRows(xmlNodePtr cur)
196 {
197  while(cur)
198  {
199  if((!xmlStrcmp(cur->name, toXmlChar("row"))))
200  {
201  // get sinr value
202  const float fSINR{Utils::ParameterConvert(getAttribute(cur, toXmlChar("sinr"))).toFloat(-255.0f, 255.0f)};
203 
204  // get por value, convert from percent to fraction
205  const float fPOR{Utils::ParameterConvert(getAttribute(cur, toXmlChar("por"))).toFloat(0.0f, 100.0f) / 100.0f};
206 
207  for(const auto & entry : pcrEntryVector_)
208  {
209  if(fSINR == entry.fSINR_)
210  {
211  std::stringstream excString;
212  excString << "EMANE::Models::RFPipe::PCRManager::getRows: duplicate sinr value " << fSINR << std::ends;
213  throw StartException(excString.str());
214  }
215  else if(fSINR < entry.fSINR_)
216  {
217  std::stringstream excString;
218  excString << "EMANE::Models::RFPipe::PCRManager::getRows: out of order sinr value, must be in increasing value " << fSINR << std::ends;
219  throw StartException(excString.str());
220  }
221  }
222 
223  pcrEntryVector_.push_back(EMANE::Models::RFPipe::PCRManager::PCREntry(fSINR, fPOR));
224  }
225 
226  cur = cur->next;
227  }
228 }
229 
230 
231 void
232 EMANE::Models::RFPipe::PCRManager::interpolate()
233 {
234  // for each sinr/pcr entry
235  for(size_t i = 0; i < (pcrEntryVector_.size() - 1); ++i)
236  {
237  // x1
238  const int x1{static_cast<int>(pcrEntryVector_[i].fSINR_ * PRECISION_FACTOR)};
239 
240  // y1
241  const float y1{pcrEntryVector_[i].fPOR_};
242 
243  // x2
244  const int x2{static_cast<int>(pcrEntryVector_[i + 1].fSINR_ * PRECISION_FACTOR)};
245 
246  // y2
247  const float y2{pcrEntryVector_[i + 1].fPOR_};
248 
249  // slope
250  const float slope{(y2 - y1) / (x2 - x1)};
251 
252  // fill in between points
253  for(int dx = x1; dx < x2; ++dx)
254  {
255  // y = mx + b
256  porVector_.push_back((dx - x1) * slope + y1);
257  }
258  }
259 
260  porVector_.push_back(pcrEntryVector_[pcrEntryVector_.size()-1].fPOR_);
261 }
262 
263 
264 float
265 EMANE::Models::RFPipe::PCRManager::getPCR(float fSINR, size_t packetLen)
266 {
267  // check low end
268  if(fSINR < pcrEntryVector_.front().fSINR_)
269  {
270  // full loss
271  return 0.0f;
272  }
273  // check high end
274  else if(fSINR > pcrEntryVector_.back().fSINR_)
275  {
276  // no loss
277  return 1.0f;
278  }
279  // lookup
280  else
281  {
282  // por value
283  float fPOR{};
284 
285  // single point
286  if(fSINR == pcrEntryVector_.front().fSINR_ && fSINR == pcrEntryVector_.back().fSINR_)
287  {
288  fPOR = pcrEntryVector_.front().fPOR_;
289  }
290  else
291  {
292  // sinr adjusted
293  const int sinrScaled{static_cast<int>(fSINR * PRECISION_FACTOR)};
294 
295  // sinr offset from the front
296  const int sinrOffset{static_cast<int>(pcrEntryVector_.front().fSINR_ * PRECISION_FACTOR)};
297 
298  // get the table index
299  int idx{sinrScaled - sinrOffset};
300 
301  // cap max index, low end check covers min index
302  if(idx >= static_cast<int>(porVector_.size()))
303  {
304  idx = porVector_.size() - 1;
305  }
306 
307  // direct lookup
308  fPOR = porVector_[idx];
309  }
310 
311  // adjust for pkt size is given in the table
312  if(tablePacketSize_ > 0)
313  {
314  fPOR = pow(fPOR,(float) packetLen /(float) tablePacketSize_);
315  }
316 
317  LOGGER_VERBOSE_LOGGING(pPlatformService_->logService(),
318  DEBUG_LEVEL,
319  "MACI %03hu PCRManager::%s: sinr %3.2f, len %zu, por %3.2f",
320  id_,
321  __func__,
322  fSINR,
323  packetLen,
324  fPOR);
325  return fPOR;
326  }
327 }
328 
329 
330 
331 
332 std::string
333 EMANE::Models::RFPipe::PCRManager::getAttribute(xmlNodePtr cur, const xmlChar * id)
334 {
335  std::string str{"0"};
336 
337  if(id)
338  {
339  xmlChar *attr{xmlGetProp(cur, id)};
340 
341  if(attr)
342  {
343  str = reinterpret_cast<const char *>(attr);
344  }
345  xmlFree(attr);
346  }
347 
348  return str;
349 }
350 
351 
352 
353 std::string
354 EMANE::Models::RFPipe::PCRManager::getContent(xmlNodePtr cur)
355 {
356  std::string str{"0"};
357 
358  xmlChar *attr{xmlNodeGetContent(cur)};
359 
360  if(attr)
361  {
362  str = reinterpret_cast<const char *>(attr);
363  }
364 
365  xmlFree(attr);
366 
367  return str;
368 }
PCRManager(EMANE::NEMId id, EMANE::PlatformServiceProvider *pPlatformService)
#define LOGGER_VERBOSE_LOGGING(logger, level, fmt, args...)
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
float getPCR(float fSinr, size_t size)
The PlatformServiceProvider interface provides access to emulator services.
std::uint16_t NEMId
Definition: types.h:52
virtual LogServiceProvider & logService()=0
Component start exception is used to indicate an exception during transition to the start state...
Parameter conversion class with range checks.
void load(const std::string &uri)