diff --git a/src/FDM/JSBSim/input_output/FGXMLElement.h b/src/FDM/JSBSim/input_output/FGXMLElement.h index cacab4235..c270c98dd 100644 --- a/src/FDM/JSBSim/input_output/FGXMLElement.h +++ b/src/FDM/JSBSim/input_output/FGXMLElement.h @@ -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). diff --git a/src/FDM/JSBSim/math/FGFunction.cpp b/src/FDM/JSBSim/math/FGFunction.cpp index bb42bf202..163c4dfdc 100644 --- a/src/FDM/JSBSim/math/FGFunction.cpp +++ b/src/FDM/JSBSim/math/FGFunction.cpp @@ -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; diff --git a/src/FDM/JSBSim/math/FGFunction.h b/src/FDM/JSBSim/math/FGFunction.h index a3f5918e2..fd142178f 100644 --- a/src/FDM/JSBSim/math/FGFunction.h +++ b/src/FDM/JSBSim/math/FGFunction.h @@ -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 - {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} Example: 5 mod 2, evaluates to 1 5 2 @endcode +- @b floor returns the largest integral value that is not greater than X. + @code + + {property, value, table, or other function element} + + @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 + + {property, value, table, or other function element} + + @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 + + {property, value, table, or other function element} + {property, value, table, or other function element} + + @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; diff --git a/src/FDM/JSBSim/models/FGFCS.cpp b/src/FDM/JSBSim/models/FGFCS.cpp index 3110a4e39..12b9d08cc 100644 --- a/src/FDM/JSBSim/models/FGFCS.cpp +++ b/src/FDM/JSBSim/models/FGFCS.cpp @@ -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")) { + // is equivalent to + 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_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")) { diff --git a/src/FDM/JSBSim/models/flight_control/FGFilter.cpp b/src/FDM/JSBSim/models/flight_control/FGFilter.cpp index a00e14bf1..500ac8e40 100644 --- a/src/FDM/JSBSim/models/flight_control/FGFilter.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGFilter.cpp @@ -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; } diff --git a/src/FDM/JSBSim/models/flight_control/FGFilter.h b/src/FDM/JSBSim/models/flight_control/FGFilter.h index 26b556fcf..3f2bc8126 100644 --- a/src/FDM/JSBSim/models/flight_control/FGFilter.h +++ b/src/FDM/JSBSim/models/flight_control/FGFilter.h @@ -199,15 +199,15 @@ For an integrator of the form: The corresponding filter definition is: @code - - property - value|property - [ property ] + + {property} + {[-]property | number} + [ {property} ] [ - {[-]property name | value} - {[-]property name | value} + {[-]property | number} + {[-]property | number} ] - [ property ] + [ {property} ] @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 + + {[-]property} + 0.0 + {number|[-]property} + 0.0 + {property} + [ + {[-]property | value} + {[-]property | value} + ] + [ {property} ] + +@endcode + +As a consequence, JSBSim internally uses PID controllers to simulate INTEGRATOR +filters. + In all the filter specifications above, an \ 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; diff --git a/src/FDM/JSBSim/models/propulsion/FGTurbine.cpp b/src/FDM/JSBSim/models/propulsion/FGTurbine.cpp index 0036387ec..9e8185bf3 100644 --- a/src/FDM/JSBSim/models/propulsion/FGTurbine.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGTurbine.cpp @@ -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")) diff --git a/src/FDM/JSBSim/models/propulsion/FGTurbine.h b/src/FDM/JSBSim/models/propulsion/FGTurbine.h index 7e2b47754..b7382acc1 100644 --- a/src/FDM/JSBSim/models/propulsion/FGTurbine.h +++ b/src/FDM/JSBSim/models/propulsion/FGTurbine.h @@ -66,8 +66,8 @@ CLASS DOCUMENTATION

- 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 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.

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.

Configuration File Format:

@code @@ -89,10 +89,14 @@ CLASS DOCUMENTATION {number} {number} {number} + {number} + {number} {number} {number} {number} {number} + {number} + {number} {number} {number} {0 | 1} @@ -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