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