]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/math/FGTable.cpp
"make dist" fixes.
[flightgear.git] / src / FDM / JSBSim / math / 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 (jsb@hal-pc.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 Software
12  Foundation; either version 2 of the License, or (at your option) any later
13  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 with
21  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22  Place - Suite 330, Boston, MA  02111-1307, USA.
23
24  Further information about the GNU Lesser General Public License can also be found on
25  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 "FGTable.h"
40 #include <iomanip>
41
42 using namespace std;
43
44 namespace JSBSim {
45
46 static const char *IdSrc = "$Id$";
47 static const char *IdHdr = ID_TABLE;
48
49 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50 CLASS IMPLEMENTATION
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
52
53 FGTable::FGTable(int NRows) : nRows(NRows), nCols(1), PropertyManager(0)
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(const FGTable& t) : PropertyManager(t.PropertyManager)
68 {
69   Type = t.Type;
70   colCounter = t.colCounter;
71   rowCounter = t.rowCounter;
72   tableCounter = t.tableCounter;
73   nRows = t.nRows;
74   nCols = t.nCols;
75   nTables = t.nTables;
76   dimension = t.dimension;
77   internal = t.internal;
78   Name = t.Name;
79   lookupProperty[0] = t.lookupProperty[0];
80   lookupProperty[1] = t.lookupProperty[1];
81   lookupProperty[2] = t.lookupProperty[2];
82
83   Tables = t.Tables;
84   Data = Allocate();
85   for (unsigned int r=0; r<=nRows; r++) {
86     for (unsigned int c=0; c<=nCols; c++) {
87       Data[r][c] = t.Data[r][c];
88     }
89   }
90   lastRowIndex = t.lastRowIndex;
91   lastColumnIndex = t.lastColumnIndex;
92   lastTableIndex = t.lastTableIndex;
93 }
94
95 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
96
97 FGTable::FGTable(FGPropertyManager* propMan, Element* el) : PropertyManager(propMan)
98 {
99   unsigned int i;
100
101   stringstream buf;
102   string property_string;
103   string lookup_axis;
104   string call_type;
105   string parent_type;
106   string brkpt_string;
107   FGPropertyManager* node;
108   Element *tableData=0;
109   Element *parent_element=0;
110   Element *axisElement=0;
111   string operation_types = "function, product, sum, difference, quotient,"
112                            "pow, abs, sin, cos, asin, acos, tan, atan, table";
113
114   nTables = 0;
115
116   // Is this an internal lookup table?
117
118   internal = false;
119   Name = el->GetAttributeValue("name"); // Allow this table to be named with a property
120   call_type = el->GetAttributeValue("type");
121   if (call_type == string("internal")) {
122     parent_element = el->GetParent();
123     parent_type = parent_element->GetName();
124     if (operation_types.find(parent_type) == string::npos) {
125       internal = true;
126     } else {
127       // internal table is a child element of a restricted type
128       cerr << endl << fgred << "  An internal table cannot be nested within another type," << endl;
129       cerr << "  such as a function. The 'internal' keyword is ignored." << fgdef << endl << endl;
130     }
131   } else if (!call_type.empty()) {
132     cerr << endl << fgred << "  An unknown table type attribute is listed: " << call_type
133                  << ". Execution cannot continue." << fgdef << endl << endl;
134     abort();
135   }
136
137   // Determine and store the lookup properties for this table unless this table
138   // is part of a 3D table, in which case its independentVar property indexes will
139   // be set by a call from the owning table during creation
140
141   dimension = 0;
142
143   axisElement = el->FindElement("independentVar");
144   if (axisElement) {
145
146     // The 'internal' attribute of the table element cannot be specified
147     // at the same time that independentVars are specified.
148     if (internal) {
149       cerr << endl << fgred << "  This table specifies both 'internal' call type" << endl;
150       cerr << "  and specific lookup properties via the 'independentVar' element." << endl;
151       cerr << "  These are mutually exclusive specifications. The 'internal'" << endl;
152       cerr << "  attribute will be ignored." << fgdef << endl << endl;
153       internal = false;
154     }
155
156     for (i=0; i<3; i++) lookupProperty[i] = 0;
157
158     while (axisElement) {
159       property_string = axisElement->GetDataLine();
160       // The property string passed into GetNode() must have no spaces or tabs.
161       node = PropertyManager->GetNode(property_string);
162
163       if (node == 0) {
164         cerr << "IndependenVar property, " << property_string
165              << " in Table definition is not defined." << endl;
166         abort();
167       }
168
169       lookup_axis = axisElement->GetAttributeValue("lookup");
170       if (lookup_axis == string("row")) {
171         lookupProperty[eRow] = node;
172       } else if (lookup_axis == string("column")) {
173         lookupProperty[eColumn] = node;
174       } else if (lookup_axis == string("table")) {
175         lookupProperty[eTable] = node;
176       } else { // assumed single dimension table; row lookup
177         lookupProperty[eRow] = node;
178       }
179       dimension++;
180       axisElement = el->FindNextElement("independentVar");
181     }
182
183   } else if (internal) { // This table is an internal table
184
185   // determine how many rows, columns, and tables in this table (dimension).
186
187     if (el->GetNumElements("tableData") > 1) {
188       dimension = 3; // this is a 3D table
189     } else {
190       tableData = el->FindElement("tableData");
191       string test_line = tableData->GetDataLine(1);  // examine second line in table for dimension
192       if (FindNumColumns(test_line) == 2) dimension = 1;    // 1D table
193       else if (FindNumColumns(test_line) > 2) dimension = 2; // 2D table
194       else {
195         cerr << "Invalid number of columns in table" << endl;
196       }
197     }
198
199   } else {
200     brkpt_string = el->GetAttributeValue("breakPoint");
201     if (brkpt_string.empty()) {
202      // no independentVars found, and table is not marked as internal, nor is it a 3D table
203       cerr << endl << fgred << "No independent variable found for table."  << fgdef << endl << endl;
204       abort();
205     }
206   }
207   // end lookup property code
208
209   if (brkpt_string.empty()) {                  // Not a 3D table "table element"
210     tableData = el->FindElement("tableData");
211   } else {                                     // This is a table in a 3D table
212     tableData = el;
213     dimension = 2;                             // Currently, infers 2D table
214   }
215
216   for (i=0; i<tableData->GetNumDataLines(); i++) {
217     buf << tableData->GetDataLine(i) << string(" ");
218   }
219   switch (dimension) {
220   case 1:
221     nRows = tableData->GetNumDataLines();
222     nCols = 1;
223     Type = tt1D;
224     colCounter = 0;
225     rowCounter = 1;
226     Data = Allocate();
227     Debug(0);
228     lastRowIndex = lastColumnIndex = 2;
229     *this << buf;
230     break;
231   case 2:
232     nRows = tableData->GetNumDataLines()-1;
233
234     if (nRows >= 2) nCols = FindNumColumns(tableData->GetDataLine(0));
235     else {
236       cerr << endl << fgred << "Not enough rows in this table." << fgdef << endl;
237       abort();
238     }
239
240     Type = tt2D;
241     colCounter = 1;
242     rowCounter = 0;
243
244     Data = Allocate();
245     lastRowIndex = lastColumnIndex = 2;
246     *this << buf;
247     break;
248   case 3:
249     nTables = el->GetNumElements("tableData");
250     nRows = nTables;
251     nCols = 1;
252     Type = tt3D;
253     colCounter = 1;
254     rowCounter = 1;
255     lastRowIndex = lastColumnIndex = 2;
256
257     Data = Allocate(); // this data array will contain the keys for the associated tables
258     Tables.reserve(nTables); // necessary?
259     tableData = el->FindElement("tableData");
260     for (i=0; i<nTables; i++) {
261       Tables.push_back(new FGTable(PropertyManager, tableData));
262       Data[i+1][1] = tableData->GetAttributeValueAsNumber("breakPoint");
263       Tables[i]->SetRowIndexProperty(lookupProperty[eRow]);
264       Tables[i]->SetColumnIndexProperty(lookupProperty[eColumn]);
265       tableData = el->FindNextElement("tableData");
266     }
267
268     Debug(0);
269     break;
270   default:
271     cout << "No dimension given" << endl;
272     break;
273   }
274
275   bind();
276
277   if (debug_lvl & 1) Print();
278 }
279
280 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
281
282 double** FGTable::Allocate(void)
283 {
284   Data = new double*[nRows+1];
285   for (unsigned int r=0; r<=nRows; r++) {
286     Data[r] = new double[nCols+1];
287     for (unsigned int c=0; c<=nCols; c++) {
288       Data[r][c] = 0.0;
289     }
290   }
291   return Data;
292 }
293
294 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
295
296 FGTable::~FGTable()
297 {
298   if (nTables > 0) {
299     for (unsigned int i=0; i<nTables; i++) delete Tables[i];
300     Tables.clear();
301   }
302   for (unsigned int r=0; r<=nRows; r++) delete[] Data[r];
303   delete[] Data;
304
305   Debug(1);
306 }
307
308 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
309
310 unsigned int FGTable::FindNumColumns(string test_line)
311 {
312   // determine number of data columns in table (first column is row lookup - don't count)
313   size_t position=0;
314   unsigned int nCols=0;
315   while ((position = test_line.find_first_not_of(" \t", position)) != string::npos) {
316     nCols++;
317     position = test_line.find_first_of(" \t", position);
318   }
319   return nCols;
320 }
321
322 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
323
324 double FGTable::GetValue(void) const
325 {
326   double temp = 0;
327   double temp2 = 0;
328
329   switch (Type) {
330   case tt1D:
331     temp = lookupProperty[eRow]->getDoubleValue();
332     temp2 = GetValue(temp);
333     return temp2;
334   case tt2D:
335     return GetValue(lookupProperty[eRow]->getDoubleValue(),
336                     lookupProperty[eColumn]->getDoubleValue());
337   case tt3D:
338     return GetValue(lookupProperty[eRow]->getDoubleValue(),
339                     lookupProperty[eColumn]->getDoubleValue(),
340                     lookupProperty[eTable]->getDoubleValue());
341   default:
342     cerr << "Attempted to GetValue() for invalid/unknown table type" << endl;
343     throw(string("Attempted to GetValue() for invalid/unknown table type"));
344   }
345 }
346
347 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
348
349 double FGTable::GetValue(double key) const
350 {
351   double Factor, Value, Span;
352   unsigned int r = lastRowIndex;
353
354   //if the key is off the end of the table, just return the
355   //end-of-table value, do not extrapolate
356   if( key <= Data[1][0] ) {
357     lastRowIndex=2;
358     //cout << "Key underneath table: " << key << endl;
359     return Data[1][1];
360   } else if ( key >= Data[nRows][0] ) {
361     lastRowIndex=nRows;
362     //cout << "Key over table: " << key << endl;
363     return Data[nRows][1];
364   }
365
366   // the key is somewhere in the middle, search for the right breakpoint
367   // The search is particularly efficient if 
368   // the correct breakpoint has not changed since last frame or
369   // has only changed very little
370
371   while (r > 2     && Data[r-1][0] > key) { r--; }
372   while (r < nRows && Data[r][0]   < key) { r++; }
373
374   lastRowIndex=r;
375   // make sure denominator below does not go to zero.
376
377   Span = Data[r][0] - Data[r-1][0];
378   if (Span != 0.0) {
379     Factor = (key - Data[r-1][0]) / Span;
380     if (Factor > 1.0) Factor = 1.0;
381   } else {
382     Factor = 1.0;
383   }
384
385   Value = Factor*(Data[r][1] - Data[r-1][1]) + Data[r-1][1];
386
387   return Value;
388 }
389
390 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
391
392 double FGTable::GetValue(double rowKey, double colKey) const
393 {
394   double rFactor, cFactor, col1temp, col2temp, Value;
395   unsigned int r = lastRowIndex;
396   unsigned int c = lastColumnIndex;
397
398   while(r > 2     && Data[r-1][0] > rowKey) { r--; }
399   while(r < nRows && Data[r]  [0] < rowKey) { r++; }
400
401   while(c > 2     && Data[0][c-1] > colKey) { c--; }
402   while(c < nCols && Data[0][c]   < colKey) { c++; }
403
404   lastRowIndex=r;
405   lastColumnIndex=c;
406
407   rFactor = (rowKey - Data[r-1][0]) / (Data[r][0] - Data[r-1][0]);
408   cFactor = (colKey - Data[0][c-1]) / (Data[0][c] - Data[0][c-1]);
409
410   if (rFactor > 1.0) rFactor = 1.0;
411   else if (rFactor < 0.0) rFactor = 0.0;
412
413   if (cFactor > 1.0) cFactor = 1.0;
414   else if (cFactor < 0.0) cFactor = 0.0;
415
416   col1temp = rFactor*(Data[r][c-1] - Data[r-1][c-1]) + Data[r-1][c-1];
417   col2temp = rFactor*(Data[r][c] - Data[r-1][c]) + Data[r-1][c];
418
419   Value = col1temp + cFactor*(col2temp - col1temp);
420
421   return Value;
422 }
423
424 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
425
426 double FGTable::GetValue(double rowKey, double colKey, double tableKey) const
427 {
428   double Factor, Value, Span;
429   unsigned int r = lastRowIndex;
430
431   //if the key is off the end  (or before the beginning) of the table,
432   // just return the boundary-table value, do not extrapolate
433
434   if( tableKey <= Data[1][1] ) {
435     lastRowIndex=2;
436     return Tables[0]->GetValue(rowKey, colKey);
437   } else if ( tableKey >= Data[nRows][1] ) {
438     lastRowIndex=nRows;
439     return Tables[nRows-1]->GetValue(rowKey, colKey);
440   }
441
442   // the key is somewhere in the middle, search for the right breakpoint
443   // The search is particularly efficient if 
444   // the correct breakpoint has not changed since last frame or
445   // has only changed very little
446
447   while(r > 2     && Data[r-1][1] > tableKey) { r--; }
448   while(r < nRows && Data[r]  [1] < tableKey) { r++; }
449
450   lastRowIndex=r;
451   // make sure denominator below does not go to zero.
452
453   Span = Data[r][1] - Data[r-1][1];
454   if (Span != 0.0) {
455     Factor = (tableKey - Data[r-1][1]) / Span;
456     if (Factor > 1.0) Factor = 1.0;
457   } else {
458     Factor = 1.0;
459   }
460
461   Value = Factor*(Tables[r-1]->GetValue(rowKey, colKey) - Tables[r-2]->GetValue(rowKey, colKey))
462                               + Tables[r-2]->GetValue(rowKey, colKey);
463
464   return Value;
465 }
466
467 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
468
469 void FGTable::operator<<(stringstream& in_stream)
470 {
471   int startRow=0;
472   int startCol=0;
473
474 // In 1D table, no pseudo-row of column-headers (i.e. keys):
475   if (Type == tt1D) startRow = 1;
476
477   for (unsigned int r=startRow; r<=nRows; r++) {
478     for (unsigned int c=startCol; c<=nCols; c++) {
479       if (r != 0 || c != 0) {
480         in_stream >> Data[r][c];
481       }
482     }
483   }
484 }
485
486 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487
488 FGTable& FGTable::operator<<(const double n)
489 {
490   Data[rowCounter][colCounter] = n;
491   if (colCounter == nCols) {
492     colCounter = 0;
493     rowCounter++;
494   } else {
495     colCounter++;
496   }
497   return *this;
498 }
499
500 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
501
502 FGTable& FGTable::operator<<(const int n)
503 {
504   *this << (double)n;
505   return *this;
506 }
507
508 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
509
510 void FGTable::Print(void)
511 {
512   int startRow=0;
513   int startCol=0;
514
515   if (Type == tt1D || Type == tt3D) startRow = 1;
516   if (Type == tt3D) startCol = 1;
517
518 #if defined (sgi) && !defined(__GNUC__) && (_COMPILER_VERSION < 740)
519   unsigned long flags = cout.setf(ios::fixed);
520 #else
521   ios::fmtflags flags = cout.setf(ios::fixed); // set up output stream
522 #endif
523
524   switch(Type) {
525     case tt1D:
526       cout << "    1 dimensional table with " << nRows << " rows." << endl;
527       break;
528     case tt2D:
529       cout << "    2 dimensional table with " << nRows << " rows, " << nCols << " columns." << endl;
530       break;
531     case tt3D:
532       cout << "    3 dimensional table with " << nRows << " rows, "
533                                           << nCols << " columns "
534                                           << nTables << " tables." << endl;
535       break;
536   }
537   cout.precision(4);
538   for (unsigned int r=startRow; r<=nRows; r++) {
539     cout << "   ";
540     for (unsigned int c=startCol; c<=nCols; c++) {
541       if (r == 0 && c == 0) {
542         cout << "       ";
543       } else {
544         cout << Data[r][c] << " ";
545         if (Type == tt3D) {
546           cout << endl;
547           Tables[r-1]->Print();
548         }
549       }
550     }
551     cout << endl;
552   }
553   cout.setf(flags); // reset
554 }
555
556 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
557
558 void FGTable::bind(void)
559 {
560   typedef double (FGTable::*PMF)(void) const;
561   if ( !Name.empty() && !internal) {
562     string tmp = PropertyManager->mkPropertyName(Name, false); // Allow upper
563     PropertyManager->Tie( tmp, this, (PMF)&FGTable::GetValue);
564   }
565 }
566 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
567 //    The bitmasked value choices are as follows:
568 //    unset: In this case (the default) JSBSim would only print
569 //       out the normally expected messages, essentially echoing
570 //       the config files as they are read. If the environment
571 //       variable is not set, debug_lvl is set to 1 internally
572 //    0: This requests JSBSim not to output any messages
573 //       whatsoever.
574 //    1: This value explicity requests the normal JSBSim
575 //       startup messages
576 //    2: This value asks for a message to be printed out when
577 //       a class is instantiated
578 //    4: When this value is set, a message is displayed when a
579 //       FGModel object executes its Run() method
580 //    8: When this value is set, various runtime state variables
581 //       are printed out periodically
582 //    16: When set various parameters are sanity checked and
583 //       a message is printed out when they go out of bounds
584
585 void FGTable::Debug(int from)
586 {
587   if (debug_lvl <= 0) return;
588
589   if (debug_lvl & 1) { // Standard console startup message output
590     if (from == 0) { // Constructor
591
592     }
593   }
594   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
595     if (from == 0) cout << "Instantiated: FGTable" << endl;
596     if (from == 1) cout << "Destroyed:    FGTable" << endl;
597   }
598   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
599   }
600   if (debug_lvl & 8 ) { // Runtime state variables
601   }
602   if (debug_lvl & 16) { // Sanity checking
603   }
604   if (debug_lvl & 64) {
605     if (from == 0) { // Constructor
606       cout << IdSrc << endl;
607       cout << IdHdr << endl;
608     }
609   }
610 }
611 }