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