New features for JSBSim (not to be included in release 2018.3)
The following new features have been added to JSBSim: * Added the ability to set up the starter and acceleration times of a turbine (parameters <n1spinup>, <n2spinup>, <n1startrate>, <n2startrate>). * The <integrator> filter can now be reset to 0.0 by setting its <trigger> property to a negative value. * The integration scheme of the <integrator> filter can now be chosen among "rect" (Euler), "trap" (Trapezoidal), "ab2" 2nd order Adams-BashForth and "ab3" 3rd order Adams-Bashforth * The following functions can now be used in <function>: floor, ceil and fmod. Their functionalities are the same than the corresponding C/C++ functions.
This commit is contained in:
parent
94e1cdc551
commit
04eb045931
8 changed files with 133 additions and 48 deletions
|
@ -177,6 +177,7 @@ public:
|
|||
/** Retrieves the element name.
|
||||
@return the element name, or the empty string if no name has been set.*/
|
||||
const std::string& GetName(void) const {return name;}
|
||||
void ChangeName(const std::string& _name) { name = _name; }
|
||||
|
||||
/** Gets a line of data belonging to an element.
|
||||
@param i the index of the data line to return (0 by default).
|
||||
|
|
|
@ -107,6 +107,9 @@ const std::string FGFunction::not_string = "not";
|
|||
const std::string FGFunction::ifthen_string = "ifthen";
|
||||
const std::string FGFunction::switch_string = "switch";
|
||||
const std::string FGFunction::interpolate1d_string = "interpolate1d";
|
||||
const std::string FGFunction::floor_string = "floor";
|
||||
const std::string FGFunction::ceil_string = "ceil";
|
||||
const std::string FGFunction::fmod_string = "fmod";
|
||||
|
||||
FGFunction::FGFunction(FGPropertyManager* PropertyManager, Element* el,
|
||||
const string& prefix, FGPropertyValue* var)
|
||||
|
@ -228,6 +231,12 @@ void FGFunction::Load(FGPropertyManager* PropertyManager, Element* el,
|
|||
Type = eSwitch;
|
||||
} else if (operation == interpolate1d_string) {
|
||||
Type = eInterpolate1D;
|
||||
} else if (operation == floor_string) {
|
||||
Type = eFloor;
|
||||
} else if (operation == ceil_string) {
|
||||
Type = eCeil;
|
||||
} else if (operation == fmod_string) {
|
||||
Type = eFmod;
|
||||
} else if (operation != description_string) {
|
||||
cerr << "Bad operation " << operation << " detected in configuration file" << endl;
|
||||
}
|
||||
|
@ -324,7 +333,10 @@ void FGFunction::Load(FGPropertyManager* PropertyManager, Element* el,
|
|||
operation == not_string ||
|
||||
operation == ifthen_string ||
|
||||
operation == switch_string ||
|
||||
operation == interpolate1d_string)
|
||||
operation == interpolate1d_string ||
|
||||
operation == floor_string ||
|
||||
operation == ceil_string ||
|
||||
operation == fmod_string)
|
||||
{
|
||||
Parameters.push_back(new FGFunction(PropertyManager, element, Prefix, var));
|
||||
} else if (operation != description_string) {
|
||||
|
@ -767,6 +779,18 @@ double FGFunction::GetValue(void) const
|
|||
else //
|
||||
{temp = 1;}
|
||||
break;
|
||||
case eFloor:
|
||||
temp = floor(temp);
|
||||
break;
|
||||
case eCeil:
|
||||
temp = ceil(temp);
|
||||
break;
|
||||
case eFmod:
|
||||
if (Parameters[1]->GetValue() != 0.0)
|
||||
temp = fmod(temp, Parameters[1]->GetValue());
|
||||
else
|
||||
temp = HUGE_VAL;
|
||||
break;
|
||||
default:
|
||||
cerr << "Unknown function operation type" << endl;
|
||||
break;
|
||||
|
|
|
@ -84,6 +84,9 @@ A function definition consists of an operation, a value, a table, or a property
|
|||
- avg (takes n args)
|
||||
- fraction
|
||||
- mod
|
||||
- floor (takes 1 arg)
|
||||
- ceil (takes 1 arg)
|
||||
- fmod (takes 2 args)
|
||||
- lt (less than, takes 2 args)
|
||||
- le (less equal, takes 2 args)
|
||||
- gt (greater than, takes 2 args)
|
||||
|
@ -466,13 +469,36 @@ of the three words refers to one or more instances of a property, value, or tabl
|
|||
quotient of 3 and leaves a remainder of 0.
|
||||
@code
|
||||
<mod>
|
||||
{property, value, table, or other function element} {property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</mod>
|
||||
|
||||
Example: 5 mod 2, evaluates to 1
|
||||
|
||||
<mod> <v> 5 </v> <v> 2 </v> </mod>
|
||||
@endcode
|
||||
- @b floor returns the largest integral value that is not greater than X.
|
||||
@code
|
||||
<floor>
|
||||
{property, value, table, or other function element}
|
||||
</floor>
|
||||
@endcode
|
||||
Examples: floor(2.3) evaluates to 2.0 while floor(-2.3) evaluates to -3.0
|
||||
- @b ceil returns the smallest integral value that is not less than X.
|
||||
@code
|
||||
<ceil>
|
||||
{property, value, table, or other function element}
|
||||
</ceil>
|
||||
@endcode
|
||||
Examples: ceil(2.3) evaluates to 3.0 while ceil(-2.3) evaluates to -2.0
|
||||
- @b fmod returns the floating-point remainder of X/Y (rounded towards zero)
|
||||
@code
|
||||
<fmod>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</fmod>
|
||||
@endcode
|
||||
Example: fmod(18.5, 4.2) evaluates to 1.7
|
||||
- @b lt returns a 1 if the value of the first immediate child element is less
|
||||
than the value of the second immediate child element, returns 0
|
||||
otherwise
|
||||
|
@ -799,6 +825,9 @@ private:
|
|||
static const std::string ifthen_string;
|
||||
static const std::string switch_string;
|
||||
static const std::string interpolate1d_string;
|
||||
static const std::string floor_string;
|
||||
static const std::string ceil_string;
|
||||
static const std::string fmod_string;
|
||||
|
||||
enum functionType {eTopLevel=0, eProduct, eDifference, eSum, eQuotient, ePow, eSqrt, eToRadians,
|
||||
eToDegrees, eExp, eAbs, eSign, eSin, eCos, eTan, eASin, eACos, eATan, eATan2,
|
||||
|
@ -806,7 +835,7 @@ private:
|
|||
eLog2, eLn, eLog10, eLT, eLE, eGE, eGT, eEQ, eNE, eAND, eOR, eNOT,
|
||||
eIfThen, eSwitch, eInterpolate1D, eRotation_alpha_local,
|
||||
eRotation_beta_local, eRotation_gamma_local, eRotation_bf_to_wf,
|
||||
eRotation_wf_to_bf} Type;
|
||||
eRotation_wf_to_bf, eFloor, eCeil, eFmod} Type;
|
||||
std::string Prefix;
|
||||
bool cached;
|
||||
double cachedValue;
|
||||
|
|
|
@ -534,8 +534,7 @@ bool FGFCS::Load(Element* document)
|
|||
if ((component_element->GetName() == string("lag_filter")) ||
|
||||
(component_element->GetName() == string("lead_lag_filter")) ||
|
||||
(component_element->GetName() == string("washout_filter")) ||
|
||||
(component_element->GetName() == string("second_order_filter")) ||
|
||||
(component_element->GetName() == string("integrator")) )
|
||||
(component_element->GetName() == string("second_order_filter")) )
|
||||
{
|
||||
newChannel->Add(new FGFilter(this, component_element));
|
||||
} else if ((component_element->GetName() == string("pure_gain")) ||
|
||||
|
@ -555,6 +554,18 @@ bool FGFCS::Load(Element* document)
|
|||
newChannel->Add(new FGFCSFunction(this, component_element));
|
||||
} else if (component_element->GetName() == string("pid")) {
|
||||
newChannel->Add(new FGPID(this, component_element));
|
||||
} else if (component_element->GetName() == string("integrator")) {
|
||||
// <integrator> is equivalent to <pid type="trap">
|
||||
Element* c1_el = component_element->FindElement("c1");
|
||||
if (!c1_el) {
|
||||
cerr << component_element->ReadFrom();
|
||||
throw("INTEGRATOR component " + component_element->GetAttributeValue("name")
|
||||
+ " does not provide the parameter <c1>");
|
||||
}
|
||||
c1_el->ChangeName("ki");
|
||||
if (!c1_el->HasAttribute("type"))
|
||||
c1_el->AddAttribute("type", "trap");
|
||||
newChannel->Add(new FGPID(this, component_element));
|
||||
} else if (component_element->GetName() == string("actuator")) {
|
||||
newChannel->Add(new FGActuator(this, component_element));
|
||||
} else if (component_element->GetName() == string("sensor")) {
|
||||
|
|
|
@ -68,7 +68,6 @@ FGFilter::FGFilter(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
|
|||
else if (Type == "LEAD_LAG_FILTER") FilterType = eLeadLag ;
|
||||
else if (Type == "SECOND_ORDER_FILTER") FilterType = eOrder2 ;
|
||||
else if (Type == "WASHOUT_FILTER") FilterType = eWashout ;
|
||||
else if (Type == "INTEGRATOR") FilterType = eIntegrator ;
|
||||
else FilterType = eUnknown ;
|
||||
|
||||
if (element->FindElement("trigger")) {
|
||||
|
@ -170,10 +169,6 @@ void FGFilter::CalculateDynamicFilters(void)
|
|||
ca = 2.00 / denom;
|
||||
cb = (2.00 - dt*C[1]) / denom;
|
||||
break;
|
||||
case eIntegrator:
|
||||
if (PropertyNode[1] != 0L) C[1] = PropertyNode[1]->getDoubleValue()*PropertySign[1];
|
||||
ca = dt*C[1] / 2.00;
|
||||
break;
|
||||
case eUnknown:
|
||||
cerr << "Unknown filter type" << endl;
|
||||
break;
|
||||
|
@ -210,15 +205,6 @@ bool FGFilter::Run(void)
|
|||
case eWashout:
|
||||
Output = Input * ca - PreviousInput1 * ca + PreviousOutput1 * cb;
|
||||
break;
|
||||
case eIntegrator:
|
||||
if (Trigger != 0) {
|
||||
double test = Trigger->getDoubleValue();
|
||||
if (fabs(test) > 0.000001) {
|
||||
Input = PreviousInput1 = PreviousInput2 = 0.0;
|
||||
}
|
||||
}
|
||||
Output = Input * ca + PreviousInput1 * ca + PreviousOutput1;
|
||||
break;
|
||||
case eUnknown:
|
||||
break;
|
||||
}
|
||||
|
@ -321,12 +307,6 @@ void FGFilter::Debug(int from)
|
|||
if (PropertyNode[1] == 0L) cout << " C[1]: " << C[1] << endl;
|
||||
else cout << " C[1] is the value of property: " << sgn << PropertyNode[1]->GetName() << endl;
|
||||
break;
|
||||
case eIntegrator:
|
||||
if (PropertySign[1] < 0.0) sgn="-";
|
||||
else sgn = "";
|
||||
if (PropertyNode[1] == 0L) cout << " C[1]: " << C[1] << endl;
|
||||
else cout << " C[1] is the value of property: " << sgn << PropertyNode[1]->GetName() << endl;
|
||||
break;
|
||||
case eUnknown:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -199,15 +199,15 @@ For an integrator of the form:
|
|||
The corresponding filter definition is:
|
||||
|
||||
@code
|
||||
<integrator name="name">
|
||||
<input> property </input>
|
||||
<c1> value|property </c1>
|
||||
[<trigger> property </trigger>]
|
||||
<integrator name="{string}">
|
||||
<input> {property} </input>
|
||||
<c1 type="rect|trap|ab2|ab3"> {[-]property | number} </c1>
|
||||
[<trigger> {property} </trigger>]
|
||||
[<clipto>
|
||||
<min> {[-]property name | value} </min>
|
||||
<max> {[-]property name | value} </max>
|
||||
<min> {[-]property | number} </min>
|
||||
<max> {[-]property | number} </max>
|
||||
</clipto>]
|
||||
[<output> property </output>]
|
||||
[<output> {property} </output>]
|
||||
</integrator>
|
||||
@endcode
|
||||
|
||||
|
@ -217,12 +217,32 @@ property value is:
|
|||
- not 0: (or simply greater than zero), all current and previous inputs will
|
||||
be set to 0.0
|
||||
|
||||
By default, the integration scheme is the trapezoidal scheme.
|
||||
|
||||
An integrator is equivalent to a PID with the following parameters:
|
||||
@code
|
||||
<pid name="{string}">
|
||||
<input> {[-]property} </input>
|
||||
<kp> 0.0 </kp>
|
||||
<ki type="rect|trap|ab2|ab3"> {number|[-]property} </ki>
|
||||
<kd> 0.0 </kd>
|
||||
<trigger> {property} </trigger>
|
||||
[<clipto>
|
||||
<min> {[-]property | value} </min>
|
||||
<max> {[-]property | value} </max>
|
||||
</clipto>]
|
||||
[<output> {property} </output>]
|
||||
</pid>
|
||||
@endcode
|
||||
|
||||
As a consequence, JSBSim internally uses PID controllers to simulate INTEGRATOR
|
||||
filters.
|
||||
|
||||
In all the filter specifications above, an \<output> element is also seen. This
|
||||
is so that the last component in a "string" can copy its value to the appropriate
|
||||
output, such as the elevator, or speedbrake, etc.
|
||||
is so that the last component in a "string" can copy its value to the
|
||||
appropriate output, such as the elevator, or speedbrake, etc.
|
||||
|
||||
@author Jon S. Berndt
|
||||
@version $Revision: 1.14 $
|
||||
|
||||
*/
|
||||
|
||||
|
@ -243,7 +263,7 @@ public:
|
|||
bool Initialize;
|
||||
void ResetPastStates(void);
|
||||
|
||||
enum {eLag, eLeadLag, eOrder2, eWashout, eIntegrator, eUnknown} FilterType;
|
||||
enum {eLag, eLeadLag, eOrder2, eWashout, eUnknown} FilterType;
|
||||
|
||||
private:
|
||||
double ca;
|
||||
|
|
|
@ -70,7 +70,7 @@ FGTurbine::FGTurbine(FGFDMExec* exec, Element *el, int engine_number, struct Inp
|
|||
Augmented = AugMethod = Injected = 0;
|
||||
BypassRatio = BleedDemand = 0.0;
|
||||
IdleThrustLookup = MilThrustLookup = MaxThrustLookup = InjectionLookup = 0;
|
||||
N1_spinup = 1.0; N2_spinup = 3.0;
|
||||
N1_spinup = 1.0; N2_spinup = 3.0; IgnitionN1 = 5.21; IgnitionN2 = 25.18; N1_start_rate = 1.4; N2_start_rate = 2.0;
|
||||
InjectionTime = 30.0;
|
||||
InjectionTimer = InjWaterNorm = 0.0;
|
||||
EPR = 1.0;
|
||||
|
@ -281,8 +281,8 @@ double FGTurbine::SpinUp(void)
|
|||
{
|
||||
Running = false;
|
||||
FuelFlow_pph = 0.0;
|
||||
N2 = Seek(&N2, 25.18, N2_spinup, N2/2.0);
|
||||
N1 = Seek(&N1, 5.21, N1_spinup, N1/2.0);
|
||||
N2 = Seek(&N2, IgnitionN2, N2_spinup, N2/2.0);
|
||||
N1 = Seek(&N1, IgnitionN1, N1_spinup, N1/2.0);
|
||||
EGT_degC = Seek(&EGT_degC, in.TAT_c, 11.7, 7.3);
|
||||
OilPressure_psi = N2 * 0.62;
|
||||
OilTemp_degK = Seek(&OilTemp_degK, in.TAT_c + 273.0, 0.2, 0.2);
|
||||
|
@ -299,8 +299,8 @@ double FGTurbine::Start(void)
|
|||
if ((N2 > 15.0) && !Starved) { // minimum 15% N2 needed for start
|
||||
Cranking = true; // provided for sound effects signal
|
||||
if (N2 < IdleN2) {
|
||||
N2 = Seek(&N2, IdleN2, 2.0, N2/2.0);
|
||||
N1 = Seek(&N1, IdleN1, 1.4, N1/2.0);
|
||||
N2 = Seek(&N2, IdleN2, N2_start_rate, N2/2.0);
|
||||
N1 = Seek(&N1, IdleN1, N1_start_rate, N1/2.0);
|
||||
EGT_degC = Seek(&EGT_degC, in.TAT_c + 363.1, 21.3, 7.3);
|
||||
FuelFlow_pph = IdleFF * N2 / IdleN2;
|
||||
OilPressure_psi = N2 * 0.62;
|
||||
|
@ -448,6 +448,10 @@ bool FGTurbine::Load(FGFDMExec* exec, Element *el)
|
|||
TSFC = el->FindElementValueAsNumber("tsfc");
|
||||
if (el->FindElement("atsfc"))
|
||||
ATSFC = el->FindElementValueAsNumber("atsfc");
|
||||
if (el->FindElement("ignitionn1"))
|
||||
IgnitionN1 = el->FindElementValueAsNumber("ignitionn1");
|
||||
if (el->FindElement("ignitionn2"))
|
||||
IgnitionN1 = el->FindElementValueAsNumber("ignitionn2");
|
||||
if (el->FindElement("idlen1"))
|
||||
IdleN1 = el->FindElementValueAsNumber("idlen1");
|
||||
if (el->FindElement("idlen2"))
|
||||
|
@ -460,6 +464,10 @@ bool FGTurbine::Load(FGFDMExec* exec, Element *el)
|
|||
N1_spinup = el->FindElementValueAsNumber("n1spinup");
|
||||
if (el->FindElement("n2spinup"))
|
||||
N2_spinup = el->FindElementValueAsNumber("n2spinup");
|
||||
if (el->FindElement("n1startrate"))
|
||||
N1_start_rate = el->FindElementValueAsNumber("n1startrate");
|
||||
if (el->FindElement("n2startrate"))
|
||||
N2_start_rate = el->FindElementValueAsNumber("n2startrate");
|
||||
if (el->FindElement("augmented"))
|
||||
Augmented = (int)el->FindElementValueAsNumber("augmented");
|
||||
if (el->FindElement("augmethod"))
|
||||
|
|
|
@ -66,8 +66,8 @@ CLASS DOCUMENTATION
|
|||
<P>
|
||||
- STARTING (on ground):
|
||||
-# Set the control FGEngine::Starter to true. The engine will spin up to
|
||||
a maximum of about %25 N2 (%5.2 N1). This simulates the action of a
|
||||
pneumatic starter.
|
||||
a maximum of about %25 N2 (%5.2 N1). This value may be changed using the <startnX> parameter.
|
||||
This simulates the action of a pneumatic starter.
|
||||
-# After reaching %15 N2 set the control FGEngine::Cutoff to false. If fuel
|
||||
is available the engine will now accelerate to idle. The starter will
|
||||
automatically be set to false after the start cycle.
|
||||
|
@ -78,7 +78,7 @@ CLASS DOCUMENTATION
|
|||
-# Place the control FGEngine::Cutoff to false.
|
||||
<P>
|
||||
Ignition is assumed to be on anytime the Cutoff control is set to false,
|
||||
therefore a seperate ignition system is not modeled.
|
||||
therefore a separate ignition system is not modeled.
|
||||
|
||||
<h3>Configuration File Format:</h3>
|
||||
@code
|
||||
|
@ -89,10 +89,14 @@ CLASS DOCUMENTATION
|
|||
<bleed> {number} </bleed>
|
||||
<tsfc> {number} </tsfc>
|
||||
<atsfc> {number} </atsfc>
|
||||
<ignitionn1> {number} </ignitionn1>
|
||||
<ignitionn2> {number} </ignitionn2>
|
||||
<idlen1> {number} </idlen1>
|
||||
<idlen2> {number} </idlen2>
|
||||
<n1spinup> {number} </n1spinup>
|
||||
<n2spinup> {number} </n2spinup>
|
||||
<n1startrate> {number} </n1startrate>
|
||||
<n2startrate> {number} </n2startrate>
|
||||
<maxn1> {number} </maxn1>
|
||||
<maxn2> {number} </maxn2>
|
||||
<augmented> {0 | 1} </augmented>
|
||||
|
@ -111,10 +115,14 @@ CLASS DOCUMENTATION
|
|||
bleed - Thrust reduction factor due to losses (0.0 to 1.0).
|
||||
tsfc - Thrust-specific fuel consumption at cruise, lbm/hr/lbf
|
||||
atsfc - Afterburning TSFC, lbm/hr/lbf
|
||||
ignitionn1 - Fan rotor rpm (% of max) while starting
|
||||
ignitionn2 - Core rotor rpm (% of max) while starting
|
||||
idlen1 - Fan rotor rpm (% of max) at idle
|
||||
idlen2 - Core rotor rpm (% of max) at idle
|
||||
n1spinup - Fan rotor rpm starter acceleration (default 1.0)
|
||||
n2spinup - Core rotor rpm starter acceleration (default 3.0)
|
||||
n1spinup - Fan rotor rpm starter acceleration to ignitionn1 value (default 1.0)
|
||||
n2spinup - Core rotor rpm starter acceleration to ignitionn2 value (default 3.0)
|
||||
n1startrate - Fan rotor rpm time taken to accelerate from ignitionn1 to idlen1 value (default 1.4)
|
||||
n2startrate - Core rotor rpm time taken to accelerate to ignitionn2 idlen2 value (default 2.0)
|
||||
maxn1 - Fan rotor rpm (% of max) at full throttle
|
||||
maxn2 - Core rotor rpm (% of max) at full throttle
|
||||
augmented
|
||||
|
@ -238,6 +246,8 @@ private:
|
|||
double ATSFC; ///< Augmented TSFC (lbm/hr/lbf)
|
||||
double IdleN1; ///< Idle N1
|
||||
double IdleN2; ///< Idle N2
|
||||
double IgnitionN1; ///< Ignition N1
|
||||
double IgnitionN2; ///< Ignition N2
|
||||
double N1; ///< N1
|
||||
double N2; ///< N2
|
||||
double N2norm; ///< N2 normalized (0=idle, 1=max)
|
||||
|
@ -248,8 +258,10 @@ private:
|
|||
double N2_factor; ///< factor to tie N2 and throttle
|
||||
double ThrottlePos; ///< FCS-supplied throttle position - modified for local use!
|
||||
double AugmentCmd; ///< modulated afterburner command (0.0 to 1.0)
|
||||
double N1_spinup; ///< N1 spin up rate from starter (per second)
|
||||
double N2_spinup; ///< N2 spin up rate from starter (per second)
|
||||
double N1_spinup; ///< N1 spin up rate from pneumatic starter (per second)
|
||||
double N2_spinup; ///< N2 spin up rate from pneumatic starter (per second)
|
||||
double N1_start_rate; ///< N1 spin up rate from ignition (per second)
|
||||
double N2_start_rate; ///< N2 spin up rate from ignition (per second)
|
||||
bool Stalled; ///< true if engine is compressor-stalled
|
||||
bool Seized; ///< true if inner spool is seized
|
||||
bool Overtemp; ///< true if EGT exceeds limits
|
||||
|
|
Loading…
Add table
Reference in a new issue