From e5fe7476620b68a9807e75a543bff0bbc073113f Mon Sep 17 00:00:00 2001 From: Bertrand Coconnier Date: Sat, 20 Nov 2021 18:23:34 +0100 Subject: [PATCH] Sync'ed with JSBSim v1.1.10 - Fixed an error which prevented the equatorial and polar radii from to be propagated to the initial conditions. - The planet radii in can now be specified by the tags and which are more self explanatory than and (which are still valid). - Improved the error messages returned by FGTable: the file name and line number where the error occurred are now printed. - Check the number of tags for flight controls such as (GitHub issue #497). This avoids a crash when some or all elements are missing. - JSBSim now accepts 2 sign conventions for the cross product inertia (xy, xz, yz) in (GitHub Pull Request #502). The sign convention is specified by the parameter negated_crossproduct_inertia which defaults to true for backward compatibility. - Turbine engines can now windmill even before they start (GitHub issue #494 and Pull Request #509). - Fixed a sign error in the computation of aero/h_b-mac-ft (GitHub Pull Request #508 ) - Fixed a bug where FGTable instances were not untied from the property manager during their destruction. This could lead to segmentation faults when the property manager was later destroyed. - Exceptions raised by FGTable are now instances of the TableException class. --- src/FDM/JSBSim/FGFDMExec.cpp | 1 + .../initialization/FGInitialCondition.h | 4 +- src/FDM/JSBSim/math/FGTable.cpp | 175 +++++++++++------- src/FDM/JSBSim/math/FGTable.h | 18 +- src/FDM/JSBSim/models/FGAuxiliary.cpp | 2 +- src/FDM/JSBSim/models/FGInertial.cpp | 6 +- src/FDM/JSBSim/models/FGMassBalance.cpp | 11 +- src/FDM/JSBSim/models/FGMassBalance.h | 23 ++- .../models/flight_control/FGActuator.cpp | 10 +- .../models/flight_control/FGDeadBand.cpp | 2 + .../models/flight_control/FGFCSComponent.cpp | 28 ++- .../models/flight_control/FGFCSComponent.h | 1 + .../JSBSim/models/flight_control/FGFilter.cpp | 11 +- .../JSBSim/models/flight_control/FGGain.cpp | 2 + .../models/flight_control/FGKinemat.cpp | 2 + .../flight_control/FGLinearActuator.cpp | 2 + .../JSBSim/models/flight_control/FGPID.cpp | 4 +- .../JSBSim/models/propulsion/FGTurbine.cpp | 5 +- 18 files changed, 209 insertions(+), 98 deletions(-) diff --git a/src/FDM/JSBSim/FGFDMExec.cpp b/src/FDM/JSBSim/FGFDMExec.cpp index e215d7ebb..5204ccde6 100644 --- a/src/FDM/JSBSim/FGFDMExec.cpp +++ b/src/FDM/JSBSim/FGFDMExec.cpp @@ -731,6 +731,7 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath) } // Reload the planet constants and re-initialize the models. LoadPlanetConstants(); + IC->InitializeIC(); InitializeModels(); } diff --git a/src/FDM/JSBSim/initialization/FGInitialCondition.h b/src/FDM/JSBSim/initialization/FGInitialCondition.h index 3fff7a4ec..9190c4651 100644 --- a/src/FDM/JSBSim/initialization/FGInitialCondition.h +++ b/src/FDM/JSBSim/initialization/FGInitialCondition.h @@ -682,6 +682,9 @@ public: @return Trim type, if any requested (version 1). */ int TrimRequested(void) const { return trimRequested; } + /** Initialize the initial conditions to default values */ + void InitializeIC(void); + void bind(FGPropertyManager* pm); private: @@ -710,7 +713,6 @@ private: bool Load_v1(Element* document); bool Load_v2(Element* document); - void InitializeIC(void); void SetEulerAngleRadIC(int idx, double angle); void SetBodyVelFpsIC(int idx, double vel); void SetNEDVelFpsIC(int idx, double vel); diff --git a/src/FDM/JSBSim/math/FGTable.cpp b/src/FDM/JSBSim/math/FGTable.cpp index a63e7fef2..81a72f7e4 100644 --- a/src/FDM/JSBSim/math/FGTable.cpp +++ b/src/FDM/JSBSim/math/FGTable.cpp @@ -36,6 +36,8 @@ JSB 1/9/00 Created INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#include + #include "FGTable.h" #include "input_output/FGXMLElement.h" @@ -107,9 +109,23 @@ FGTable::FGTable(const FGTable& t) : PropertyManager(t.PropertyManager) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +unsigned int FindNumColumns(const string& test_line) +{ + // determine number of data columns in table (first column is row lookup - don't count) + size_t position=0; + unsigned int nCols=0; + while ((position = test_line.find_first_not_of(" \t", position)) != string::npos) { + nCols++; + position = test_line.find_first_of(" \t", position); + } + return nCols; +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + FGTable::FGTable(FGPropertyManager* propMan, Element* el, - const std::string& prefix) - : PropertyManager(propMan), Prefix(prefix) + const std::string& Prefix) + : PropertyManager(propMan) { unsigned int i; @@ -142,7 +158,7 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el, std::cerr << el->ReadFrom() <<" An unknown table type attribute is listed: " << call_type << endl; - throw("Execution cannot continue."); + throw TableException("Unknown table type."); } // Determine and store the lookup properties for this table unless this table @@ -157,10 +173,11 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el, // The 'internal' attribute of the table element cannot be specified // at the same time that independentVars are specified. if (internal) { - cerr << endl << fgred << " This table specifies both 'internal' call type" << endl; - cerr << " and specific lookup properties via the 'independentVar' element." << endl; - cerr << " These are mutually exclusive specifications. The 'internal'" << endl; - cerr << " attribute will be ignored." << fgdef << endl << endl; + cerr << el->ReadFrom() + << fgred << " This table specifies both 'internal' call type" << endl + << " and specific lookup properties via the 'independentVar' element." << endl + << " These are mutually exclusive specifications. The 'internal'" << endl + << " attribute will be ignored." << fgdef << endl << endl; internal = false; } @@ -182,7 +199,7 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el, } else if (lookup_axis == string("table")) { lookupProperty[eTable] = node; } else if (!lookup_axis.empty()) { - throw("Lookup table axis specification not understood: " + lookup_axis); + throw TableException("Lookup table axis specification not understood: " + lookup_axis); } else { // assumed single dimension table; row lookup lookupProperty[eRow] = node; } @@ -202,7 +219,8 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el, if (FindNumColumns(test_line) == 2) dimension = 1; // 1D table else if (FindNumColumns(test_line) > 2) dimension = 2; // 2D table else { - cerr << "Invalid number of columns in table" << endl; + std::cerr << tableData->ReadFrom() + << "Invalid number of columns in table" << endl; } } @@ -211,7 +229,10 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el, if (brkpt_string.empty()) { // no independentVars found, and table is not marked as internal, nor is it // a 3D table - throw("No independent variable found for table."); + std::cerr << el->ReadFrom() + << "No independentVars found, and table is not marked as internal," + << " nor is it a 3D table." << endl; + throw TableException("No independent variable found for table."); } } // end lookup property code @@ -243,9 +264,15 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el, if (nRows >= 2) { nCols = FindNumColumns(tableData->GetDataLine(0)); - if (nCols < 2) throw(string("Not enough columns in table data.")); + if (nCols < 2) { + std::cerr << tableData->ReadFrom() + << "Not enough columns in table data" << endl; + throw TableException("Not enough columns in table data."); + } } else { - throw(string("Not enough rows in the table data.")); + std::cerr << tableData->ReadFrom() + << "Not enough rows in table data" << endl; + throw TableException("Not enough rows in the table data."); } Type = tt2D; @@ -296,14 +323,14 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el, if (dimension > 2) { for (b=2; b<=nTables; ++b) { if (Data[b][1] <= Data[b-1][1]) { - stringstream errormsg; - errormsg << fgred << highint << endl - << " FGTable: breakpoint lookup is not monotonically increasing" << endl - << " in breakpoint " << b; - if (nameel != 0) errormsg << " of table in " << nameel->GetAttributeValue("name"); - errormsg << ":" << reset << endl - << " " << Data[b][1] << "<=" << Data[b-1][1] << endl; - throw(errormsg.str()); + std::cerr << el->ReadFrom() + << fgred << highint + << " FGTable: breakpoint lookup is not monotonically increasing" << endl + << " in breakpoint " << b; + if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name"); + std::cerr << ":" << reset << endl + << " " << Data[b][1] << "<=" << Data[b-1][1] << endl; + throw TableException("Breakpoint lookup is not monotonically increasing"); } } } @@ -312,14 +339,14 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el, if (dimension > 1) { for (c=2; c<=nCols; ++c) { if (Data[0][c] <= Data[0][c-1]) { - stringstream errormsg; - errormsg << fgred << highint << endl - << " FGTable: column lookup is not monotonically increasing" << endl - << " in column " << c; - if (nameel != 0) errormsg << " of table in " << nameel->GetAttributeValue("name"); - errormsg << ":" << reset << endl - << " " << Data[0][c] << "<=" << Data[0][c-1] << endl; - throw(errormsg.str()); + std::cerr << el->ReadFrom() + << fgred << highint + << " FGTable: column lookup is not monotonically increasing" << endl + << " in column " << c; + if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name"); + std::cerr << ":" << reset << endl + << " " << Data[0][c] << "<=" << Data[0][c-1] << endl; + throw TableException("FGTable: column lookup is not monotonically increasing"); } } } @@ -328,19 +355,19 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el, if (dimension < 3) { // in 3D tables, check only rows of subtables for (r=2; r<=nRows; ++r) { if (Data[r][0]<=Data[r-1][0]) { - stringstream errormsg; - errormsg << fgred << highint << endl - << " FGTable: row lookup is not monotonically increasing" << endl - << " in row " << r; - if (nameel != 0) errormsg << " of table in " << nameel->GetAttributeValue("name"); - errormsg << ":" << reset << endl - << " " << Data[r][0] << "<=" << Data[r-1][0] << endl; - throw(errormsg.str()); + std::cerr << el->ReadFrom() + << fgred << highint + << " FGTable: row lookup is not monotonically increasing" << endl + << " in row " << r; + if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name"); + std::cerr << ":" << reset << endl + << " " << Data[r][0] << "<=" << Data[r-1][0] << endl; + throw TableException("FGTable: row lookup is not monotonically increasing"); } } } - bind(el); + bind(el, Prefix); if (debug_lvl & 1) Print(); } @@ -363,6 +390,13 @@ double** FGTable::Allocate(void) FGTable::~FGTable() { + // Untie the bound property so that it makes no further reference to this + // instance of FGTable after the destruction is completed. + if (!Name.empty() && !internal) { + string tmp = mkPropertyName(nullptr, ""); + PropertyManager->Untie(tmp); + } + if (nTables > 0) { for (unsigned int i=0; igetDoubleValue(); temp2 = GetValue(temp); return temp2; case tt2D: + assert(lookupProperty[eRow]); + assert(lookupProperty[eColumn]); return GetValue(lookupProperty[eRow]->getDoubleValue(), lookupProperty[eColumn]->getDoubleValue()); case tt3D: + assert(lookupProperty[eRow]); + assert(lookupProperty[eColumn]); + assert(lookupProperty[eTable]); return GetValue(lookupProperty[eRow]->getDoubleValue(), lookupProperty[eColumn]->getDoubleValue(), lookupProperty[eTable]->getDoubleValue()); @@ -432,7 +458,7 @@ double FGTable::GetValue(double key) const } // the key is somewhere in the middle, search for the right breakpoint - // The search is particularly efficient if + // The search is particularly efficient if // the correct breakpoint has not changed since last frame or // has only changed very little @@ -508,7 +534,7 @@ double FGTable::GetValue(double rowKey, double colKey, double tableKey) const } // the key is somewhere in the middle, search for the right breakpoint - // The search is particularly efficient if + // The search is particularly efficient if // the correct breakpoint has not changed since last frame or // has only changed very little @@ -625,28 +651,33 @@ void FGTable::Print(void) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -void FGTable::bind(Element* el) +string FGTable::mkPropertyName(Element* el, const std::string& Prefix) +{ + if (!Prefix.empty()) { + if (is_number(Prefix)) { + if (Name.find("#") != string::npos) { // if "#" is found + Name = replace(Name, "#", Prefix); + } else { + cerr << el->ReadFrom() + << "Malformed table name with number: " << Prefix + << " and property name: " << Name + << " but no \"#\" sign for substitution." << endl; + } + } else { + Name = Prefix + "/" + Name; + } + } + + return PropertyManager->mkPropertyName(Name, false); +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +void FGTable::bind(Element* el, const string& Prefix) { typedef double (FGTable::*PMF)(void) const; if ( !Name.empty() && !internal) { - string tmp; - if (Prefix.empty()) - tmp = PropertyManager->mkPropertyName(Name, false); // Allow upper - else { - if (is_number(Prefix)) { - if (Name.find("#") != string::npos) { // if "#" is found - Name = replace(Name,"#",Prefix); - tmp = PropertyManager->mkPropertyName(Name, false); // Allow upper - } else { - cerr << el->ReadFrom() - << "Malformed table name with number: " << Prefix - << " and property name: " << Name - << " but no \"#\" sign for substitution." << endl; - } - } else { - tmp = PropertyManager->mkPropertyName(Prefix + "/" + Name, false); - } - } + string tmp = mkPropertyName(el, Prefix); if (PropertyManager->HasNode(tmp)) { FGPropertyNode* _property = PropertyManager->GetNode(tmp); diff --git a/src/FDM/JSBSim/math/FGTable.h b/src/FDM/JSBSim/math/FGTable.h index 12a0ae98d..d9cea9198 100644 --- a/src/FDM/JSBSim/math/FGTable.h +++ b/src/FDM/JSBSim/math/FGTable.h @@ -226,6 +226,19 @@ combustion_efficiency = Lookup_Combustion_Efficiency->GetValue(equivalence_ratio @author Jon S. Berndt */ +/** Exception convenience class. + */ + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +DECLARATION: TableException +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +class TableException : public std::runtime_error +{ +public: + TableException(const std::string& msg) : std::runtime_error{msg} { } +}; + /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CLASS DECLARATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ @@ -302,11 +315,10 @@ private: mutable int lastRowIndex, lastColumnIndex, lastTableIndex; double** Allocate(void); FGPropertyManager* const PropertyManager; - std::string Prefix; std::string Name; - void bind(Element*); + void bind(Element* el, const std::string& Prefix); - unsigned int FindNumColumns(const std::string&); + std::string mkPropertyName(Element* el, const std::string& Prefix); void Debug(int from); }; } diff --git a/src/FDM/JSBSim/models/FGAuxiliary.cpp b/src/FDM/JSBSim/models/FGAuxiliary.cpp index 68cee146a..67c5b905f 100644 --- a/src/FDM/JSBSim/models/FGAuxiliary.cpp +++ b/src/FDM/JSBSim/models/FGAuxiliary.cpp @@ -223,7 +223,7 @@ bool FGAuxiliary::Run(bool Holding) hoverbcg = in.DistanceAGL / in.Wingspan; FGColumnVector3 vMac = in.Tb2l * in.RPBody; - hoverbmac = (in.DistanceAGL + vMac(3)) / in.Wingspan; + hoverbmac = (in.DistanceAGL - vMac(3)) / in.Wingspan; return false; } diff --git a/src/FDM/JSBSim/models/FGInertial.cpp b/src/FDM/JSBSim/models/FGInertial.cpp index 730055de9..c1c624986 100644 --- a/src/FDM/JSBSim/models/FGInertial.cpp +++ b/src/FDM/JSBSim/models/FGInertial.cpp @@ -94,8 +94,12 @@ bool FGInertial::Load(Element* el) if (el->FindElement("semimajor_axis")) a = el->FindElementValueAsNumberConvertTo("semimajor_axis", "FT"); + else if (el->FindElement("equatorial_radius")) + a = el->FindElementValueAsNumberConvertTo("equatorial_radius", "FT"); if (el->FindElement("semiminor_axis")) b = el->FindElementValueAsNumberConvertTo("semiminor_axis", "FT"); + else if (el->FindElement("polar_radius")) + b = el->FindElementValueAsNumberConvertTo("polar_radius", "FT"); if (el->FindElement("rotation_rate")) { double RotationRate = el->FindElementValueAsNumberConvertTo("rotation_rate", "RAD/SEC"); vOmegaPlanet = {0., 0., RotationRate}; @@ -229,7 +233,7 @@ void FGInertial::SetGravityType(int gt) // Messages to warn the user about possible inconsistencies. switch (gt) { - case eGravType::gtStandard: + case eGravType::gtStandard: if (a != b) cout << "Warning: Standard gravity model has been set for a non-spherical planet" << endl; break; diff --git a/src/FDM/JSBSim/models/FGMassBalance.cpp b/src/FDM/JSBSim/models/FGMassBalance.cpp index bc0c7d33b..bf58c3d27 100644 --- a/src/FDM/JSBSim/models/FGMassBalance.cpp +++ b/src/FDM/JSBSim/models/FGMassBalance.cpp @@ -117,9 +117,14 @@ static FGMatrix33 ReadInertiaMatrix(Element* document) // Transform the inertia products from the structural frame to the body frame // and create the inertia matrix. - return FGMatrix33( bixx, -bixy, bixz, - -bixy, biyy, -biyz, - bixz, -biyz, bizz ); + if (document->GetAttributeValue("negated_crossproduct_inertia") == string("false")) + return FGMatrix33( bixx, bixy, -bixz, + bixy, biyy, biyz, + -bixz, biyz, bizz ); + else + return FGMatrix33( bixx, -bixy, bixz, + -bixy, biyy, -biyz, + bixz, -biyz, bizz ); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/FDM/JSBSim/models/FGMassBalance.h b/src/FDM/JSBSim/models/FGMassBalance.h index 2723c9325..3027342d0 100644 --- a/src/FDM/JSBSim/models/FGMassBalance.h +++ b/src/FDM/JSBSim/models/FGMassBalance.h @@ -65,12 +65,22 @@ CLASS DOCUMENTATION The inertia tensor must be specified in the structural frame (x axis positive aft, y axis positive out of the right wing and z axis upward). The - sign of the inertia cross products are not modified by JSBSim so in most - cases, negative values should be provided for , and . - + sign of the inertia cross products are optional by JSBSim. + if negated_crossproduct_inertia == "true", then define: + ixy = -integral( x * y * dm ), + ixz = -integral( x * z * dm ), + iyz = -integral( y * z * dm ). + else if negated_crossproduct_inertia == "false", then define: + ixy = integral( x * y * dm ), + ixz = integral( x * z * dm ), + iyz = integral( y * z * dm ). + default is negated_crossproduct_inertia = "true". + We strongly recommend defining negated_crossproduct_inertia = "false", + which is consistent with the specifications in the field of flight dynamics. +

Configuration File Format for \ Section:

@code{.xml} - + {number} {number} {number} @@ -98,6 +108,11 @@ CLASS DOCUMENTATION ... other point masses ...] @endcode + + @see Stevens and Lewis, "Flight Control & Simulation" + @see Bernard Etkin, " Dynamics Of Atmosferic Flight" + @see https://en.wikipedia.org/wiki/Moment_of_inertia#Inertia_tensor + @see https://www.mathworks.com/help/physmod/sm/ug/specify-custom-inertia.html */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/FDM/JSBSim/models/flight_control/FGActuator.cpp b/src/FDM/JSBSim/models/flight_control/FGActuator.cpp index bb7cecc93..b0cc9d4cf 100644 --- a/src/FDM/JSBSim/models/flight_control/FGActuator.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGActuator.cpp @@ -67,6 +67,8 @@ FGActuator::FGActuator(FGFCS* fcs, Element* element) initialized = 0; saturated = false; + CheckInputNodes(1, 1, element); + if ( element->FindElement("deadband_width") ) { deadband_width = element->FindElementValueAsNumber("deadband_width"); } @@ -74,7 +76,7 @@ FGActuator::FGActuator(FGFCS* fcs, Element* element) hysteresis_width = element->FindElementValueAsNumber("hysteresis_width"); } - // There can be a single rate limit specified, or increasing and + // There can be a single rate limit specified, or increasing and // decreasing rate limits specified, and rate limits can be numeric, or // a property. Element* ratelim_el = element->FindElement("rate_limit"); @@ -133,7 +135,7 @@ void FGActuator::ResetPastStates(void) FGFCSComponent::ResetPastStates(); PreviousOutput = PreviousHystOutput = PreviousRateLimOutput - = PreviousLagInput = PreviousLagOutput = Output = 0.0; + = PreviousLagInput = PreviousLagOutput = Output = 0.0; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -166,7 +168,7 @@ bool FGActuator::Run(void ) } PreviousOutput = Output; // previous value needed for "stuck" malfunction - + initialized = 1; Clip(); @@ -223,7 +225,7 @@ void FGActuator::Hysteresis(void) // "Output" is - for the purposes of this Hysteresis method - really the input // to the method. double input = Output; - + if ( initialized ) { if (input > PreviousHystOutput) Output = max(PreviousHystOutput, input-0.5*hysteresis_width); diff --git a/src/FDM/JSBSim/models/flight_control/FGDeadBand.cpp b/src/FDM/JSBSim/models/flight_control/FGDeadBand.cpp index 82cf8df69..b60fe8c3e 100644 --- a/src/FDM/JSBSim/models/flight_control/FGDeadBand.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGDeadBand.cpp @@ -56,6 +56,8 @@ FGDeadBand::FGDeadBand(FGFCS* fcs, Element* element) Width = nullptr; gain = 1.0; + CheckInputNodes(1, 1, element); + Element* width_element = element->FindElement("width"); if (width_element) Width = new FGParameterValue(width_element, PropertyManager); diff --git a/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp b/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp index 8f37b40c8..dbfe6cc46 100644 --- a/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp @@ -116,7 +116,7 @@ FGFCSComponent::FGFCSComponent(FGFCS* _fcs, Element* element) : fcs(_fcs) PropertyManager )); init_element = element->FindNextElement("init"); } - + Element *input_element = element->FindElement("input"); while (input_element) { InputNodes.push_back(new FGPropertyValue(input_element->GetDataLine(), @@ -212,6 +212,30 @@ void FGFCSComponent::ResetPastStates(void) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +void FGFCSComponent::CheckInputNodes(size_t MinNodes, size_t MaxNodes, Element* el) +{ + size_t num = InputNodes.size(); + + if (num < MinNodes) { + cerr << el->ReadFrom() + << " Not enough nodes are provided" << endl + << " Expecting " << MinNodes << " while " << num + << " are provided." << endl; + throw("Some inputs are missing."); + } + + if (num > MaxNodes) { + cerr << el->ReadFrom() + << " Too many nodes are provided" << endl + << " Expecting " << MaxNodes << " while " << num + << " are provided." << endl + << " The last " << num-MaxNodes << " input nodes will be ignored." + << endl; + } +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + void FGFCSComponent::SetOutput(void) { for (auto node: OutputNodes) @@ -329,7 +353,7 @@ void FGFCSComponent::Debug(int from) if (clip) { cout << " Minimum limit: " << ClipMin->GetName() << endl; cout << " Maximum limit: " << ClipMax->GetName() << endl; - } + } if (delay > 0) cout <<" Frame delay: " << delay << " frames (" << delay*dt << " sec)" << endl; } diff --git a/src/FDM/JSBSim/models/flight_control/FGFCSComponent.h b/src/FDM/JSBSim/models/flight_control/FGFCSComponent.h index f7bc735e9..8beb6e946 100644 --- a/src/FDM/JSBSim/models/flight_control/FGFCSComponent.h +++ b/src/FDM/JSBSim/models/flight_control/FGFCSComponent.h @@ -117,6 +117,7 @@ protected: void Delay(void); void Clip(void); + void CheckInputNodes(size_t MinNodes, size_t MaxNodes, Element* el); virtual void bind(Element* el); virtual void Debug(int from); }; diff --git a/src/FDM/JSBSim/models/flight_control/FGFilter.cpp b/src/FDM/JSBSim/models/flight_control/FGFilter.cpp index f25e0ef74..ddd009d72 100644 --- a/src/FDM/JSBSim/models/flight_control/FGFilter.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGFilter.cpp @@ -52,6 +52,9 @@ FGFilter::FGFilter(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element), DynamicFilter(false), Initialize(true) { C[1] = C[2] = C[3] = C[4] = C[5] = C[6] = nullptr; + + CheckInputNodes(1, 1, element); + for (int i=1; i<7; i++) ReadFilterCoefficients(element, i); @@ -88,11 +91,11 @@ void FGFilter::ResetPastStates(void) void FGFilter::ReadFilterCoefficients(Element* element, int index) { - // index is known to be 1-7. + // index is known to be 1-7. // A stringstream would be overkill, but also trying to avoid sprintf string coefficient = "c0"; coefficient[1] += index; - + if ( element->FindElement(coefficient) ) { C[index] = new FGParameterValue(element->FindElement(coefficient), PropertyManager); @@ -151,9 +154,9 @@ bool FGFilter::Run(void) } else { Input = InputNodes[0]->getDoubleValue(); - + if (DynamicFilter) CalculateDynamicFilters(); - + switch (FilterType) { case eLag: Output = (Input + PreviousInput1) * ca + PreviousOutput1 * cb; diff --git a/src/FDM/JSBSim/models/flight_control/FGGain.cpp b/src/FDM/JSBSim/models/flight_control/FGGain.cpp index f61808d05..a64305f42 100644 --- a/src/FDM/JSBSim/models/flight_control/FGGain.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGGain.cpp @@ -57,6 +57,8 @@ FGGain::FGGain(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element) InMax = 1.0; OutMin = OutMax = 0.0; + CheckInputNodes(1, 1, element); + if (Type == "PURE_GAIN") { if ( !element->FindElement("gain") ) { cerr << element->ReadFrom() diff --git a/src/FDM/JSBSim/models/flight_control/FGKinemat.cpp b/src/FDM/JSBSim/models/flight_control/FGKinemat.cpp index a42ccecf1..4a8c1e4bc 100644 --- a/src/FDM/JSBSim/models/flight_control/FGKinemat.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGKinemat.cpp @@ -56,6 +56,8 @@ FGKinemat::FGKinemat(FGFCS* fcs, Element* element) double tmpDetent; double tmpTime; + CheckInputNodes(1, 1, element); + Detents.clear(); TransitionTimes.clear(); diff --git a/src/FDM/JSBSim/models/flight_control/FGLinearActuator.cpp b/src/FDM/JSBSim/models/flight_control/FGLinearActuator.cpp index 1f5223585..b18ca841a 100644 --- a/src/FDM/JSBSim/models/flight_control/FGLinearActuator.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGLinearActuator.cpp @@ -51,6 +51,8 @@ CLASS IMPLEMENTATION FGLinearActuator::FGLinearActuator(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element) { + CheckInputNodes(1, 1, element); + ptrSet = nullptr; if (element->FindElement("set")) { string property_string = element->FindElementValue("set"); diff --git a/src/FDM/JSBSim/models/flight_control/FGPID.cpp b/src/FDM/JSBSim/models/flight_control/FGPID.cpp index 28dc7d4b5..fc06985bd 100644 --- a/src/FDM/JSBSim/models/flight_control/FGPID.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGPID.cpp @@ -57,6 +57,8 @@ FGPID::FGPID(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element) IsStandard = false; IntType = eNone; // No integrator initially defined. + CheckInputNodes(1, 1, element); + string pid_type = element->GetAttributeValue("type"); if (pid_type == "standard") IsStandard = true; @@ -190,7 +192,7 @@ bool FGPID::Run(void ) } if (test < 0.0) I_out_total = 0.0; // Reset integrator to 0.0 - + I_out_total += Ki->GetValue() * dt * I_out_delta; if (IsStandard) diff --git a/src/FDM/JSBSim/models/propulsion/FGTurbine.cpp b/src/FDM/JSBSim/models/propulsion/FGTurbine.cpp index 6d77e2ca7..75b01106e 100644 --- a/src/FDM/JSBSim/models/propulsion/FGTurbine.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGTurbine.cpp @@ -190,8 +190,9 @@ double FGTurbine::Off(void) FuelFlow_pph = Seek(&FuelFlow_pph, 0, 1000.0, 10000.0); // some engines have inlets that close when they are off. So, if a flag is true disable windmilling if (disableWindmill == false) { - N1 = Seek(&N1, in.qbar/10.0, N1/2.0, N1/N1_spindown); - N2 = Seek(&N2, in.qbar/15.0, N2/2.0, N2/N2_spindown); + // Need a small non-zero increment for acceleration otherwise acceleration will be 0 if N1 = 0 + N1 = Seek(&N1, in.qbar/10.0, N1/2.0 + 0.1, N1/N1_spindown); + N2 = Seek(&N2, in.qbar/15.0, N2/2.0 + 0.1, N2/N2_spindown); } else { N1 = Seek(&N1, 0, N1/2.0, N1/N1_spindown); N2 = Seek(&N2, 0, N2/2.0, N2/N2_spindown);