JSBSim Flight Dynamics Model 1.3.0 (09 Apr 2026)
An Open Source Flight Dynamics and Control Software Library in C++
Loading...
Searching...
No Matches
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
27FUNCTIONAL DESCRIPTION
28--------------------------------------------------------------------------------
29Models a lookup table
30
31HISTORY
32--------------------------------------------------------------------------------
33JSB 1/9/00 Created
34
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36INCLUDES
37%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
38
39#include <limits>
40#include <assert.h>
41
42#include "FGTable.h"
43#include "input_output/FGXMLElement.h"
44#include "input_output/string_utilities.h"
45
46using namespace std;
47
48namespace JSBSim {
49
50/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
51CLASS IMPLEMENTATION
52%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
53
54FGTable::FGTable(int NRows)
55 : nRows(NRows), nCols(1)
56{
57 Type = tt1D;
58 // Fill unused elements with NaNs to detect illegal access.
59 Data.push_back(std::numeric_limits<double>::quiet_NaN());
60 Data.push_back(std::numeric_limits<double>::quiet_NaN());
61 Debug(0);
62}
63
64//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65
66FGTable::FGTable(int NRows, int NCols)
67 : nRows(NRows), nCols(NCols)
68{
69 Type = tt2D;
70 // Fill unused elements with NaNs to detect illegal access.
71 Data.push_back(std::numeric_limits<double>::quiet_NaN());
72 Debug(0);
73}
74
75//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76
77FGTable::FGTable(const FGTable& t)
78 : PropertyManager(t.PropertyManager)
79{
80 Type = t.Type;
81 nRows = t.nRows;
82 nCols = t.nCols;
83 internal = t.internal;
84 Name = t.Name;
85 lookupProperty[0] = t.lookupProperty[0];
86 lookupProperty[1] = t.lookupProperty[1];
87 lookupProperty[2] = t.lookupProperty[2];
88
89 // Deep copy of t.Tables
90 Tables.reserve(t.Tables.size());
91 for(const auto &t: t.Tables)
92 Tables.push_back(std::make_unique<FGTable>(*t));
93
94 Data = t.Data;
95}
96
97//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98
99unsigned int FindNumColumns(const string& test_line)
100{
101 // determine number of data columns in table (first column is row lookup - don't count)
102 size_t position=0;
103 unsigned int nCols=0;
104 while ((position = test_line.find_first_not_of(" \t", position)) != string::npos) {
105 nCols++;
106 position = test_line.find_first_of(" \t", position);
107 }
108 return nCols;
109}
110
111//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112
113FGTable::FGTable(std::shared_ptr<FGPropertyManager> pm, Element* el,
114 const std::string& Prefix)
115 : PropertyManager(pm)
116{
117 string brkpt_string;
118 Element *tableData = nullptr;
119
120 // Is this an internal lookup table?
121
122 Name = el->GetAttributeValue("name"); // Allow this table to be named with a property
123 string call_type = el->GetAttributeValue("type");
124 if (call_type == "internal") {
125 internal = true;
126 } else if (!call_type.empty()) {
127 XMLLogException err(el);
128 err <<" An unknown table type attribute is listed: " << call_type << "\n";
129 throw err;
130 }
131
132 // Determine and store the lookup properties for this table unless this table
133 // is part of a 3D table, in which case its independentVar property indexes
134 // will be set by a call from the owning table during creation
135
136 unsigned int dimension = 0;
137
138 Element* axisElement = el->FindElement("independentVar");
139 if (axisElement) {
140
141 // The 'internal' attribute of the table element cannot be specified
142 // at the same time that independentVars are specified.
143 if (internal) {
144 FGXMLLogging log(el, LogLevel::ERROR);
145 log << LogFormat::RED << " This table specifies both 'internal' call type\n"
146 << " and specific lookup properties via the 'independentVar' element.\n"
147 << " These are mutually exclusive specifications. The 'internal'\n"
148 << " attribute will be ignored.\n" << LogFormat::DEFAULT;
149 internal = false;
150 }
151
152 while (axisElement) {
153 string property_string = axisElement->GetDataLine();
154 if (property_string.find("#") != string::npos) {
155 if (is_number(Prefix)) {
156 property_string = replace(property_string,"#",Prefix);
157 }
158 }
159
160 FGPropertyValue_ptr node = new FGPropertyValue(property_string,
161 PropertyManager, axisElement);
162 string lookup_axis = axisElement->GetAttributeValue("lookup");
163 if (lookup_axis == string("row")) {
164 lookupProperty[eRow] = node;
165 dimension = std::max(dimension, 1u);
166 } else if (lookup_axis == string("column")) {
167 lookupProperty[eColumn] = node;
168 dimension = std::max(dimension, 2u);
169 } else if (lookup_axis == string("table")) {
170 lookupProperty[eTable] = node;
171 dimension = std::max(dimension, 3u);
172 } else if (!lookup_axis.empty()) {
173 throw BaseException("Lookup table axis specification not understood: " + lookup_axis);
174 } else { // assumed single dimension table; row lookup
175 lookupProperty[eRow] = node;
176 dimension = std::max(dimension, 1u);
177 }
178 axisElement = el->FindNextElement("independentVar");
179 }
180
181 } else if (internal) { // This table is an internal table
182
183 // determine how many rows, columns, and tables in this table (dimension).
184
185 if (el->GetNumElements("tableData") > 1) {
186 dimension = 3; // this is a 3D table
187 } else {
188 tableData = el->FindElement("tableData");
189 if (tableData) {
190 unsigned int nLines = tableData->GetNumDataLines();
191 unsigned int nColumns = FindNumColumns(tableData->GetDataLine(0));
192 if (nLines > 1) {
193 unsigned int nColumns1 = FindNumColumns(tableData->GetDataLine(1));
194 if (nColumns1 == nColumns + 1) {
195 dimension = 2;
196 nColumns = nColumns1;
197 }
198 else
199 dimension = 1;
200
201 // Check that every line (but the header line) has the same number of
202 // columns.
203 for(unsigned int i=1; i<nLines; ++i) {
204 if (FindNumColumns(tableData->GetDataLine(i)) != nColumns) {
205 XMLLogException err(tableData);
206 err << "Invalid number of columns in line "
207 << tableData->GetLineNumber()+i << "\n";
208 throw err;
209 }
210 }
211 }
212 else
213 dimension = 1;
214
215 if (dimension == 1 && nColumns != 2) {
216 XMLLogException err(tableData);
217 err << "Too many columns for a 1D table\n";
218 throw err;
219 }
220 }
221 }
222
223 } else {
224 brkpt_string = el->GetAttributeValue("breakPoint");
225 if (brkpt_string.empty()) {
226 // no independentVars found, and table is not marked as internal, nor is it
227 // a 3D table
228 XMLLogException err(el);
229 err << "No independentVars found, and table is not marked as internal,"
230 << " nor is it a 3D table.\n";
231 throw err;
232 }
233 }
234 // end lookup property code
235
236 if (brkpt_string.empty()) { // Not a 3D table "table element"
237 // Force the dimension to 3 if there are several instances of <tableData>.
238 // This is needed for sanity checks.
239 if (el->GetNumElements("tableData") > 1) dimension = 3;
240 tableData = el->FindElement("tableData");
241 } else { // This is a table in a 3D table
242 tableData = el;
243 dimension = 2; // Currently, infers 2D table
244 }
245
246 if (!tableData) {
247 XMLLogException err(el);
248 err << "FGTable: <tableData> elements are missing\n";
249 throw err;
250 }
251 else if (tableData->GetNumDataLines() == 0) {
252 XMLLogException err(tableData);
253 err << "<tableData> is empty.\n";
254 throw err;
255 }
256
257 // Check that the lookup axes match the declared dimension of the table.
258 if (!internal && brkpt_string.empty()) {
259 switch (dimension) {
260 case 3u:
261 if (!lookupProperty[eTable]) {
262 XMLLogException err(el);
263 err << "FGTable: missing lookup axis \"table\"\n";
264 throw err;
265 }
266 // Don't break as we want to investigate the other lookup axes as well.
267 case 2u:
268 if (!lookupProperty[eColumn]) {
269 XMLLogException err(el);
270 err << "FGTable: missing lookup axis \"column\"\n";
271 throw err;
272 }
273 // Don't break as we want to investigate the last lookup axes as well.
274 case 1u:
275 if (!lookupProperty[eRow]) {
276 XMLLogException err(el);
277 err << "FGTable: missing lookup axis \"row\"\n";
278 throw err;
279 }
280 break;
281 default:
282 assert(false); // Should never be called
283 break;
284 }
285 }
286
287 stringstream buf;
288
289 for (unsigned int i=0; i<tableData->GetNumDataLines(); i++) {
290 string line = tableData->GetDataLine(i);
291 if (line.find_first_not_of("0123456789.-+eE \t\n") != string::npos) {
292 XMLLogException err(tableData);
293 err << " Illegal character found in line "
294 << tableData->GetLineNumber() + i + 1 << ": \n" << line << "\n";
295 throw err;
296 }
297 buf << line << " ";
298 }
299
300 switch (dimension) {
301 case 1:
302 nRows = tableData->GetNumDataLines();
303 nCols = 1;
304 Type = tt1D;
305 // Fill unused elements with NaNs to detect illegal access.
306 Data.push_back(std::numeric_limits<double>::quiet_NaN());
307 Data.push_back(std::numeric_limits<double>::quiet_NaN());
308 *this << buf;
309 break;
310 case 2:
311 nRows = tableData->GetNumDataLines()-1;
312 nCols = FindNumColumns(tableData->GetDataLine(0));
313 Type = tt2D;
314 // Fill unused elements with NaNs to detect illegal access.
315 Data.push_back(std::numeric_limits<double>::quiet_NaN());
316 *this << buf;
317 break;
318 case 3:
319 nRows = el->GetNumElements("tableData");
320 nCols = 1;
321 Type = tt3D;
322 // Fill unused elements with NaNs to detect illegal access.
323 Data.push_back(std::numeric_limits<double>::quiet_NaN());
324
325 tableData = el->FindElement("tableData");
326 while (tableData) {
327 Tables.push_back(std::make_unique<FGTable>(PropertyManager, tableData));
328 Data.push_back(tableData->GetAttributeValueAsNumber("breakPoint"));
329 Tables.back()->lookupProperty[eRow] = lookupProperty[eRow];
330 Tables.back()->lookupProperty[eColumn] = lookupProperty[eColumn];
331 tableData = el->FindNextElement("tableData");
332 }
333
334 break;
335 default:
336 assert(false); // Should never be called
337 break;
338 }
339
340 Debug(0);
341
342 // Sanity checks: lookup indices must be increasing monotonically
343
344 // find next xml element containing a name attribute
345 // to indicate where the error occured
346 Element* nameel = el;
347 while (nameel && nameel->GetAttributeValue("name") == "")
348 nameel=nameel->GetParent();
349
350 // check breakpoints, if applicable
351 if (Type == tt3D) {
352 for (unsigned int b=2; b<=Tables.size(); ++b) {
353 if (Data[b] <= Data[b-1]) {
354 XMLLogException err(el);
355 err << LogFormat::RED << LogFormat::BOLD
356 << " FGTable: breakpoint lookup is not monotonically increasing\n"
357 << " in breakpoint " << b;
358 if (nameel) err << " of table in " << nameel->GetAttributeValue("name");
359 err << ":\n" << LogFormat::RESET
360 << " " << Data[b] << "<=" << Data[b-1] << "\n";
361 throw err;
362 }
363 }
364 }
365
366 // check columns, if applicable
367 if (Type == tt2D) {
368 for (unsigned int c=2; c<=nCols; ++c) {
369 if (Data[c] <= Data[c-1]) {
370 XMLLogException err(el);
371 err << LogFormat::RED << LogFormat::BOLD
372 << " FGTable: column lookup is not monotonically increasing\n"
373 << " in column " << c;
374 if (nameel != 0) err << " of table in " << nameel->GetAttributeValue("name");
375 err << ":\n" << LogFormat::RESET
376 << " " << Data[c] << "<=" << Data[c-1] << "\n";
377 throw err;
378 }
379 }
380 }
381
382 // check rows
383 if (Type != tt3D) { // in 3D tables, check only rows of subtables
384 for (size_t r=2; r<=nRows; ++r) {
385 if (Data[r*(nCols+1)]<=Data[(r-1)*(nCols+1)]) {
386 XMLLogException err(el);
387 err << LogFormat::RED << LogFormat::BOLD
388 << " FGTable: row lookup is not monotonically increasing\n"
389 << " in row " << r;
390 if (nameel != 0) err << " of table in " << nameel->GetAttributeValue("name");
391 err << ":\n" << LogFormat::RESET
392 << " " << Data[r*(nCols+1)] << "<=" << Data[(r-1)*(nCols+1)] << "\n";
393 throw err;
394 }
395 }
396 }
397
398 // Check the table has been entirely populated.
399 switch (Type) {
400 case tt1D:
401 if (Data.size() != 2*nRows+2) missingData(el, 2*nRows, Data.size()-2);
402 break;
403 case tt2D:
404 if (Data.size() != static_cast<size_t>(nRows+1)*(nCols+1))
405 missingData(el, (nRows+1)*(nCols+1)-1, Data.size()-1);
406 break;
407 case tt3D:
408 if (Data.size() != nRows+1) missingData(el, nRows, Data.size()-1);
409 break;
410 default:
411 assert(false); // Should never be called
412 break;
413 }
414
415 bind(el, Prefix);
416
417 if (debug_lvl & 1) Print();
418}
419
420//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421
422void FGTable::missingData(Element *el, unsigned int expected_size, size_t actual_size)
423{
424 XMLLogException err(el);
425 err << LogFormat::RED << LogFormat::BOLD
426 << " FGTable: Missing data";
427 if (!Name.empty()) err << " in table " << Name;
428 err << ":\n" << LogFormat::RESET
429 << " Expecting " << expected_size << " elements while "
430 << actual_size << " elements were provided.\n";
431 throw err;
432}
433
434//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
435
437{
438 // Untie the bound property so that it makes no further reference to this
439 // instance of FGTable after the destruction is completed.
440 if (!Name.empty() && !internal) {
441 string tmp = PropertyManager->mkPropertyName(Name, false);
442 SGPropertyNode* node = PropertyManager->GetNode(tmp);
443 if (node && node->isTied())
444 PropertyManager->Untie(node);
445 }
446
447 Debug(1);
448}
449
450//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
451
452double FGTable::GetElement(unsigned int r, unsigned int c) const
453{
454 assert(r <= nRows && c <= nCols);
455 if (Type == tt3D) {
456 assert(Data.size() == nRows+1);
457 return Data[r];
458 }
459 assert(Data.size() == (nCols+1)*(nRows+1));
460 return Data[r*(nCols+1)+c];
461}
462
463//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
464
465double FGTable::GetValue(void) const
466{
467 assert(!internal);
468
469 switch (Type) {
470 case tt1D:
471 assert(lookupProperty[eRow]);
472 return GetValue(lookupProperty[eRow]->getDoubleValue());
473 case tt2D:
474 assert(lookupProperty[eRow]);
475 assert(lookupProperty[eColumn]);
476 return GetValue(lookupProperty[eRow]->getDoubleValue(),
477 lookupProperty[eColumn]->getDoubleValue());
478 case tt3D:
479 assert(lookupProperty[eRow]);
480 assert(lookupProperty[eColumn]);
481 assert(lookupProperty[eTable]);
482 return GetValue(lookupProperty[eRow]->getDoubleValue(),
483 lookupProperty[eColumn]->getDoubleValue(),
484 lookupProperty[eTable]->getDoubleValue());
485 default:
486 assert(false); // Should never be called
487 return std::numeric_limits<double>::quiet_NaN();
488 }
489}
490
491//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
492
493double FGTable::GetValue(double key) const
494{
495 assert(nCols == 1);
496 assert(Data.size() == 2*nRows+2);
497 // If the key is off the end (or before the beginning) of the table, just
498 // return the boundary-table value, do not extrapolate.
499 if (key <= Data[2])
500 return Data[3];
501 else if (key >= Data[2*nRows])
502 return Data[2*nRows+1];
503
504 // Search for the right breakpoint.
505 // This is a linear search, the algorithm is O(n).
506 unsigned int r = 2;
507 while (Data[2*r] < key) r++;
508
509 double x0 = Data[2*r-2];
510 double Span = Data[2*r] - x0;
511 assert(Span > 0.0);
512 double Factor = (key - x0) / Span;
513 assert(Factor >= 0.0 && Factor <= 1.0);
514
515 double y0 = Data[2*r-1];
516 return Factor*(Data[2*r+1] - y0) + y0;
517}
518
519//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
520
521double FGTable::GetValue(double rowKey, double colKey) const
522{
523 if (nCols == 1) return GetValue(rowKey);
524
525 assert(Type == tt2D);
526 assert(Data.size() == (nCols+1)*(nRows+1));
527
528 unsigned int c = 2;
529 while(Data[c] < colKey && c < nCols) c++;
530 double x0 = Data[c-1];
531 double Span = Data[c] - x0;
532 assert(Span > 0.0);
533 double cFactor = Constrain(0.0, (colKey - x0) / Span, 1.0);
534
535 if (nRows == 1) {
536 double y0 = Data[(nCols+1)+c-1];
537 return cFactor*(Data[(nCols+1)+c] - y0) + y0;
538 }
539
540 size_t r = 2;
541 while(Data[r*(nCols+1)] < rowKey && r < nRows) r++;
542 x0 = Data[(r-1)*(nCols+1)];
543 Span = Data[r*(nCols+1)] - x0;
544 assert(Span > 0.0);
545 double rFactor = Constrain(0.0, (rowKey - x0) / Span, 1.0);
546 double col1temp = rFactor*Data[r*(nCols+1)+c-1]+(1.0-rFactor)*Data[(r-1)*(nCols+1)+c-1];
547 double col2temp = rFactor*Data[r*(nCols+1)+c]+(1.0-rFactor)*Data[(r-1)*(nCols+1)+c];
548
549 return cFactor*(col2temp-col1temp)+col1temp;
550}
551
552//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
553
554double FGTable::GetValue(double rowKey, double colKey, double tableKey) const
555{
556 assert(Type == tt3D);
557 assert(Data.size() == nRows+1);
558 // If the key is off the end (or before the beginning) of the table, just
559 // return the boundary-table value, do not extrapolate.
560 if(tableKey <= Data[1])
561 return Tables[0]->GetValue(rowKey, colKey);
562 else if (tableKey >= Data[nRows])
563 return Tables[nRows-1]->GetValue(rowKey, colKey);
564
565 // Search for the right breakpoint.
566 // This is a linear search, the algorithm is O(n).
567 unsigned int r = 2;
568 while (Data[r] < tableKey) r++;
569
570 double x0 = Data[r-1];
571 double Span = Data[r] - x0;
572 assert(Span > 0.0);
573 double Factor = (tableKey - x0) / Span;
574 assert(Factor >= 0.0 && Factor <= 1.0);
575
576 double y0 = Tables[r-2]->GetValue(rowKey, colKey);
577 return Factor*(Tables[r-1]->GetValue(rowKey, colKey) - y0) + y0;
578}
579
580//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
581
582double FGTable::GetMinValue(void) const
583{
584 assert(Type == tt1D);
585 assert(Data.size() == 2*nRows+2);
586
587 double minValue = HUGE_VAL;
588
589 for(unsigned int i=1; i<=nRows; ++i)
590 minValue = std::min(minValue, Data[2*i+1]);
591
592 return minValue;
593}
594
595//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
596
597void FGTable::operator<<(istream& in_stream)
598{
599 double x;
600 assert(Type != tt3D);
601
602 in_stream >> x;
603 while(in_stream) {
604 Data.push_back(x);
605 in_stream >> x;
606 }
607}
608
609//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
610
611FGTable& FGTable::operator<<(const double x)
612{
613 assert(Type != tt3D);
614 Data.push_back(x);
615
616 // Check column is monotically increasing
617 size_t n = Data.size();
618 if (Type == tt2D && nCols > 1 && n >= 3 && n <= nCols+1) {
619 if (Data.at(n-1) <= Data.at(n-2))
620 throw BaseException("FGTable: column lookup is not monotonically increasing");
621 }
622
623 // Check row is monotically increasing
624 size_t row = (n-1) / (nCols+1);
625 if (row >=2 && row*(nCols+1) == n-1) {
626 if (Data.at(row*(nCols+1)) <= Data.at((row-1)*(nCols+1)))
627 throw BaseException("FGTable: row lookup is not monotonically increasing");
628 }
629
630 return *this;
631}
632
633//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
634
635void FGTable::Print(void)
636{
637 FGLogging out(LogLevel::STDOUT);
638 out << std::setprecision(4);
639
640 switch(Type) {
641 case tt1D:
642 out << " 1 dimensional table with " << nRows << " rows.\n";
643 break;
644 case tt2D:
645 out << " 2 dimensional table with " << nRows << " rows, " << nCols << " columns.\n";
646 break;
647 case tt3D:
648 out << " 3 dimensional table with " << nRows << " breakpoints, "
649 << Tables.size() << " tables.\n";
650 break;
651 }
652 unsigned int startCol=1, startRow=1;
653 unsigned int p = 1;
654
655 if (Type == tt1D) {
656 startCol = 0;
657 p = 2;
658 }
659 if (Type == tt2D) startRow = 0;
660
661 for (unsigned int r=startRow; r<=nRows; r++) {
662 out << "\t";
663 if (Type == tt2D) {
664 if (r == startRow)
665 out << "\t";
666 else
667 startCol = 0;
668 }
669
670 for (unsigned int c=startCol; c<=nCols; c++) {
671 out << Data[p++] << "\t";
672 if (Type == tt3D) {
673 out << "\n";
674 Tables[r-1]->Print();
675 }
676 }
677 out << "\n";
678 }
679 out << std::fixed;
680}
681
682//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
683
684void FGTable::bind(Element* el, const string& Prefix)
685{
686 if ( !Name.empty() && !internal) {
687 if (!Prefix.empty()) {
688 if (is_number(Prefix)) {
689 if (Name.find("#") != string::npos) {
690 Name = replace(Name, "#", Prefix);
691 } else {
692 XMLLogException err(el);
693 err << "Malformed table name with number: " << Prefix
694 << " and property name: " << Name
695 << " but no \"#\" sign for substitution.\n";
696 throw err;
697 }
698 } else {
699 Name = Prefix + "/" + Name;
700 }
701 }
702 string tmp = PropertyManager->mkPropertyName(Name, false);
703
704 if (PropertyManager->HasNode(tmp)) {
705 SGPropertyNode* _property = PropertyManager->GetNode(tmp);
706 if (_property->isTied()) {
707 XMLLogException err(el);
708 err << "Property " << tmp << " has already been successfully bound (late).\n";
709 throw err;
710 }
711 }
712
713 PropertyManager->Tie<FGTable, double>(tmp, this, &FGTable::GetValue);
714 }
715}
716//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
717// The bitmasked value choices are as follows:
718// unset: In this case (the default) JSBSim would only print
719// out the normally expected messages, essentially echoing
720// the config files as they are read. If the environment
721// variable is not set, debug_lvl is set to 1 internally
722// 0: This requests JSBSim not to output any messages
723// whatsoever.
724// 1: This value explicity requests the normal JSBSim
725// startup messages
726// 2: This value asks for a message to be printed out when
727// a class is instantiated
728// 4: When this value is set, a message is displayed when a
729// FGModel object executes its Run() method
730// 8: When this value is set, various runtime state variables
731// are printed out periodically
732// 16: When set various parameters are sanity checked and
733// a message is printed out when they go out of bounds
734
735void FGTable::Debug(int from)
736{
737 if (debug_lvl <= 0) return;
738
739 if (debug_lvl & 1) { // Standard console startup message output
740 if (from == 0) { } // Constructor
741 }
742 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
743 FGLogging log(LogLevel::DEBUG);
744 if (from == 0) log << "Instantiated: FGTable\n";
745 if (from == 1) log << "Destroyed: FGTable\n";
746 }
747 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
748 }
749 if (debug_lvl & 8 ) { // Runtime state variables
750 }
751 if (debug_lvl & 16) { // Sanity checking
752 }
753 if (debug_lvl & 64) {
754 if (from == 0) { // Constructor
755 }
756 }
757}
758}
Element * FindElement(const std::string &el="")
Searches for a specified element.
double GetAttributeValueAsNumber(const std::string &key)
Retrieves an attribute value as a double precision real number.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
int GetLineNumber(void) const
Returns the line number at which the element has been defined.
std::string GetDataLine(unsigned int i=0)
Gets a line of data belonging to an element.
Element * GetParent(void)
Returns a pointer to the parent of an element.
unsigned int GetNumDataLines(void)
Returns the number of lines of data stored.
unsigned int GetNumElements(void)
Returns the number of child elements for this element.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
static constexpr double Constrain(double min, double value, double max)
Constrain a value between a minimum and a maximum value.
Definition FGJSBBase.h:289
Represents a property value which can use late binding.
Lookup table class.
Definition FGTable.h:234
~FGTable()
Destructor.
Definition FGTable.cpp:436
void operator<<(std::istream &)
Read the table in.
Definition FGTable.cpp:597
FGTable(const FGTable &table)
This is the very important copy constructor.
Definition FGTable.cpp:77
double GetValue(void) const
Get the current table value.
Definition FGTable.cpp:465
A node in a property tree.
Definition props.hxx:747
bool isTied() const
Test whether this node is bound to an external data source.
Definition props.hxx:1349
Main namespace for the JSBSim Flight Dynamics Model.
Definition FGFDMExec.cpp:71
Type
The possible types of an SGPropertyNode.
Definition props.hxx:153