JSBSim Flight Dynamics Model  1.2.0 (05 Nov 2023)
An Open Source Flight Dynamics and Control Software Library in C++
FGTable.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGTable.cpp
4  Author: Jon S. Berndt
5  Date started: 1/9/2001
6  Purpose: Models a lookup table
7 
8  ------------- Copyright (C) 2001 Jon S. Berndt (jon@jsbsim.org) -------------
9 
10  This program is free software; you can redistribute it and/or modify it under
11  the terms of the GNU Lesser General Public License as published by the Free
12  Software Foundation; either version 2 of the License, or (at your option) any
13  later version.
14 
15  This program is distributed in the hope that it will be useful, but WITHOUT
16  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18  details.
19 
20  You should have received a copy of the GNU Lesser General Public License along
21  with this program; if not, write to the Free Software Foundation, Inc., 59
22  Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24  Further information about the GNU Lesser General Public License can also be
25  found on the world wide web at http://www.gnu.org.
26 
27 FUNCTIONAL DESCRIPTION
28 --------------------------------------------------------------------------------
29 Models a lookup table
30 
31 HISTORY
32 --------------------------------------------------------------------------------
33 JSB 1/9/00 Created
34 
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 INCLUDES
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
38 
39 #include <limits>
40 #include <assert.h>
41 
42 #include "FGTable.h"
43 #include "input_output/FGXMLElement.h"
44 
45 using namespace std;
46 
47 namespace JSBSim {
48 
49 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50 CLASS IMPLEMENTATION
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
52 
53 FGTable::FGTable(int NRows)
54  : nRows(NRows), nCols(1)
55 {
56  Type = tt1D;
57  // Fill unused elements with NaNs to detect illegal access.
58  Data.push_back(std::numeric_limits<double>::quiet_NaN());
59  Data.push_back(std::numeric_limits<double>::quiet_NaN());
60  Debug(0);
61 }
62 
63 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64 
65 FGTable::FGTable(int NRows, int NCols)
66  : nRows(NRows), nCols(NCols)
67 {
68  Type = tt2D;
69  // Fill unused elements with NaNs to detect illegal access.
70  Data.push_back(std::numeric_limits<double>::quiet_NaN());
71  Debug(0);
72 }
73 
74 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
75 
76 FGTable::FGTable(const FGTable& t)
77  : PropertyManager(t.PropertyManager)
78 {
79  Type = t.Type;
80  nRows = t.nRows;
81  nCols = t.nCols;
82  internal = t.internal;
83  Name = t.Name;
84  lookupProperty[0] = t.lookupProperty[0];
85  lookupProperty[1] = t.lookupProperty[1];
86  lookupProperty[2] = t.lookupProperty[2];
87 
88  // Deep copy of t.Tables
89  Tables.reserve(t.Tables.size());
90  for(const auto &t: t.Tables)
91  Tables.push_back(std::make_unique<FGTable>(*t));
92 
93  Data = t.Data;
94 }
95 
96 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97 
98 unsigned int FindNumColumns(const string& test_line)
99 {
100  // determine number of data columns in table (first column is row lookup - don't count)
101  size_t position=0;
102  unsigned int nCols=0;
103  while ((position = test_line.find_first_not_of(" \t", position)) != string::npos) {
104  nCols++;
105  position = test_line.find_first_of(" \t", position);
106  }
107  return nCols;
108 }
109 
110 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111 
112 FGTable::FGTable(std::shared_ptr<FGPropertyManager> pm, Element* el,
113  const std::string& Prefix)
114  : PropertyManager(pm)
115 {
116  string brkpt_string;
117  Element *tableData = nullptr;
118 
119  // Is this an internal lookup table?
120 
121  Name = el->GetAttributeValue("name"); // Allow this table to be named with a property
122  string call_type = el->GetAttributeValue("type");
123  if (call_type == "internal") {
124  internal = true;
125  } else if (!call_type.empty()) {
126  std::cerr << el->ReadFrom()
127  <<" An unknown table type attribute is listed: " << call_type
128  << endl;
129  throw BaseException("Unknown table type.");
130  }
131 
132  // Determine and store the lookup properties for this table unless this table
133  // is part of a 3D table, in which case its independentVar property indexes
134  // will be set by a call from the owning table during creation
135 
136  unsigned int dimension = 0;
137 
138  Element* axisElement = el->FindElement("independentVar");
139  if (axisElement) {
140 
141  // The 'internal' attribute of the table element cannot be specified
142  // at the same time that independentVars are specified.
143  if (internal) {
144  cerr << el->ReadFrom()
145  << fgred << " This table specifies both 'internal' call type" << endl
146  << " and specific lookup properties via the 'independentVar' element." << endl
147  << " These are mutually exclusive specifications. The 'internal'" << endl
148  << " attribute will be ignored." << fgdef << endl << endl;
149  internal = false;
150  }
151 
152  while (axisElement) {
153  string property_string = axisElement->GetDataLine();
154  if (property_string.find("#") != string::npos) {
155  if (is_number(Prefix)) {
156  property_string = replace(property_string,"#",Prefix);
157  }
158  }
159 
160  FGPropertyValue_ptr node = new FGPropertyValue(property_string,
161  PropertyManager, axisElement);
162  string lookup_axis = axisElement->GetAttributeValue("lookup");
163  if (lookup_axis == string("row")) {
164  lookupProperty[eRow] = node;
165  dimension = std::max(dimension, 1u);
166  } else if (lookup_axis == string("column")) {
167  lookupProperty[eColumn] = node;
168  dimension = std::max(dimension, 2u);
169  } else if (lookup_axis == string("table")) {
170  lookupProperty[eTable] = node;
171  dimension = std::max(dimension, 3u);
172  } else if (!lookup_axis.empty()) {
173  throw BaseException("Lookup table axis specification not understood: " + lookup_axis);
174  } else { // assumed single dimension table; row lookup
175  lookupProperty[eRow] = node;
176  dimension = std::max(dimension, 1u);
177  }
178  axisElement = el->FindNextElement("independentVar");
179  }
180 
181  } else if (internal) { // This table is an internal table
182 
183  // determine how many rows, columns, and tables in this table (dimension).
184 
185  if (el->GetNumElements("tableData") > 1) {
186  dimension = 3; // this is a 3D table
187  } else {
188  tableData = el->FindElement("tableData");
189  if (tableData) {
190  unsigned int nLines = tableData->GetNumDataLines();
191  unsigned int nColumns = FindNumColumns(tableData->GetDataLine(0));
192  if (nLines > 1) {
193  unsigned int nColumns1 = FindNumColumns(tableData->GetDataLine(1));
194  if (nColumns1 == nColumns + 1) {
195  dimension = 2;
196  nColumns = nColumns1;
197  }
198  else
199  dimension = 1;
200 
201  // Check that every line (but the header line) has the same number of
202  // columns.
203  for(unsigned int i=1; i<nLines; ++i) {
204  if (FindNumColumns(tableData->GetDataLine(i)) != nColumns) {
205  std::cerr << tableData->ReadFrom()
206  << "Invalid number of columns in line "
207  << tableData->GetLineNumber()+i << endl;
208  throw BaseException("Invalid number of columns in table");
209  }
210  }
211  }
212  else
213  dimension = 1;
214 
215  if (dimension == 1 && nColumns != 2) {
216  std::cerr << tableData->ReadFrom()
217  << "Too many columns for a 1D table" << endl;
218  throw BaseException("Too many columns for a 1D table");
219  }
220  }
221  }
222 
223  } else {
224  brkpt_string = el->GetAttributeValue("breakPoint");
225  if (brkpt_string.empty()) {
226  // no independentVars found, and table is not marked as internal, nor is it
227  // a 3D table
228  std::cerr << el->ReadFrom()
229  << "No independentVars found, and table is not marked as internal,"
230  << " nor is it a 3D table." << endl;
231  throw BaseException("No independent variable found for table.");
232  }
233  }
234  // end lookup property code
235 
236  if (brkpt_string.empty()) { // Not a 3D table "table element"
237  // Force the dimension to 3 if there are several instances of <tableData>.
238  // This is needed for sanity checks.
239  if (el->GetNumElements("tableData") > 1) dimension = 3;
240  tableData = el->FindElement("tableData");
241  } else { // This is a table in a 3D table
242  tableData = el;
243  dimension = 2; // Currently, infers 2D table
244  }
245 
246  if (!tableData) {
247  std::cerr << el->ReadFrom()
248  << "FGTable: <tableData> elements are missing" << endl;
249  throw BaseException("FGTable: <tableData> elements are missing");
250  }
251  else if (tableData->GetNumDataLines() == 0) {
252  std::cerr << tableData->ReadFrom() << "<tableData> is empty." << endl;
253  throw BaseException("<tableData> is empty.");
254  }
255 
256  // Check that the lookup axes match the declared dimension of the table.
257  if (!internal && brkpt_string.empty()) {
258  switch (dimension) {
259  case 3u:
260  if (!lookupProperty[eTable]) {
261  std::cerr << el->ReadFrom()
262  << "FGTable: missing lookup axis \"table\"";
263  throw BaseException("FGTable: missing lookup axis \"table\"");
264  }
265  // Don't break as we want to investigate the other lookup axes as well.
266  case 2u:
267  if (!lookupProperty[eColumn]) {
268  std::cerr << el->ReadFrom()
269  << "FGTable: missing lookup axis \"column\"";
270  throw BaseException("FGTable: missing lookup axis \"column\"");
271  }
272  // Don't break as we want to investigate the last lookup axes as well.
273  case 1u:
274  if (!lookupProperty[eRow]) {
275  std::cerr << el->ReadFrom()
276  << "FGTable: missing lookup axis \"row\"";
277  throw BaseException("FGTable: missing lookup axis \"row\"");
278  }
279  break;
280  default:
281  assert(false); // Should never be called
282  break;
283  }
284  }
285 
286  stringstream buf;
287 
288  for (unsigned int i=0; i<tableData->GetNumDataLines(); i++) {
289  string line = tableData->GetDataLine(i);
290  if (line.find_first_not_of("0123456789.-+eE \t\n") != string::npos) {
291  cerr << " In file " << tableData->GetFileName() << endl
292  << " Illegal character found in line "
293  << tableData->GetLineNumber() + i + 1 << ": " << endl << line << endl;
294  throw BaseException("Illegal character");
295  }
296  buf << line << " ";
297  }
298 
299  switch (dimension) {
300  case 1:
301  nRows = tableData->GetNumDataLines();
302  nCols = 1;
303  Type = tt1D;
304  // Fill unused elements with NaNs to detect illegal access.
305  Data.push_back(std::numeric_limits<double>::quiet_NaN());
306  Data.push_back(std::numeric_limits<double>::quiet_NaN());
307  *this << buf;
308  break;
309  case 2:
310  nRows = tableData->GetNumDataLines()-1;
311  nCols = FindNumColumns(tableData->GetDataLine(0));
312  Type = tt2D;
313  // Fill unused elements with NaNs to detect illegal access.
314  Data.push_back(std::numeric_limits<double>::quiet_NaN());
315  *this << buf;
316  break;
317  case 3:
318  nRows = el->GetNumElements("tableData");
319  nCols = 1;
320  Type = tt3D;
321  // Fill unused elements with NaNs to detect illegal access.
322  Data.push_back(std::numeric_limits<double>::quiet_NaN());
323 
324  tableData = el->FindElement("tableData");
325  while (tableData) {
326  Tables.push_back(std::make_unique<FGTable>(PropertyManager, tableData));
327  Data.push_back(tableData->GetAttributeValueAsNumber("breakPoint"));
328  Tables.back()->lookupProperty[eRow] = lookupProperty[eRow];
329  Tables.back()->lookupProperty[eColumn] = lookupProperty[eColumn];
330  tableData = el->FindNextElement("tableData");
331  }
332 
333  break;
334  default:
335  assert(false); // Should never be called
336  break;
337  }
338 
339  Debug(0);
340 
341  // Sanity checks: lookup indices must be increasing monotonically
342 
343  // find next xml element containing a name attribute
344  // to indicate where the error occured
345  Element* nameel = el;
346  while (nameel != 0 && nameel->GetAttributeValue("name") == "")
347  nameel=nameel->GetParent();
348 
349  // check breakpoints, if applicable
350  if (Type == tt3D) {
351  for (unsigned int b=2; b<=Tables.size(); ++b) {
352  if (Data[b] <= Data[b-1]) {
353  std::cerr << el->ReadFrom()
354  << fgred << highint
355  << " FGTable: breakpoint lookup is not monotonically increasing" << endl
356  << " in breakpoint " << b;
357  if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name");
358  std::cerr << ":" << reset << endl
359  << " " << Data[b] << "<=" << Data[b-1] << endl;
360  throw BaseException("Breakpoint lookup is not monotonically increasing");
361  }
362  }
363  }
364 
365  // check columns, if applicable
366  if (Type == tt2D) {
367  for (unsigned int c=2; c<=nCols; ++c) {
368  if (Data[c] <= Data[c-1]) {
369  std::cerr << el->ReadFrom()
370  << fgred << highint
371  << " FGTable: column lookup is not monotonically increasing" << endl
372  << " in column " << c;
373  if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name");
374  std::cerr << ":" << reset << endl
375  << " " << Data[c] << "<=" << Data[c-1] << endl;
376  throw BaseException("FGTable: column lookup is not monotonically increasing");
377  }
378  }
379  }
380 
381  // check rows
382  if (Type != tt3D) { // in 3D tables, check only rows of subtables
383  for (size_t r=2; r<=nRows; ++r) {
384  if (Data[r*(nCols+1)]<=Data[(r-1)*(nCols+1)]) {
385  std::cerr << el->ReadFrom()
386  << fgred << highint
387  << " FGTable: row lookup is not monotonically increasing" << endl
388  << " in row " << r;
389  if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name");
390  std::cerr << ":" << reset << endl
391  << " " << Data[r*(nCols+1)] << "<=" << Data[(r-1)*(nCols+1)] << endl;
392  throw BaseException("FGTable: row lookup is not monotonically increasing");
393  }
394  }
395  }
396 
397  // Check the table has been entirely populated.
398  switch (Type) {
399  case tt1D:
400  if (Data.size() != 2*nRows+2) missingData(el, 2*nRows, Data.size()-2);
401  break;
402  case tt2D:
403  if (Data.size() != static_cast<size_t>(nRows+1)*(nCols+1))
404  missingData(el, (nRows+1)*(nCols+1)-1, Data.size()-1);
405  break;
406  case tt3D:
407  if (Data.size() != nRows+1) missingData(el, nRows, Data.size()-1);
408  break;
409  default:
410  assert(false); // Should never be called
411  break;
412  }
413 
414  bind(el, Prefix);
415 
416  if (debug_lvl & 1) Print();
417 }
418 
419 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
420 
421 void FGTable::missingData(Element *el, unsigned int expected_size, size_t actual_size)
422 {
423  std::cerr << el->ReadFrom()
424  << fgred << highint
425  << " FGTable: Missing data";
426  if (!Name.empty()) std::cerr << " in table " << Name;
427  std::cerr << ":" << reset << endl
428  << " Expecting " << expected_size << " elements while "
429  << actual_size << " elements were provided." << endl;
430  throw BaseException("FGTable: missing data");
431 }
432 
433 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
434 
436 {
437  // Untie the bound property so that it makes no further reference to this
438  // instance of FGTable after the destruction is completed.
439  if (!Name.empty() && !internal) {
440  string tmp = PropertyManager->mkPropertyName(Name, false);
441  FGPropertyNode* node = PropertyManager->GetNode(tmp);
442  if (node && node->isTied())
443  PropertyManager->Untie(node);
444  }
445 
446  Debug(1);
447 }
448 
449 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
450 
451 double FGTable::GetElement(unsigned int r, unsigned int c) const
452 {
453  assert(r <= nRows && c <= nCols);
454  if (Type == tt3D) {
455  assert(Data.size() == nRows+1);
456  return Data[r];
457  }
458  assert(Data.size() == (nCols+1)*(nRows+1));
459  return Data[r*(nCols+1)+c];
460 }
461 
462 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463 
464 double FGTable::GetValue(void) const
465 {
466  assert(!internal);
467 
468  switch (Type) {
469  case tt1D:
470  assert(lookupProperty[eRow]);
471  return GetValue(lookupProperty[eRow]->getDoubleValue());
472  case tt2D:
473  assert(lookupProperty[eRow]);
474  assert(lookupProperty[eColumn]);
475  return GetValue(lookupProperty[eRow]->getDoubleValue(),
476  lookupProperty[eColumn]->getDoubleValue());
477  case tt3D:
478  assert(lookupProperty[eRow]);
479  assert(lookupProperty[eColumn]);
480  assert(lookupProperty[eTable]);
481  return GetValue(lookupProperty[eRow]->getDoubleValue(),
482  lookupProperty[eColumn]->getDoubleValue(),
483  lookupProperty[eTable]->getDoubleValue());
484  default:
485  assert(false); // Should never be called
486  return std::numeric_limits<double>::quiet_NaN();
487  }
488 }
489 
490 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
491 
492 double FGTable::GetValue(double key) const
493 {
494  assert(nCols == 1);
495  assert(Data.size() == 2*nRows+2);
496  // If the key is off the end (or before the beginning) of the table, just
497  // return the boundary-table value, do not extrapolate.
498  if (key <= Data[2])
499  return Data[3];
500  else if (key >= Data[2*nRows])
501  return Data[2*nRows+1];
502 
503  // Search for the right breakpoint.
504  // This is a linear search, the algorithm is O(n).
505  unsigned int r = 2;
506  while (Data[2*r] < key) r++;
507 
508  double x0 = Data[2*r-2];
509  double Span = Data[2*r] - x0;
510  assert(Span > 0.0);
511  double Factor = (key - x0) / Span;
512  assert(Factor >= 0.0 && Factor <= 1.0);
513 
514  double y0 = Data[2*r-1];
515  return Factor*(Data[2*r+1] - y0) + y0;
516 }
517 
518 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
519 
520 double FGTable::GetValue(double rowKey, double colKey) const
521 {
522  if (nCols == 1) return GetValue(rowKey);
523 
524  assert(Type == tt2D);
525  assert(Data.size() == (nCols+1)*(nRows+1));
526 
527  unsigned int c = 2;
528  while(Data[c] < colKey && c < nCols) c++;
529  double x0 = Data[c-1];
530  double Span = Data[c] - x0;
531  assert(Span > 0.0);
532  double cFactor = Constrain(0.0, (colKey - x0) / Span, 1.0);
533 
534  if (nRows == 1) {
535  double y0 = Data[(nCols+1)+c-1];
536  return cFactor*(Data[(nCols+1)+c] - y0) + y0;
537  }
538 
539  size_t r = 2;
540  while(Data[r*(nCols+1)] < rowKey && r < nRows) r++;
541  x0 = Data[(r-1)*(nCols+1)];
542  Span = Data[r*(nCols+1)] - x0;
543  assert(Span > 0.0);
544  double rFactor = Constrain(0.0, (rowKey - x0) / Span, 1.0);
545  double col1temp = rFactor*Data[r*(nCols+1)+c-1]+(1.0-rFactor)*Data[(r-1)*(nCols+1)+c-1];
546  double col2temp = rFactor*Data[r*(nCols+1)+c]+(1.0-rFactor)*Data[(r-1)*(nCols+1)+c];
547 
548  return cFactor*(col2temp-col1temp)+col1temp;
549 }
550 
551 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
552 
553 double FGTable::GetValue(double rowKey, double colKey, double tableKey) const
554 {
555  assert(Type == tt3D);
556  assert(Data.size() == nRows+1);
557  // If the key is off the end (or before the beginning) of the table, just
558  // return the boundary-table value, do not extrapolate.
559  if(tableKey <= Data[1])
560  return Tables[0]->GetValue(rowKey, colKey);
561  else if (tableKey >= Data[nRows])
562  return Tables[nRows-1]->GetValue(rowKey, colKey);
563 
564  // Search for the right breakpoint.
565  // This is a linear search, the algorithm is O(n).
566  unsigned int r = 2;
567  while (Data[r] < tableKey) r++;
568 
569  double x0 = Data[r-1];
570  double Span = Data[r] - x0;
571  assert(Span > 0.0);
572  double Factor = (tableKey - x0) / Span;
573  assert(Factor >= 0.0 && Factor <= 1.0);
574 
575  double y0 = Tables[r-2]->GetValue(rowKey, colKey);
576  return Factor*(Tables[r-1]->GetValue(rowKey, colKey) - y0) + y0;
577 }
578 
579 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
580 
581 double FGTable::GetMinValue(void) const
582 {
583  assert(Type == tt1D);
584  assert(Data.size() == 2*nRows+2);
585 
586  double minValue = HUGE_VAL;
587 
588  for(unsigned int i=1; i<=nRows; ++i)
589  minValue = std::min(minValue, Data[2*i+1]);
590 
591  return minValue;
592 }
593 
594 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
595 
596 void FGTable::operator<<(istream& in_stream)
597 {
598  double x;
599  assert(Type != tt3D);
600 
601  in_stream >> x;
602  while(in_stream) {
603  Data.push_back(x);
604  in_stream >> x;
605  }
606 }
607 
608 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609 
610 FGTable& FGTable::operator<<(const double x)
611 {
612  assert(Type != tt3D);
613  Data.push_back(x);
614 
615  // Check column is monotically increasing
616  size_t n = Data.size();
617  if (Type == tt2D && nCols > 1 && n >= 3 && n <= nCols+1) {
618  if (Data.at(n-1) <= Data.at(n-2))
619  throw BaseException("FGTable: column lookup is not monotonically increasing");
620  }
621 
622  // Check row is monotically increasing
623  size_t row = (n-1) / (nCols+1);
624  if (row >=2 && row*(nCols+1) == n-1) {
625  if (Data.at(row*(nCols+1)) <= Data.at((row-1)*(nCols+1)))
626  throw BaseException("FGTable: row lookup is not monotonically increasing");
627  }
628 
629  return *this;
630 }
631 
632 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
633 
634 void FGTable::Print(void)
635 {
636  ios::fmtflags flags = cout.setf(ios::fixed); // set up output stream
637  cout.precision(4);
638 
639  switch(Type) {
640  case tt1D:
641  cout << " 1 dimensional table with " << nRows << " rows." << endl;
642  break;
643  case tt2D:
644  cout << " 2 dimensional table with " << nRows << " rows, " << nCols << " columns." << endl;
645  break;
646  case tt3D:
647  cout << " 3 dimensional table with " << nRows << " breakpoints, "
648  << Tables.size() << " tables." << endl;
649  break;
650  }
651  unsigned int startCol=1, startRow=1;
652  unsigned int p = 1;
653 
654  if (Type == tt1D) {
655  startCol = 0;
656  p = 2;
657  }
658  if (Type == tt2D) startRow = 0;
659 
660  for (unsigned int r=startRow; r<=nRows; r++) {
661  cout << "\t";
662  if (Type == tt2D) {
663  if (r == startRow)
664  cout << "\t";
665  else
666  startCol = 0;
667  }
668 
669  for (unsigned int c=startCol; c<=nCols; c++) {
670  cout << Data[p++] << "\t";
671  if (Type == tt3D) {
672  cout << endl;
673  Tables[r-1]->Print();
674  }
675  }
676  cout << endl;
677  }
678  cout.setf(flags); // reset
679 }
680 
681 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
682 
683 void FGTable::bind(Element* el, const string& Prefix)
684 {
685  if ( !Name.empty() && !internal) {
686  if (!Prefix.empty()) {
687  if (is_number(Prefix)) {
688  if (Name.find("#") != string::npos) {
689  Name = replace(Name, "#", Prefix);
690  } else {
691  cerr << el->ReadFrom()
692  << "Malformed table name with number: " << Prefix
693  << " and property name: " << Name
694  << " but no \"#\" sign for substitution." << endl;
695  throw BaseException("Missing \"#\" sign for substitution");
696  }
697  } else {
698  Name = Prefix + "/" + Name;
699  }
700  }
701  string tmp = PropertyManager->mkPropertyName(Name, false);
702 
703  if (PropertyManager->HasNode(tmp)) {
704  FGPropertyNode* _property = PropertyManager->GetNode(tmp);
705  if (_property->isTied()) {
706  cerr << el->ReadFrom()
707  << "Property " << tmp << " has already been successfully bound (late)." << endl;
708  throw BaseException("Failed to bind the property to an existing already tied node.");
709  }
710  }
711 
712  PropertyManager->Tie<FGTable, double>(tmp, this, &FGTable::GetValue);
713  }
714 }
715 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
716 // The bitmasked value choices are as follows:
717 // unset: In this case (the default) JSBSim would only print
718 // out the normally expected messages, essentially echoing
719 // the config files as they are read. If the environment
720 // variable is not set, debug_lvl is set to 1 internally
721 // 0: This requests JSBSim not to output any messages
722 // whatsoever.
723 // 1: This value explicity requests the normal JSBSim
724 // startup messages
725 // 2: This value asks for a message to be printed out when
726 // a class is instantiated
727 // 4: When this value is set, a message is displayed when a
728 // FGModel object executes its Run() method
729 // 8: When this value is set, various runtime state variables
730 // are printed out periodically
731 // 16: When set various parameters are sanity checked and
732 // a message is printed out when they go out of bounds
733 
734 void FGTable::Debug(int from)
735 {
736  if (debug_lvl <= 0) return;
737 
738  if (debug_lvl & 1) { // Standard console startup message output
739  if (from == 0) { } // Constructor
740  }
741  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
742  if (from == 0) cout << "Instantiated: FGTable" << endl;
743  if (from == 1) cout << "Destroyed: FGTable" << endl;
744  }
745  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
746  }
747  if (debug_lvl & 8 ) { // Runtime state variables
748  }
749  if (debug_lvl & 16) { // Sanity checking
750  }
751  if (debug_lvl & 64) {
752  if (from == 0) { // Constructor
753  }
754  }
755 }
756 }
Element * FindElement(const std::string &el="")
Searches for a specified element.
double GetAttributeValueAsNumber(const std::string &key)
Retrieves an attribute value as a double precision real number.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
int GetLineNumber(void) const
Returns the line number at which the element has been defined.
Definition: FGXMLElement.h:230
std::string GetDataLine(unsigned int i=0)
Gets a line of data belonging to an element.
unsigned int GetNumDataLines(void)
Returns the number of lines of data stored.
Definition: FGXMLElement.h:189
const std::string & GetFileName(void) const
Returns the name of the file in which the element has been read.
Definition: FGXMLElement.h:235
unsigned int GetNumElements(void)
Returns the number of child elements for this element.
Definition: FGXMLElement.h:192
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
Element * GetParent(void)
Returns a pointer to the parent of an element.
Definition: FGXMLElement.h:225
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
static char fgred[6]
red text
Definition: FGJSBBase.h:167
static constexpr double Constrain(double min, double value, double max)
Constrain a value between a minimum and a maximum value.
Definition: FGJSBBase.h:289
static char fgdef[6]
default text
Definition: FGJSBBase.h:171
static char reset[5]
resets text properties
Definition: FGJSBBase.h:157
static char highint[5]
highlights text
Definition: FGJSBBase.h:151
Class wrapper for property handling.
Represents a property value which can use late binding.
Lookup table class.
Definition: FGTable.h:234
~FGTable()
Destructor.
Definition: FGTable.cpp:435
void operator<<(std::istream &)
Read the table in.
Definition: FGTable.cpp:596
FGTable(const FGTable &table)
This is the very important copy constructor.
Definition: FGTable.cpp:76
double GetValue(void) const
Get the current table value.
Definition: FGTable.cpp:464