43#include "input_output/FGXMLElement.h"
44#include "input_output/string_utilities.h"
55 : nRows(NRows), nCols(1)
59 Data.push_back(std::numeric_limits<double>::quiet_NaN());
60 Data.push_back(std::numeric_limits<double>::quiet_NaN());
66FGTable::FGTable(
int NRows,
int NCols)
67 : nRows(NRows), nCols(NCols)
71 Data.push_back(std::numeric_limits<double>::quiet_NaN());
78 : PropertyManager(t.PropertyManager)
83 internal = t.internal;
85 lookupProperty[0] = t.lookupProperty[0];
86 lookupProperty[1] = t.lookupProperty[1];
87 lookupProperty[2] = t.lookupProperty[2];
90 Tables.reserve(t.Tables.size());
91 for(
const auto &t: t.Tables)
92 Tables.push_back(std::make_unique<FGTable>(*t));
99unsigned int FindNumColumns(
const string& test_line)
103 unsigned int nCols=0;
104 while ((position = test_line.find_first_not_of(
" \t", position)) != string::npos) {
106 position = test_line.find_first_of(
" \t", position);
114 const std::string& Prefix)
115 : PropertyManager(pm)
124 if (call_type ==
"internal") {
126 }
else if (!call_type.empty()) {
128 err <<
" An unknown table type attribute is listed: " << call_type <<
"\n";
136 unsigned int dimension = 0;
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;
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);
161 PropertyManager, axisElement);
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);
175 lookupProperty[eRow] = node;
176 dimension = std::max(dimension, 1u);
181 }
else if (internal) {
191 unsigned int nColumns = FindNumColumns(tableData->
GetDataLine(0));
193 unsigned int nColumns1 = FindNumColumns(tableData->
GetDataLine(1));
194 if (nColumns1 == nColumns + 1) {
196 nColumns = nColumns1;
203 for(
unsigned int i=1; i<nLines; ++i) {
204 if (FindNumColumns(tableData->
GetDataLine(i)) != nColumns) {
206 err <<
"Invalid number of columns in line "
215 if (dimension == 1 && nColumns != 2) {
217 err <<
"Too many columns for a 1D table\n";
225 if (brkpt_string.empty()) {
229 err <<
"No independentVars found, and table is not marked as internal,"
230 <<
" nor is it a 3D table.\n";
236 if (brkpt_string.empty()) {
248 err <<
"FGTable: <tableData> elements are missing\n";
253 err <<
"<tableData> is empty.\n";
258 if (!internal && brkpt_string.empty()) {
261 if (!lookupProperty[eTable]) {
263 err <<
"FGTable: missing lookup axis \"table\"\n";
268 if (!lookupProperty[eColumn]) {
270 err <<
"FGTable: missing lookup axis \"column\"\n";
275 if (!lookupProperty[eRow]) {
277 err <<
"FGTable: missing lookup axis \"row\"\n";
291 if (line.find_first_not_of(
"0123456789.-+eE \t\n") != string::npos) {
293 err <<
" Illegal character found in line "
294 << tableData->
GetLineNumber() + i + 1 <<
": \n" << line <<
"\n";
306 Data.push_back(std::numeric_limits<double>::quiet_NaN());
307 Data.push_back(std::numeric_limits<double>::quiet_NaN());
315 Data.push_back(std::numeric_limits<double>::quiet_NaN());
323 Data.push_back(std::numeric_limits<double>::quiet_NaN());
327 Tables.push_back(std::make_unique<FGTable>(PropertyManager, tableData));
329 Tables.back()->lookupProperty[eRow] = lookupProperty[eRow];
330 Tables.back()->lookupProperty[eColumn] = lookupProperty[eColumn];
352 for (
unsigned int b=2; b<=Tables.size(); ++b) {
353 if (Data[b] <= Data[b-1]) {
355 err << LogFormat::RED << LogFormat::BOLD
356 <<
" FGTable: breakpoint lookup is not monotonically increasing\n"
357 <<
" in breakpoint " << b;
359 err <<
":\n" << LogFormat::RESET
360 <<
" " << Data[b] <<
"<=" << Data[b-1] <<
"\n";
368 for (
unsigned int c=2; c<=nCols; ++c) {
369 if (Data[c] <= Data[c-1]) {
371 err << LogFormat::RED << LogFormat::BOLD
372 <<
" FGTable: column lookup is not monotonically increasing\n"
373 <<
" in column " << c;
375 err <<
":\n" << LogFormat::RESET
376 <<
" " << Data[c] <<
"<=" << Data[c-1] <<
"\n";
384 for (
size_t r=2; r<=nRows; ++r) {
385 if (Data[r*(nCols+1)]<=Data[(r-1)*(nCols+1)]) {
387 err << LogFormat::RED << LogFormat::BOLD
388 <<
" FGTable: row lookup is not monotonically increasing\n"
391 err <<
":\n" << LogFormat::RESET
392 <<
" " << Data[r*(nCols+1)] <<
"<=" << Data[(r-1)*(nCols+1)] <<
"\n";
401 if (Data.size() != 2*nRows+2) missingData(el, 2*nRows, Data.size()-2);
404 if (Data.size() !=
static_cast<size_t>(nRows+1)*(nCols+1))
405 missingData(el, (nRows+1)*(nCols+1)-1, Data.size()-1);
408 if (Data.size() != nRows+1) missingData(el, nRows, Data.size()-1);
417 if (debug_lvl & 1) Print();
422void FGTable::missingData(
Element *el,
unsigned int expected_size,
size_t actual_size)
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";
440 if (!Name.empty() && !internal) {
441 string tmp = PropertyManager->mkPropertyName(Name,
false);
443 if (node && node->
isTied())
444 PropertyManager->Untie(node);
452double FGTable::GetElement(
unsigned int r,
unsigned int c)
const
454 assert(r <= nRows && c <= nCols);
456 assert(Data.size() == nRows+1);
459 assert(Data.size() == (nCols+1)*(nRows+1));
460 return Data[r*(nCols+1)+c];
471 assert(lookupProperty[eRow]);
472 return GetValue(lookupProperty[eRow]->getDoubleValue());
474 assert(lookupProperty[eRow]);
475 assert(lookupProperty[eColumn]);
476 return GetValue(lookupProperty[eRow]->getDoubleValue(),
477 lookupProperty[eColumn]->getDoubleValue());
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());
487 return std::numeric_limits<double>::quiet_NaN();
496 assert(Data.size() == 2*nRows+2);
501 else if (key >= Data[2*nRows])
502 return Data[2*nRows+1];
507 while (Data[2*r] < key) r++;
509 double x0 = Data[2*r-2];
510 double Span = Data[2*r] - x0;
512 double Factor = (key - x0) / Span;
513 assert(Factor >= 0.0 && Factor <= 1.0);
515 double y0 = Data[2*r-1];
516 return Factor*(Data[2*r+1] - y0) + y0;
523 if (nCols == 1)
return GetValue(rowKey);
525 assert(Type == tt2D);
526 assert(Data.size() == (nCols+1)*(nRows+1));
529 while(Data[c] < colKey && c < nCols) c++;
530 double x0 = Data[c-1];
531 double Span = Data[c] - x0;
533 double cFactor =
Constrain(0.0, (colKey - x0) / Span, 1.0);
536 double y0 = Data[(nCols+1)+c-1];
537 return cFactor*(Data[(nCols+1)+c] - y0) + y0;
541 while(Data[r*(nCols+1)] < rowKey && r < nRows) r++;
542 x0 = Data[(r-1)*(nCols+1)];
543 Span = Data[r*(nCols+1)] - x0;
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];
549 return cFactor*(col2temp-col1temp)+col1temp;
556 assert(Type == tt3D);
557 assert(Data.size() == nRows+1);
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);
568 while (Data[r] < tableKey) r++;
570 double x0 = Data[r-1];
571 double Span = Data[r] - x0;
573 double Factor = (tableKey - x0) / Span;
574 assert(Factor >= 0.0 && Factor <= 1.0);
576 double y0 = Tables[r-2]->GetValue(rowKey, colKey);
577 return Factor*(Tables[r-1]->GetValue(rowKey, colKey) - y0) + y0;
582double FGTable::GetMinValue(
void)
const
584 assert(Type == tt1D);
585 assert(Data.size() == 2*nRows+2);
587 double minValue = HUGE_VAL;
589 for(
unsigned int i=1; i<=nRows; ++i)
590 minValue = std::min(minValue, Data[2*i+1]);
600 assert(Type != tt3D);
613 assert(Type != tt3D);
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");
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");
635void FGTable::Print(
void)
637 FGLogging out(LogLevel::STDOUT);
638 out << std::setprecision(4);
642 out <<
" 1 dimensional table with " << nRows <<
" rows.\n";
645 out <<
" 2 dimensional table with " << nRows <<
" rows, " << nCols <<
" columns.\n";
648 out <<
" 3 dimensional table with " << nRows <<
" breakpoints, "
649 << Tables.size() <<
" tables.\n";
652 unsigned int startCol=1, startRow=1;
659 if (Type == tt2D) startRow = 0;
661 for (
unsigned int r=startRow; r<=nRows; r++) {
670 for (
unsigned int c=startCol; c<=nCols; c++) {
671 out << Data[p++] <<
"\t";
674 Tables[r-1]->Print();
684void FGTable::bind(Element* el,
const string& Prefix)
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);
692 XMLLogException err(el);
693 err <<
"Malformed table name with number: " << Prefix
694 <<
" and property name: " << Name
695 <<
" but no \"#\" sign for substitution.\n";
699 Name = Prefix +
"/" + Name;
702 string tmp = PropertyManager->mkPropertyName(Name,
false);
704 if (PropertyManager->HasNode(tmp)) {
706 if (_property->
isTied()) {
707 XMLLogException err(el);
708 err <<
"Property " << tmp <<
" has already been successfully bound (late).\n";
735void FGTable::Debug(
int from)
737 if (debug_lvl <= 0)
return;
742 if (debug_lvl & 2 ) {
743 FGLogging log(LogLevel::DEBUG);
744 if (from == 0) log <<
"Instantiated: FGTable\n";
745 if (from == 1) log <<
"Destroyed: FGTable\n";
747 if (debug_lvl & 4 ) {
749 if (debug_lvl & 8 ) {
751 if (debug_lvl & 16) {
753 if (debug_lvl & 64) {
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.
Represents a property value which can use late binding.
void operator<<(std::istream &)
Read the table in.
FGTable(const FGTable &table)
This is the very important copy constructor.
double GetValue(void) const
Get the current table value.
A node in a property tree.
bool isTied() const
Test whether this node is bound to an external data source.
Main namespace for the JSBSim Flight Dynamics Model.
Type
The possible types of an SGPropertyNode.