Sync'ed with JSBSim v1.1.10
- Fixed an error which prevented the equatorial and polar radii from <planet> to be propagated to the initial conditions. - The planet radii in <planet> can now be specified by the tags <equatorial_radius> and <polar_radius> which are more self explanatory than <semimajor_axis> and <semiminor_axis> (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 <input> tags for flight controls such as <pure_gain> (GitHub issue #497). This avoids a crash when some or all <input> elements are missing. - JSBSim now accepts 2 sign conventions for the cross product inertia (xy, xz, yz) in <mass_balance> (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.
This commit is contained in:
parent
5023e9786a
commit
e5fe747662
18 changed files with 209 additions and 98 deletions
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -36,6 +36,8 @@ JSB 1/9/00 Created
|
|||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#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; i<nTables; i++) delete Tables[i];
|
||||
Tables.clear();
|
||||
|
@ -375,20 +409,6 @@ FGTable::~FGTable()
|
|||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
unsigned int FGTable::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;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGTable::GetValue(void) const
|
||||
{
|
||||
double temp = 0;
|
||||
|
@ -396,13 +416,19 @@ double FGTable::GetValue(void) const
|
|||
|
||||
switch (Type) {
|
||||
case tt1D:
|
||||
assert(lookupProperty[eRow]);
|
||||
temp = lookupProperty[eRow]->getDoubleValue();
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
|
|
@ -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 <ixy>, <ixz> and <iyz>.
|
||||
|
||||
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.
|
||||
|
||||
<h3>Configuration File Format for \<mass_balance> Section:</h3>
|
||||
@code{.xml}
|
||||
<mass_balance>
|
||||
<mass_balance negated_crossproduct_inertia="true|false">
|
||||
<ixx unit="{SLUG*FT2 | KG*M2}"> {number} </ixx>
|
||||
<iyy unit="{SLUG*FT2 | KG*M2}"> {number} </iyy>
|
||||
<izz unit="{SLUG*FT2 | KG*M2}"> {number} </izz>
|
||||
|
@ -98,6 +108,11 @@ CLASS DOCUMENTATION
|
|||
... other point masses ...]
|
||||
</mass_balance>
|
||||
@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
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 <input> nodes are provided" << endl
|
||||
<< " Expecting " << MinNodes << " while " << num
|
||||
<< " are provided." << endl;
|
||||
throw("Some inputs are missing.");
|
||||
}
|
||||
|
||||
if (num > MaxNodes) {
|
||||
cerr << el->ReadFrom()
|
||||
<< " Too many <input> 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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -56,6 +56,8 @@ FGKinemat::FGKinemat(FGFCS* fcs, Element* element)
|
|||
double tmpDetent;
|
||||
double tmpTime;
|
||||
|
||||
CheckInputNodes(1, 1, element);
|
||||
|
||||
Detents.clear();
|
||||
TransitionTimes.clear();
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue