JSBSim Flight Dynamics Model  1.1.11 (13 Feb 2022)
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 <assert.h>
40 
41 #include "FGTable.h"
42 #include "input_output/FGXMLElement.h"
43 
44 using namespace std;
45 
46 namespace JSBSim {
47 
48 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
49 CLASS IMPLEMENTATION
50 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
51 
52 FGTable::FGTable(int NRows)
53  : nRows(NRows), nCols(1), PropertyManager(nullptr)
54 {
55  Type = tt1D;
56  colCounter = 0;
57  rowCounter = 1;
58  nTables = 0;
59 
60  Data = Allocate();
61  Debug(0);
62  lastRowIndex=lastColumnIndex=2;
63 }
64 
65 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66 
67 FGTable::FGTable(int NRows, int NCols)
68  : nRows(NRows), nCols(NCols), PropertyManager(nullptr)
69 {
70  Type = tt2D;
71  colCounter = 1;
72  rowCounter = 0;
73  nTables = 0;
74 
75  Data = Allocate();
76  Debug(0);
77  lastRowIndex=lastColumnIndex=2;
78 }
79 
80 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81 
82 FGTable::FGTable(const FGTable& t) : PropertyManager(t.PropertyManager)
83 {
84  Type = t.Type;
85  colCounter = t.colCounter;
86  rowCounter = t.rowCounter;
87  tableCounter = t.tableCounter;
88  nRows = t.nRows;
89  nCols = t.nCols;
90  nTables = t.nTables;
91  dimension = t.dimension;
92  internal = t.internal;
93  Name = t.Name;
94  lookupProperty[0] = t.lookupProperty[0];
95  lookupProperty[1] = t.lookupProperty[1];
96  lookupProperty[2] = t.lookupProperty[2];
97 
98  Tables = t.Tables;
99  Data = Allocate();
100  for (unsigned int r=0; r<=nRows; r++) {
101  for (unsigned int c=0; c<=nCols; c++) {
102  Data[r][c] = t.Data[r][c];
103  }
104  }
105  lastRowIndex = t.lastRowIndex;
106  lastColumnIndex = t.lastColumnIndex;
107  lastTableIndex = t.lastTableIndex;
108 }
109 
110 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111 
112 unsigned int FindNumColumns(const string& test_line)
113 {
114  // determine number of data columns in table (first column is row lookup - don't count)
115  size_t position=0;
116  unsigned int nCols=0;
117  while ((position = test_line.find_first_not_of(" \t", position)) != string::npos) {
118  nCols++;
119  position = test_line.find_first_of(" \t", position);
120  }
121  return nCols;
122 }
123 
124 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
125 
127  const std::string& Prefix)
128  : PropertyManager(propMan)
129 {
130  unsigned int i;
131 
132  stringstream buf;
133  string brkpt_string;
134  Element *tableData = nullptr;
135  string operation_types = "function, product, sum, difference, quotient,"
136  "pow, abs, sin, cos, asin, acos, tan, atan, table";
137 
138  nTables = 0;
139 
140  // Is this an internal lookup table?
141 
142  internal = false;
143  Name = el->GetAttributeValue("name"); // Allow this table to be named with a property
144  string call_type = el->GetAttributeValue("type");
145  if (call_type == string("internal")) {
146  Element* parent_element = el->GetParent();
147  string parent_type = parent_element->GetName();
148  if (operation_types.find(parent_type) == string::npos) {
149  internal = true;
150  } else {
151  // internal table is a child element of a restricted type
152  std::cerr << el->ReadFrom()
153  << " An internal table cannot be nested within another type,"
154  << " such as a function. The 'internal' keyword of table "
155  << Name << "is ignored." << endl;
156  }
157  } else if (!call_type.empty()) {
158  std::cerr << el->ReadFrom()
159  <<" An unknown table type attribute is listed: " << call_type
160  << endl;
161  throw BaseException("Unknown table type.");
162  }
163 
164  // Determine and store the lookup properties for this table unless this table
165  // is part of a 3D table, in which case its independentVar property indexes
166  // will be set by a call from the owning table during creation
167 
168  dimension = 0;
169 
170  Element* axisElement = el->FindElement("independentVar");
171  if (axisElement) {
172 
173  // The 'internal' attribute of the table element cannot be specified
174  // at the same time that independentVars are specified.
175  if (internal) {
176  cerr << el->ReadFrom()
177  << fgred << " This table specifies both 'internal' call type" << endl
178  << " and specific lookup properties via the 'independentVar' element." << endl
179  << " These are mutually exclusive specifications. The 'internal'" << endl
180  << " attribute will be ignored." << fgdef << endl << endl;
181  internal = false;
182  }
183 
184  while (axisElement) {
185  string property_string = axisElement->GetDataLine();
186  if (property_string.find("#") != string::npos) {
187  if (is_number(Prefix)) {
188  property_string = replace(property_string,"#",Prefix);
189  }
190  }
191 
192  FGPropertyValue_ptr node = new FGPropertyValue(property_string,
193  PropertyManager);
194  string lookup_axis = axisElement->GetAttributeValue("lookup");
195  if (lookup_axis == string("row")) {
196  lookupProperty[eRow] = node;
197  } else if (lookup_axis == string("column")) {
198  lookupProperty[eColumn] = node;
199  } else if (lookup_axis == string("table")) {
200  lookupProperty[eTable] = node;
201  } else if (!lookup_axis.empty()) {
202  throw BaseException("Lookup table axis specification not understood: " + lookup_axis);
203  } else { // assumed single dimension table; row lookup
204  lookupProperty[eRow] = node;
205  }
206  dimension++;
207  axisElement = el->FindNextElement("independentVar");
208  }
209 
210  } else if (internal) { // This table is an internal table
211 
212  // determine how many rows, columns, and tables in this table (dimension).
213 
214  if (el->GetNumElements("tableData") > 1) {
215  dimension = 3; // this is a 3D table
216  } else {
217  tableData = el->FindElement("tableData");
218  string test_line = tableData->GetDataLine(1); // examine second line in table for dimension
219  if (FindNumColumns(test_line) == 2) dimension = 1; // 1D table
220  else if (FindNumColumns(test_line) > 2) dimension = 2; // 2D table
221  else {
222  std::cerr << tableData->ReadFrom()
223  << "Invalid number of columns in table" << endl;
224  }
225  }
226 
227  } else {
228  brkpt_string = el->GetAttributeValue("breakPoint");
229  if (brkpt_string.empty()) {
230  // no independentVars found, and table is not marked as internal, nor is it
231  // a 3D table
232  std::cerr << el->ReadFrom()
233  << "No independentVars found, and table is not marked as internal,"
234  << " nor is it a 3D table." << endl;
235  throw BaseException("No independent variable found for table.");
236  }
237  }
238  // end lookup property code
239 
240  if (brkpt_string.empty()) { // Not a 3D table "table element"
241  tableData = el->FindElement("tableData");
242  } else { // This is a table in a 3D table
243  tableData = el;
244  dimension = 2; // Currently, infers 2D table
245  }
246 
247  for (i=0; i<tableData->GetNumDataLines(); i++) {
248  string line = tableData->GetDataLine(i);
249  if (line.find_first_not_of("0123456789.-+eE \t\n") != string::npos) {
250  cerr << " In file " << tableData->GetFileName() << endl
251  << " Illegal character found in line "
252  << tableData->GetLineNumber() + i + 1 << ": " << endl << line << endl;
253  throw BaseException("Illegal character");
254  }
255  buf << line << " ";
256  }
257 
258  switch (dimension) {
259  case 1:
260  nRows = tableData->GetNumDataLines();
261  nCols = 1;
262  Type = tt1D;
263  colCounter = 0;
264  rowCounter = 1;
265  Data = Allocate();
266  Debug(0);
267  lastRowIndex = lastColumnIndex = 2;
268  *this << buf;
269  break;
270  case 2:
271  nRows = tableData->GetNumDataLines()-1;
272 
273  if (nRows >= 2) {
274  nCols = FindNumColumns(tableData->GetDataLine(0));
275  if (nCols < 2) {
276  std::cerr << tableData->ReadFrom()
277  << "Not enough columns in table data" << endl;
278  throw BaseException("Not enough columns in table data.");
279  }
280  } else {
281  std::cerr << tableData->ReadFrom()
282  << "Not enough rows in table data" << endl;
283  throw BaseException("Not enough rows in the table data.");
284  }
285 
286  Type = tt2D;
287  colCounter = 1;
288  rowCounter = 0;
289 
290  Data = Allocate();
291  lastRowIndex = lastColumnIndex = 2;
292  *this << buf;
293  break;
294  case 3:
295  nTables = el->GetNumElements("tableData");
296  nRows = nTables;
297  nCols = 1;
298  Type = tt3D;
299  colCounter = 1;
300  rowCounter = 1;
301  lastRowIndex = lastColumnIndex = 2;
302 
303  Data = Allocate(); // this data array will contain the keys for the associated tables
304  Tables.reserve(nTables); // necessary?
305  tableData = el->FindElement("tableData");
306  for (i=0; i<nTables; i++) {
307  Tables.push_back(new FGTable(PropertyManager, tableData));
308  Data[i+1][1] = tableData->GetAttributeValueAsNumber("breakPoint");
309  Tables[i]->lookupProperty[eRow] = lookupProperty[eRow];
310  Tables[i]->lookupProperty[eColumn] = lookupProperty[eColumn];
311  tableData = el->FindNextElement("tableData");
312  }
313 
314  Debug(0);
315  break;
316  default:
317  cout << "No dimension given" << endl;
318  break;
319  }
320 
321  // Sanity checks: lookup indices must be increasing monotonically
322  unsigned int r,c,b;
323 
324  // find next xml element containing a name attribute
325  // to indicate where the error occured
326  Element* nameel = el;
327  while (nameel != 0 && nameel->GetAttributeValue("name") == "")
328  nameel=nameel->GetParent();
329 
330  // check breakpoints, if applicable
331  if (dimension > 2) {
332  for (b=2; b<=nTables; ++b) {
333  if (Data[b][1] <= Data[b-1][1]) {
334  std::cerr << el->ReadFrom()
335  << fgred << highint
336  << " FGTable: breakpoint lookup is not monotonically increasing" << endl
337  << " in breakpoint " << b;
338  if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name");
339  std::cerr << ":" << reset << endl
340  << " " << Data[b][1] << "<=" << Data[b-1][1] << endl;
341  throw BaseException("Breakpoint lookup is not monotonically increasing");
342  }
343  }
344  }
345 
346  // check columns, if applicable
347  if (dimension > 1) {
348  for (c=2; c<=nCols; ++c) {
349  if (Data[0][c] <= Data[0][c-1]) {
350  std::cerr << el->ReadFrom()
351  << fgred << highint
352  << " FGTable: column lookup is not monotonically increasing" << endl
353  << " in column " << c;
354  if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name");
355  std::cerr << ":" << reset << endl
356  << " " << Data[0][c] << "<=" << Data[0][c-1] << endl;
357  throw BaseException("FGTable: column lookup is not monotonically increasing");
358  }
359  }
360  }
361 
362  // check rows
363  if (dimension < 3) { // in 3D tables, check only rows of subtables
364  for (r=2; r<=nRows; ++r) {
365  if (Data[r][0]<=Data[r-1][0]) {
366  std::cerr << el->ReadFrom()
367  << fgred << highint
368  << " FGTable: row lookup is not monotonically increasing" << endl
369  << " in row " << r;
370  if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name");
371  std::cerr << ":" << reset << endl
372  << " " << Data[r][0] << "<=" << Data[r-1][0] << endl;
373  throw BaseException("FGTable: row lookup is not monotonically increasing");
374  }
375  }
376  }
377 
378  bind(el, Prefix);
379 
380  if (debug_lvl & 1) Print();
381 }
382 
383 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
384 
385 double** FGTable::Allocate(void)
386 {
387  Data = new double*[nRows+1];
388  for (unsigned int r=0; r<=nRows; r++) {
389  Data[r] = new double[nCols+1];
390  for (unsigned int c=0; c<=nCols; c++) {
391  Data[r][c] = 0.0;
392  }
393  }
394  return Data;
395 }
396 
397 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398 
400 {
401  // Untie the bound property so that it makes no further reference to this
402  // instance of FGTable after the destruction is completed.
403  if (!Name.empty() && !internal) {
404  string tmp = PropertyManager->mkPropertyName(Name, false);
405  FGPropertyNode* node = PropertyManager->GetNode(tmp);
406  if (node && node->isTied())
407  PropertyManager->Untie(node);
408  }
409 
410  if (nTables > 0) {
411  for (unsigned int i=0; i<nTables; i++) delete Tables[i];
412  Tables.clear();
413  }
414  for (unsigned int r=0; r<=nRows; r++) delete[] Data[r];
415  delete[] Data;
416 
417  Debug(1);
418 }
419 
420 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421 
422 double FGTable::GetValue(void) const
423 {
424  double temp = 0;
425  double temp2 = 0;
426 
427  switch (Type) {
428  case tt1D:
429  assert(lookupProperty[eRow]);
430  temp = lookupProperty[eRow]->getDoubleValue();
431  temp2 = GetValue(temp);
432  return temp2;
433  case tt2D:
434  assert(lookupProperty[eRow]);
435  assert(lookupProperty[eColumn]);
436  return GetValue(lookupProperty[eRow]->getDoubleValue(),
437  lookupProperty[eColumn]->getDoubleValue());
438  case tt3D:
439  assert(lookupProperty[eRow]);
440  assert(lookupProperty[eColumn]);
441  assert(lookupProperty[eTable]);
442  return GetValue(lookupProperty[eRow]->getDoubleValue(),
443  lookupProperty[eColumn]->getDoubleValue(),
444  lookupProperty[eTable]->getDoubleValue());
445  default:
446  cerr << "Attempted to GetValue() for invalid/unknown table type" << endl;
447  throw(string("Attempted to GetValue() for invalid/unknown table type"));
448  }
449 }
450 
451 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
452 
453 double FGTable::GetValue(double key) const
454 {
455  double Factor, Value, Span;
456  unsigned int r = lastRowIndex;
457 
458  //if the key is off the end of the table, just return the
459  //end-of-table value, do not extrapolate
460  if( key <= Data[1][0] ) {
461  lastRowIndex=2;
462  //cout << "Key underneath table: " << key << endl;
463  return Data[1][1];
464  } else if ( key >= Data[nRows][0] ) {
465  lastRowIndex=nRows;
466  //cout << "Key over table: " << key << endl;
467  return Data[nRows][1];
468  }
469 
470  // the key is somewhere in the middle, search for the right breakpoint
471  // The search is particularly efficient if
472  // the correct breakpoint has not changed since last frame or
473  // has only changed very little
474 
475  while (r > 2 && Data[r-1][0] > key) { r--; }
476  while (r < nRows && Data[r][0] < key) { r++; }
477 
478  lastRowIndex=r;
479  // make sure denominator below does not go to zero.
480 
481  Span = Data[r][0] - Data[r-1][0];
482  if (Span != 0.0) {
483  Factor = (key - Data[r-1][0]) / Span;
484  if (Factor > 1.0) Factor = 1.0;
485  } else {
486  Factor = 1.0;
487  }
488 
489  Value = Factor*(Data[r][1] - Data[r-1][1]) + Data[r-1][1];
490 
491  return Value;
492 }
493 
494 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
495 
496 double FGTable::GetValue(double rowKey, double colKey) const
497 {
498  double rFactor, cFactor, col1temp, col2temp, Value;
499  unsigned int r = lastRowIndex;
500  unsigned int c = lastColumnIndex;
501 
502  while(r > 2 && Data[r-1][0] > rowKey) { r--; }
503  while(r < nRows && Data[r] [0] < rowKey) { r++; }
504 
505  while(c > 2 && Data[0][c-1] > colKey) { c--; }
506  while(c < nCols && Data[0][c] < colKey) { c++; }
507 
508  lastRowIndex=r;
509  lastColumnIndex=c;
510 
511  rFactor = (rowKey - Data[r-1][0]) / (Data[r][0] - Data[r-1][0]);
512  cFactor = (colKey - Data[0][c-1]) / (Data[0][c] - Data[0][c-1]);
513 
514  if (rFactor > 1.0) rFactor = 1.0;
515  else if (rFactor < 0.0) rFactor = 0.0;
516 
517  if (cFactor > 1.0) cFactor = 1.0;
518  else if (cFactor < 0.0) cFactor = 0.0;
519 
520  col1temp = rFactor*(Data[r][c-1] - Data[r-1][c-1]) + Data[r-1][c-1];
521  col2temp = rFactor*(Data[r][c] - Data[r-1][c]) + Data[r-1][c];
522 
523  Value = col1temp + cFactor*(col2temp - col1temp);
524 
525  return Value;
526 }
527 
528 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
529 
530 double FGTable::GetValue(double rowKey, double colKey, double tableKey) const
531 {
532  double Factor, Value, Span;
533  unsigned int r = lastRowIndex;
534 
535  //if the key is off the end (or before the beginning) of the table,
536  // just return the boundary-table value, do not extrapolate
537 
538  if( tableKey <= Data[1][1] ) {
539  lastRowIndex=2;
540  return Tables[0]->GetValue(rowKey, colKey);
541  } else if ( tableKey >= Data[nRows][1] ) {
542  lastRowIndex=nRows;
543  return Tables[nRows-1]->GetValue(rowKey, colKey);
544  }
545 
546  // the key is somewhere in the middle, search for the right breakpoint
547  // The search is particularly efficient if
548  // the correct breakpoint has not changed since last frame or
549  // has only changed very little
550 
551  while(r > 2 && Data[r-1][1] > tableKey) { r--; }
552  while(r < nRows && Data[r] [1] < tableKey) { r++; }
553 
554  lastRowIndex=r;
555  // make sure denominator below does not go to zero.
556 
557  Span = Data[r][1] - Data[r-1][1];
558  if (Span != 0.0) {
559  Factor = (tableKey - Data[r-1][1]) / Span;
560  if (Factor > 1.0) Factor = 1.0;
561  } else {
562  Factor = 1.0;
563  }
564 
565  Value = Factor*(Tables[r-1]->GetValue(rowKey, colKey) - Tables[r-2]->GetValue(rowKey, colKey))
566  + Tables[r-2]->GetValue(rowKey, colKey);
567 
568  return Value;
569 }
570 
571 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
572 
573 void FGTable::operator<<(istream& in_stream)
574 {
575  int startRow=0;
576  int startCol=0;
577 
578 // In 1D table, no pseudo-row of column-headers (i.e. keys):
579  if (Type == tt1D) startRow = 1;
580 
581  for (unsigned int r=startRow; r<=nRows; r++) {
582  for (unsigned int c=startCol; c<=nCols; c++) {
583  if (r != 0 || c != 0) {
584  in_stream >> Data[r][c];
585  }
586  }
587  }
588 }
589 
590 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
591 
592 // Put some error handling in here if trying to access out of range row, col.
593 
594 FGTable& FGTable::operator<<(const double n)
595 {
596  Data[rowCounter][colCounter] = n;
597  if (colCounter == (int)nCols) {
598  colCounter = 0;
599  rowCounter++;
600  } else {
601  colCounter++;
602  }
603  return *this;
604 }
605 
606 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
607 
608 FGTable& FGTable::operator<<(const int n)
609 {
610  *this << (double)n;
611  return *this;
612 }
613 
614 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
615 
616 void FGTable::Print(void)
617 {
618  int startRow=0;
619  int startCol=0;
620 
621  if (Type == tt1D || Type == tt3D) startRow = 1;
622  if (Type == tt3D) startCol = 1;
623 
624 #if defined (sgi) && !defined(__GNUC__) && (_COMPILER_VERSION < 740)
625  unsigned long flags = cout.setf(ios::fixed);
626 #else
627  ios::fmtflags flags = cout.setf(ios::fixed); // set up output stream
628 #endif
629 
630  switch(Type) {
631  case tt1D:
632  cout << " 1 dimensional table with " << nRows << " rows." << endl;
633  break;
634  case tt2D:
635  cout << " 2 dimensional table with " << nRows << " rows, " << nCols << " columns." << endl;
636  break;
637  case tt3D:
638  cout << " 3 dimensional table with " << nRows << " rows, "
639  << nCols << " columns "
640  << nTables << " tables." << endl;
641  break;
642  }
643  cout.precision(4);
644  for (unsigned int r=startRow; r<=nRows; r++) {
645  cout << " ";
646  for (unsigned int c=startCol; c<=nCols; c++) {
647  if (r == 0 && c == 0) {
648  cout << " ";
649  } else {
650  cout << Data[r][c] << " ";
651  if (Type == tt3D) {
652  cout << endl;
653  Tables[r-1]->Print();
654  }
655  }
656  }
657  cout << endl;
658  }
659  cout.setf(flags); // reset
660 }
661 
662 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
663 
664 void FGTable::bind(Element* el, const string& Prefix)
665 {
666  typedef double (FGTable::*PMF)(void) const;
667 
668  if ( !Name.empty() && !internal) {
669  if (!Prefix.empty()) {
670  if (is_number(Prefix)) {
671  if (Name.find("#") != string::npos) { // if "#" is found
672  Name = replace(Name, "#", Prefix);
673  } else {
674  cerr << el->ReadFrom()
675  << "Malformed table name with number: " << Prefix
676  << " and property name: " << Name
677  << " but no \"#\" sign for substitution." << endl;
678  }
679  } else {
680  Name = Prefix + "/" + Name;
681  }
682  }
683  string tmp = PropertyManager->mkPropertyName(Name, false);
684 
685  if (PropertyManager->HasNode(tmp)) {
686  FGPropertyNode* _property = PropertyManager->GetNode(tmp);
687  if (_property->isTied()) {
688  cerr << el->ReadFrom()
689  << "Property " << tmp << " has already been successfully bound (late)." << endl;
690  throw("Failed to bind the property to an existing already tied node.");
691  }
692  }
693 
694  PropertyManager->Tie(tmp, this, (PMF)&FGTable::GetValue);
695  }
696 }
697 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
698 // The bitmasked value choices are as follows:
699 // unset: In this case (the default) JSBSim would only print
700 // out the normally expected messages, essentially echoing
701 // the config files as they are read. If the environment
702 // variable is not set, debug_lvl is set to 1 internally
703 // 0: This requests JSBSim not to output any messages
704 // whatsoever.
705 // 1: This value explicity requests the normal JSBSim
706 // startup messages
707 // 2: This value asks for a message to be printed out when
708 // a class is instantiated
709 // 4: When this value is set, a message is displayed when a
710 // FGModel object executes its Run() method
711 // 8: When this value is set, various runtime state variables
712 // are printed out periodically
713 // 16: When set various parameters are sanity checked and
714 // a message is printed out when they go out of bounds
715 
716 void FGTable::Debug(int from)
717 {
718  if (debug_lvl <= 0) return;
719 
720  if (debug_lvl & 1) { // Standard console startup message output
721  if (from == 0) { // Constructor
722 
723  }
724  }
725  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
726  if (from == 0) cout << "Instantiated: FGTable" << endl;
727  if (from == 1) cout << "Destroyed: FGTable" << endl;
728  }
729  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
730  }
731  if (debug_lvl & 8 ) { // Runtime state variables
732  }
733  if (debug_lvl & 16) { // Sanity checking
734  }
735  if (debug_lvl & 64) {
736  if (from == 0) { // Constructor
737  }
738  }
739 }
740 }
JSBSim::Element::GetParent
Element * GetParent(void)
Returns a pointer to the parent of an element.
Definition: FGXMLElement.h:225
JSBSim::Element::GetFileName
const std::string & GetFileName(void) const
Returns the name of the file in which the element has been read.
Definition: FGXMLElement.h:235
JSBSim::FGJSBBase::fgdef
static char fgdef[6]
default text
Definition: FGJSBBase.h:143
JSBSim::Element::GetAttributeValue
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
Definition: FGXMLElement.cpp:260
JSBSim::Element::FindElement
Element * FindElement(const std::string &el="")
Searches for a specified element.
Definition: FGXMLElement.cpp:389
JSBSim::FGTable::operator<<
void operator<<(std::istream &)
Read the table in.
Definition: FGTable.cpp:573
JSBSim::FGTable
Lookup table class.
Definition: FGTable.h:233
JSBSim::BaseException
Definition: FGJSBBase.h:59
JSBSim::FGPropertyNode
Class wrapper for property handling.
Definition: FGPropertyManager.h:70
JSBSim::Element::GetNumDataLines
unsigned int GetNumDataLines(void)
Returns the number of lines of data stored.
Definition: FGXMLElement.h:189
JSBSim::FGJSBBase::fgred
static char fgred[6]
red text
Definition: FGJSBBase.h:139
JSBSim::Element::GetLineNumber
int GetLineNumber(void) const
Returns the line number at which the element has been defined.
Definition: FGXMLElement.h:230
JSBSim::FGTable::FGTable
FGTable(const FGTable &table)
This is the very important copy constructor.
Definition: FGTable.cpp:82
JSBSim::FGPropertyValue
Represents a property value which can use late binding.
Definition: FGPropertyValue.h:59
JSBSim::FGJSBBase::reset
static char reset[5]
resets text properties
Definition: FGJSBBase.h:129
JSBSim::Element::GetDataLine
std::string GetDataLine(unsigned int i=0)
Gets a line of data belonging to an element.
Definition: FGXMLElement.cpp:333
JSBSim::Element::GetAttributeValueAsNumber
double GetAttributeValueAsNumber(const std::string &key)
Retrieves an attribute value as a double precision real number.
Definition: FGXMLElement.cpp:279
JSBSim::Element::GetName
const std::string & GetName(void) const
Retrieves the element name.
Definition: FGXMLElement.h:179
JSBSim::Element::GetNumElements
unsigned int GetNumElements(void)
Returns the number of child elements for this element.
Definition: FGXMLElement.h:192
JSBSim::FGPropertyManager::Untie
void Untie(const std::string &name)
Untie a property from an external data source.
Definition: FGPropertyManager.cpp:298
JSBSim::FGPropertyManager::mkPropertyName
std::string mkPropertyName(std::string name, bool lowercase)
Property-ify a name replaces spaces with '-' and, optionally, makes name all lower case.
Definition: FGPropertyManager.cpp:64
JSBSim::Element::ReadFrom
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
Definition: FGXMLElement.cpp:738
JSBSim::FGJSBBase::highint
static char highint[5]
highlights text
Definition: FGJSBBase.h:123
JSBSim::Element::FindNextElement
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
Definition: FGXMLElement.cpp:407
JSBSim::FGTable::~FGTable
~FGTable()
Destructor.
Definition: FGTable.cpp:399
JSBSim::FGPropertyManager
Definition: FGPropertyManager.h:373
JSBSim::Element
Definition: FGXMLElement.h:143
JSBSim::FGPropertyManager::Tie
void Tie(const std::string &name, T *pointer)
Tie a property to an external variable.
Definition: FGPropertyManager.h:449