EMANE  1.2.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 {
122  xmlDoc * doc{};
123  xmlNode * root{};
124  xmlParserCtxtPtr pContext{};
125 
126  if(uri.empty())
127  {
128  std::stringstream excString;
129  excString << "EMANE::Models::RFPipe::PCRManager::load: must supply curve file name" << std::ends;
130 
131  throw StartException(excString.str());
132  }
133 
134  // open doc
135  Open(uri, &pContext, &doc, &root);
136 
137  // root
138  xmlNodePtr cur{root};
139 
140  // clear pcr entries
141  pcrEntryVector_.clear();
142 
143  // clear por entries
144  porVector_.clear();
145 
146  // get table
147  while(cur)
148  {
149  if((!xmlStrcmp(cur->name, toXmlChar("pcr"))))
150  {
151  getTable(cur->xmlChildrenNode);
152  }
153 
154  cur = cur->next;
155  }
156 
157  // close doc
158  Close(&pContext, &doc);
159 
160  // need at least 1 point
161  if(pcrEntryVector_.size() < 1)
162  {
163  std::stringstream excString;
164  excString << "EMANE::Models::RFPipe::PCRManager::getRows: need at least 1 point to define a pcr curve " << std::ends;
165 
166  throw StartException(excString.str());
167  }
168 
169  // fill in points
170  interpolate();
171 }
172 
173 
174 
175 void
176 EMANE::Models::RFPipe::PCRManager::getTable(xmlNodePtr cur)
177 {
178  while(cur)
179  {
180  if((!xmlStrcmp(cur->name, toXmlChar("table"))))
181  {
182  tablePacketSize_ = Utils::ParameterConvert(getAttribute(cur, toXmlChar("pktsize"))).toUINT32();
183 
184  getRows(cur->xmlChildrenNode);
185  }
186 
187  cur = cur->next;
188  }
189 }
190 
191 
192 
193 void
194 EMANE::Models::RFPipe::PCRManager::getRows(xmlNodePtr cur)
195 {
196  while(cur)
197  {
198  if((!xmlStrcmp(cur->name, toXmlChar("row"))))
199  {
200  // get sinr value
201  const float fSINR{Utils::ParameterConvert(getAttribute(cur, toXmlChar("sinr"))).toFloat(-255.0f, 255.0f)};
202 
203  // get por value, convert from percent to fraction
204  const float fPOR{Utils::ParameterConvert(getAttribute(cur, toXmlChar("por"))).toFloat(0.0f, 100.0f) / 100.0f};
205 
206  for(const auto & entry : pcrEntryVector_)
207  {
208  if(fSINR == entry.fSINR_)
209  {
210  std::stringstream excString;
211  excString << "EMANE::Models::RFPipe::PCRManager::getRows: duplicate sinr value " << fSINR << std::ends;
212  throw StartException(excString.str());
213  }
214  else if(fSINR < entry.fSINR_)
215  {
216  std::stringstream excString;
217  excString << "EMANE::Models::RFPipe::PCRManager::getRows: out of order sinr value, must be in increasing value " << fSINR << std::ends;
218  throw StartException(excString.str());
219  }
220  }
221 
222  pcrEntryVector_.push_back(EMANE::Models::RFPipe::PCRManager::PCREntry(fSINR, fPOR));
223  }
224 
225  cur = cur->next;
226  }
227 }
228 
229 
230 void
231 EMANE::Models::RFPipe::PCRManager::interpolate()
232 {
233  // for each sinr/pcr entry
234  for(size_t i = 0; i < (pcrEntryVector_.size() - 1); ++i)
235  {
236  // x1
237  const int x1{static_cast<int>(pcrEntryVector_[i].fSINR_ * PRECISION_FACTOR)};
238 
239  // y1
240  const float y1{pcrEntryVector_[i].fPOR_};
241 
242  // x2
243  const int x2{static_cast<int>(pcrEntryVector_[i + 1].fSINR_ * PRECISION_FACTOR)};
244 
245  // y2
246  const float y2{pcrEntryVector_[i + 1].fPOR_};
247 
248  // slope
249  const float slope{(y2 - y1) / (x2 - x1)};
250 
251  // fill in between points
252  for(int dx = x1; dx < x2; ++dx)
253  {
254  // y = mx + b
255  porVector_.push_back((dx - x1) * slope + y1);
256  }
257  }
258 
259  porVector_.push_back(pcrEntryVector_[pcrEntryVector_.size()-1].fPOR_);
260 }
261 
262 
263 float
264 EMANE::Models::RFPipe::PCRManager::getPCR(float fSINR, size_t packetLen)
265 {
266  // check low end
267  if(fSINR < pcrEntryVector_.front().fSINR_)
268  {
269  // full loss
270  return 0.0f;
271  }
272  // check high end
273  else if(fSINR > pcrEntryVector_.back().fSINR_)
274  {
275  // no loss
276  return 1.0f;
277  }
278  // lookup
279  else
280  {
281  // por value
282  float fPOR{};
283 
284  // single point
285  if(fSINR == pcrEntryVector_.front().fSINR_ && fSINR == pcrEntryVector_.back().fSINR_)
286  {
287  fPOR = pcrEntryVector_.front().fPOR_;
288  }
289  else
290  {
291  // sinr adjusted
292  const int sinrScaled{static_cast<int>(fSINR * PRECISION_FACTOR)};
293 
294  // sinr offset from the front
295  const int sinrOffset{static_cast<int>(pcrEntryVector_.front().fSINR_ * PRECISION_FACTOR)};
296 
297  // get the table index
298  int idx{sinrScaled - sinrOffset};
299 
300  // cap max index, low end check covers min index
301  if(idx >= static_cast<int>(porVector_.size()))
302  {
303  idx = porVector_.size() - 1;
304  }
305 
306  // direct lookup
307  fPOR = porVector_[idx];
308  }
309 
310  // adjust for pkt size is given in the table
311  if(tablePacketSize_ > 0)
312  {
313  fPOR = pow(fPOR,(float) packetLen /(float) tablePacketSize_);
314  }
315 
316  LOGGER_VERBOSE_LOGGING(pPlatformService_->logService(),
317  DEBUG_LEVEL,
318  "MACI %03hu PCRManager::%s: sinr %3.2f, len %zu, por %3.2f",
319  id_,
320  __func__,
321  fSINR,
322  packetLen,
323  fPOR);
324  return fPOR;
325  }
326 }
327 
328 
329 
330 
331 std::string
332 EMANE::Models::RFPipe::PCRManager::getAttribute(xmlNodePtr cur, const xmlChar * id)
333 {
334  std::string str{"0"};
335 
336  if(id)
337  {
338  xmlChar *attr{xmlGetProp(cur, id)};
339 
340  if(attr)
341  {
342  str = reinterpret_cast<const char *>(attr);
343  }
344  xmlFree(attr);
345  }
346 
347  return str;
348 }
349 
350 
351 
352 std::string
353 EMANE::Models::RFPipe::PCRManager::getContent(xmlNodePtr cur)
354 {
355  std::string str{"0"};
356 
357  xmlChar *attr{xmlNodeGetContent(cur)};
358 
359  if(attr)
360  {
361  str = reinterpret_cast<const char *>(attr);
362  }
363 
364  xmlFree(attr);
365 
366  return str;
367 }
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)