1
0
Fork 0

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:
Bertrand Coconnier 2021-11-20 18:23:34 +01:00
parent 5023e9786a
commit e5fe747662
18 changed files with 209 additions and 98 deletions

View file

@ -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();
}

View file

@ -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);

View file

@ -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);

View file

@ -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);
};
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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 );
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -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
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
};

View file

@ -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;

View file

@ -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()

View file

@ -56,6 +56,8 @@ FGKinemat::FGKinemat(FGFCS* fcs, Element* element)
double tmpDetent;
double tmpTime;
CheckInputNodes(1, 1, element);
Detents.clear();
TransitionTimes.clear();

View file

@ -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");

View file

@ -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)

View file

@ -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);