]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/math/FGTable.cpp
100d5d784d1e9f5d0cd07075ef52cdd1e073ddfe
[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 (unsigned int r=0; r<=nRows; r++) {
91     for (unsigned 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   unsigned 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 (unsigned int r=0; r<=nRows; r++) {
291     Data[r] = new double[nCols+1];
292     for (unsigned 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 (unsigned int i=0; i<nTables; i++) delete Tables[i];
310     Tables.clear();
311   }
312   for (unsigned int r=0; r<=nRows; r++) delete[] Data[r];
313   delete[] Data;
314
315   Debug(1);
316 }
317
318 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
319
320 unsigned int FGTable::FindNumColumns(string test_line)
321 {
322   // determine number of data columns in table (first column is row lookup - don't count)
323   size_t position=0;
324   unsigned 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   unsigned 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   // The search is particularly efficient if 
378   // the correct breakpoint has not changed since last frame or
379   // has only changed very little
380
381   while (r > 2     && Data[r-1][0] > key) { r--; }
382   while (r < nRows && Data[r][0]   < key) { r++; }
383
384   lastRowIndex=r;
385   // make sure denominator below does not go to zero.
386
387   Span = Data[r][0] - Data[r-1][0];
388   if (Span != 0.0) {
389     Factor = (key - Data[r-1][0]) / Span;
390     if (Factor > 1.0) Factor = 1.0;
391   } else {
392     Factor = 1.0;
393   }
394
395   Value = Factor*(Data[r][1] - Data[r-1][1]) + Data[r-1][1];
396
397   return Value;
398 }
399
400 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
401
402 double FGTable::GetValue(double rowKey, double colKey) const
403 {
404   double rFactor, cFactor, col1temp, col2temp, Value;
405   unsigned int r = lastRowIndex;
406   unsigned int c = lastColumnIndex;
407
408   while(r > 2     && Data[r-1][0] > rowKey) { r--; }
409   while(r < nRows && Data[r]  [0] < rowKey) { r++; }
410
411   while(c > 2     && Data[0][c-1] > colKey) { c--; }
412   while(c < nCols && Data[0][c]   < colKey) { c++; }
413
414   lastRowIndex=r;
415   lastColumnIndex=c;
416
417   rFactor = (rowKey - Data[r-1][0]) / (Data[r][0] - Data[r-1][0]);
418   cFactor = (colKey - Data[0][c-1]) / (Data[0][c] - Data[0][c-1]);
419
420   if (rFactor > 1.0) rFactor = 1.0;
421   else if (rFactor < 0.0) rFactor = 0.0;
422
423   if (cFactor > 1.0) cFactor = 1.0;
424   else if (cFactor < 0.0) cFactor = 0.0;
425
426   col1temp = rFactor*(Data[r][c-1] - Data[r-1][c-1]) + Data[r-1][c-1];
427   col2temp = rFactor*(Data[r][c] - Data[r-1][c]) + Data[r-1][c];
428
429   Value = col1temp + cFactor*(col2temp - col1temp);
430
431   return Value;
432 }
433
434 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
435
436 double FGTable::GetValue(double rowKey, double colKey, double tableKey) const
437 {
438   double Factor, Value, Span;
439   unsigned int r = lastRowIndex;
440
441   //if the key is off the end  (or before the beginning) of the table,
442   // just return the boundary-table value, do not extrapolate
443
444   if( tableKey <= Data[1][1] ) {
445     lastRowIndex=2;
446     return Tables[0]->GetValue(rowKey, colKey);
447   } else if ( tableKey >= Data[nRows][1] ) {
448     lastRowIndex=nRows;
449     return Tables[nRows-1]->GetValue(rowKey, colKey);
450   }
451
452   // the key is somewhere in the middle, search for the right breakpoint
453   // The search is particularly efficient if 
454   // the correct breakpoint has not changed since last frame or
455   // has only changed very little
456
457   while(r > 2     && Data[r-1][1] > tableKey) { r--; }
458   while(r < nRows && Data[r]  [1] < tableKey) { r++; }
459
460   lastRowIndex=r;
461   // make sure denominator below does not go to zero.
462
463   Span = Data[r][1] - Data[r-1][1];
464   if (Span != 0.0) {
465     Factor = (tableKey - Data[r-1][1]) / Span;
466     if (Factor > 1.0) Factor = 1.0;
467   } else {
468     Factor = 1.0;
469   }
470
471   Value = Factor*(Tables[r-1]->GetValue(rowKey, colKey) - Tables[r-2]->GetValue(rowKey, colKey))
472                               + Tables[r-2]->GetValue(rowKey, colKey);
473
474   return Value;
475 }
476
477 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
478
479 void FGTable::operator<<(stringstream& in_stream)
480 {
481   int startRow=0;
482   int startCol=0;
483
484 // In 1D table, no pseudo-row of column-headers (i.e. keys):
485   if (Type == tt1D) startRow = 1;
486
487   for (unsigned int r=startRow; r<=nRows; r++) {
488     for (unsigned int c=startCol; c<=nCols; c++) {
489       if (r != 0 || c != 0) {
490         in_stream >> Data[r][c];
491       }
492     }
493   }
494 }
495
496 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497
498 FGTable& FGTable::operator<<(const double n)
499 {
500   Data[rowCounter][colCounter] = n;
501   if (colCounter == nCols) {
502     colCounter = 0;
503     rowCounter++;
504   } else {
505     colCounter++;
506   }
507   return *this;
508 }
509
510 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511
512 FGTable& FGTable::operator<<(const int n)
513 {
514   *this << (double)n;
515   return *this;
516 }
517
518 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
519
520 void FGTable::Print(void)
521 {
522   int startRow=0;
523   int startCol=0;
524
525   if (Type == tt1D || Type == tt3D) startRow = 1;
526   if (Type == tt3D) startCol = 1;
527
528 #if defined (sgi) && !defined(__GNUC__) && (_COMPILER_VERSION < 740)
529   unsigned long flags = cout.setf(ios::fixed);
530 #else
531   ios::fmtflags flags = cout.setf(ios::fixed); // set up output stream
532 #endif
533
534   switch(Type) {
535     case tt1D:
536       cout << "    1 dimensional table with " << nRows << " rows." << endl;
537       break;
538     case tt2D:
539       cout << "    2 dimensional table with " << nRows << " rows, " << nCols << " columns." << endl;
540       break;
541     case tt3D:
542       cout << "    3 dimensional table with " << nRows << " rows, "
543                                           << nCols << " columns "
544                                           << nTables << " tables." << endl;
545       break;
546   }
547   cout.precision(4);
548   for (unsigned int r=startRow; r<=nRows; r++) {
549     cout << "   ";
550     for (unsigned int c=startCol; c<=nCols; c++) {
551       if (r == 0 && c == 0) {
552         cout << "       ";
553       } else {
554         cout << Data[r][c] << " ";
555         if (Type == tt3D) {
556           cout << endl;
557           Tables[r-1]->Print();
558         }
559       }
560     }
561     cout << endl;
562   }
563   cout.setf(flags); // reset
564 }
565
566 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
567
568 void FGTable::bind(void)
569 {
570   typedef double (FGTable::*PMF)(void) const;
571   if ( !Name.empty() && !internal) {
572     string tmp = PropertyManager->mkPropertyName(Name, false); // Allow upper
573     PropertyManager->Tie( tmp, this, (PMF)&FGTable::GetValue);
574   }
575 }
576 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
577 //    The bitmasked value choices are as follows:
578 //    unset: In this case (the default) JSBSim would only print
579 //       out the normally expected messages, essentially echoing
580 //       the config files as they are read. If the environment
581 //       variable is not set, debug_lvl is set to 1 internally
582 //    0: This requests JSBSim not to output any messages
583 //       whatsoever.
584 //    1: This value explicity requests the normal JSBSim
585 //       startup messages
586 //    2: This value asks for a message to be printed out when
587 //       a class is instantiated
588 //    4: When this value is set, a message is displayed when a
589 //       FGModel object executes its Run() method
590 //    8: When this value is set, various runtime state variables
591 //       are printed out periodically
592 //    16: When set various parameters are sanity checked and
593 //       a message is printed out when they go out of bounds
594
595 void FGTable::Debug(int from)
596 {
597   if (debug_lvl <= 0) return;
598
599   if (debug_lvl & 1) { // Standard console startup message output
600     if (from == 0) { // Constructor
601
602     }
603   }
604   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
605     if (from == 0) cout << "Instantiated: FGTable" << endl;
606     if (from == 1) cout << "Destroyed:    FGTable" << endl;
607   }
608   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
609   }
610   if (debug_lvl & 8 ) { // Runtime state variables
611   }
612   if (debug_lvl & 16) { // Sanity checking
613   }
614   if (debug_lvl & 64) {
615     if (from == 0) { // Constructor
616       cout << IdSrc << endl;
617       cout << IdHdr << endl;
618     }
619   }
620 }
621 }