FlightGear next
Jet.cpp
Go to the documentation of this file.
1#include "Atmosphere.hpp"
2#include "Math.hpp"
3#include "Jet.hpp"
4namespace yasim {
5
6Jet::Jet()
7{
8 _maxThrust = 0;
9 _abThrust = 0;
10 _abFactor = 1;
11 _reheat = 0;
12 _rotControl = 0;
13 _maxRot = 0;
14 _reverseThrust = false;
15
16 // Initialize parameters for an early-ish subsonic turbojet. More
17 // recent turbofans will typically have a lower vMax, epr0, and
18 // tsfc.
19 _vMax = 800;
20 _epr0 = 3.0;
21 _tsfc = 0.8f;
22 _atsfc = 0.0f;
23 _abFuelFactor = 3.5f; //previously was constant in code below
24 _egt0 = 1050;
25 _n1Min = 55;
26 _n1Max = 102;
27 _n2Min = 73;
28 _n2Max = 103;
29 setSpooling(4); // 4 second spool time? s'bout right.
30
31 // And initialize to an engine that is idling
32 _n1 = _n1Min;
33 _n2 = _n2Min;
34
35 // And sanify the remaining junk, just in case.
36 _running = true;
37 _cranking = false;
38 _fuel = true;
39 _epr = 1;
40 _fuelFlow = 0;
41 _egt = 273;
42 _tempCorrect = 1;
43 _pressureCorrect = 1;
44}
45
46void Jet::stabilize()
47{
48 // Just run it for an hour, there's no need to iterate given the
49 // algorithms used.
50 integrate(3600);
51}
52
53void Jet::setMaxThrust(float thrust, float afterburner)
54{
55 _maxThrust = thrust;
56 if(afterburner == 0) _abFactor = 1;
57 else { _abFactor = afterburner/thrust;
58 _abThrust = afterburner - thrust;
59 }
60}
61
62void Jet::setRPMs(float idleN1, float maxN1, float idleN2, float maxN2)
63{
64 _n1Min = idleN1;
65 _n1Max = maxN1;
66 _n2Min = idleN2;
67 _n2Max = maxN2;
68}
69
70void Jet::setSpooling(float time)
71{
72 // 2.3 = -ln(0.1), i.e. x=2.3 is the 90% point we're defining
73 // The extra fudge factor is there because the N1 speed (which
74 // determines thrust) lags the N2 speed.
75 _decay = 1.5f * 2.3f / time;
76}
77
78void Jet::setReheat(float reheat)
79{
80 _reheat = Math::clamp(reheat, 0, 1);
81}
82
83void Jet::setRotation(float rot)
84{
85 _rotControl = Math::clamp(rot, 0, 1);
86}
87
88float Jet::getN1()
89{
90 return _n1 * _tempCorrect;
91}
92
93float Jet::getN2()
94{
95 return _n2 * _tempCorrect;
96}
97
98float Jet::getEGT()
99{
100 // Exactly zero means "off" -- return the ambient temperature
101 if(_egt == 0) {
102 return _atmo.getTemperature();
103 }
104 return _egt * _tempCorrect * _tempCorrect;
105}
106
107float Jet::getFuelFlow()
108{
109 return _fuelFlow * _pressureCorrect;
110}
111
112void Jet::integrate(float dt)
113{
114 // Sea-level values
115 const static float P0 = Atmosphere::getStdPressure(0);
116 const static float T0 = Atmosphere::getStdTemperature(0);
117 const static float D0 = Atmosphere::getStdDensity(0);
118
119 float spd = -Math::dot3(_wind, _dir);
120
121 float statT, statP, statD;
122 _atmo.calcStaticAir(spd, &statP, &statT, &statD);
123 _pressureCorrect = statP/P0;
124 _tempCorrect = Math::sqrt(statT/T0);
125
126 // Handle running out of fuel. This is a hack. What should
127 // really happen is a simulation of ram air torque on the
128 // turbine. This just forces the engine into ground idle.
129 if(_fuel == false)
130 _throttle = 0;
131
132 // Linearly taper maxThrust to zero at vMax
133 float vCorr = spd<0 ? 1 : (spd<_vMax ? 1-spd/_vMax : 0);
134
135 float maxThrust = _maxThrust * vCorr * (statD/D0);
136 float setThrust = maxThrust * _throttle;
137
138 // Now get a "beta" (i.e. EPR - 1) value. The output values are
139 // expressed as functions of beta.
140 float ibeta0 = 1/(_epr0 - 1);
141 float betaTarget = (_epr0 - 1) * (setThrust/_maxThrust) * (P0/_atmo.getPressure())
142 * (_atmo.getTemperature()/statT);
143 float n2Target = _n2Min + (betaTarget*ibeta0) * (_n2Max - _n2Min);
144
145 // Note that this "first" beta value is used to compute a target
146 // for N2 only Integrate the N2 speed and back-calculate a beta1
147 // target. The N1 speed will seek to this.
148 _n2 = (_n2 + dt*_decay * n2Target) / (1 + dt*_decay);
149
150 float betaN2 = (_epr0-1) * (_n2 - _n2Min) / (_n2Max - _n2Min);
151 float n1Target = _n1Min + betaN2*ibeta0 * (_n1Max - _n1Min);
152 _n1 = (_n1 + dt*_decay * n1Target) / (1 + dt*_decay);
153
154 // The actual thrust produced is keyed to the N1 speed. Add the
155 // afterburners in at the end.
156 float betaN1 = (_epr0-1) * (_n1 - _n1Min) / (_n1Max - _n1Min);
157 _thrust = _maxThrust * betaN1/((_epr0-1)*(P0/_atmo.getPressure())*(_atmo.getTemperature()/statT));
158 _thrust *= 1 + _reheat*(_abFactor-1);
159
160 // Finally, calculate the output variables. Use a 80/20 mix of
161 // the N2/N1 speeds as the key.
162 float beta = 0.8f*betaN2 + 0.2f*betaN1;
163 _epr = beta + 1;
164 float ff0 = _maxThrust*_tsfc*(1/(3600.0f*9.8f)); // takeoff fuel flow, kg/s
165 _fuelFlow = ff0 * beta*ibeta0;
166 //Calc afterburner only tsfc if atsfc is set
167 if(_atsfc > 0) _abFuelFactor = ((_maxThrust*_abFactor*_atsfc)-(_maxThrust*_tsfc))/_abThrust;
168 if(_atsfc == 0 ) _fuelFlow *= 1 + (_abFuelFactor * _reheat * _abFactor); // Afterburners take
169 // 3.5 times as much
170 // fuel per thrust unit
171 else _fuelFlow += _abThrust*_abFuelFactor*(1/(3600.0f*9.8f))*beta*ibeta0*_reheat;
172 // add afterburning only component of fuel flow
173
174 _egt = T0 + beta*ibeta0 * (_egt0 - T0);
175
176 // Thrust reverse handling:
177 if(_reverseThrust) _thrust *= -_reverseEff;
178}
179
180void Jet::getThrust(float* out)
181{
182 Math::mul3(_thrust, _dir, out);
183
184 // Rotate about the Y axis for thrust vectoring
185 float angle = _rotControl * _maxRot;
186 float s = Math::sin(angle);
187 float c = Math::cos(angle);
188 float o0 = out[0];
189 out[0] = c * o0 + s * out[2];
190 out[2] = -s * o0 + c * out[2];
191}
192
193void Jet::getTorque(float* out)
194{
195 out[0] = out[1] = out[2] = 0;
196}
197
198void Jet::getGyro(float* out)
199{
200 out[0] = out[1] = out[2] = 0;
201}
202
203}; // namespace yasim
const double P0(101325.0)
const double T0(15.+freezing)