Branch data Line data Source code
1 : : /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 : :
3 : : Module: FGEngine.cpp
4 : : Author: Jon Berndt
5 : : Date started: 01/21/99
6 : : Called by: FGAircraft
7 : :
8 : : ------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
9 : :
10 : : This program is free software; you can redistribute it and/or modify it under
11 : : the terms of the GNU Lesser General Public License as published by the Free Software
12 : : Foundation; either version 2 of the License, or (at your option) any later
13 : : version.
14 : :
15 : : This program is distributed in the hope that it will be useful, but WITHOUT
16 : : ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 : : FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
18 : :
19 : : You should have received a copy of the GNU Lesser General Public License along with
20 : : this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21 : : Place - Suite 330, Boston, MA 02111-1307, USA.
22 : :
23 : : Further information about the GNU Lesser General Public License can also be found on
24 : : the world wide web at http://www.gnu.org.
25 : :
26 : : FUNCTIONAL DESCRIPTION
27 : : --------------------------------------------------------------------------------
28 : : See header file.
29 : :
30 : : HISTORY
31 : : --------------------------------------------------------------------------------
32 : : 01/21/99 JSB Created
33 : : 09/03/99 JSB Changed Rocket thrust equation to correct -= Thrust instead of
34 : : += Thrust (thanks to Tony Peden)
35 : :
36 : : %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37 : : INCLUDES
38 : : %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
39 : :
40 : : #include "FGEngine.h"
41 : : #include "FGTank.h"
42 : : #include "FGPropeller.h"
43 : : #include "FGNozzle.h"
44 : : #include "FGRotor.h"
45 : : #include "models/FGPropulsion.h"
46 : : #include "input_output/FGXMLParse.h"
47 : : #include "math/FGColumnVector3.h"
48 : :
49 : : #include <iostream>
50 : : #include <fstream>
51 : : #include <cstdlib>
52 : :
53 : : using namespace std;
54 : :
55 : : namespace JSBSim {
56 : :
57 : : static const char *IdSrc = "$Id: FGEngine.cpp,v 1.39 2010/08/21 17:13:48 jberndt Exp $";
58 : : static const char *IdHdr = ID_ENGINE;
59 : :
60 : : /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
61 : : CLASS IMPLEMENTATION
62 : : %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
63 : :
64 : 12 : FGEngine::FGEngine(FGFDMExec* exec, Element* engine_element, int engine_number)
65 : 12 : : EngineNumber(engine_number)
66 : : {
67 : : Element* local_element;
68 : 12 : FGColumnVector3 location, orientation;
69 : :
70 : 12 : Name = "";
71 : 12 : Type = etUnknown;
72 : 12 : X = Y = Z = 0.0;
73 : 12 : EnginePitch = EngineYaw = 0.0;
74 : 12 : SLFuelFlowMax = 0.0;
75 : 12 : MaxThrottle = 1.0;
76 : 12 : MinThrottle = 0.0;
77 : 12 : FuelDensity = 6.0;
78 : : unsigned int i;
79 : :
80 : 12 : ResetToIC(); // initialize dynamic terms
81 : :
82 : 12 : FDMExec = exec;
83 : 24 : Atmosphere = FDMExec->GetAtmosphere();
84 : 24 : FCS = FDMExec->GetFCS();
85 : 24 : Propulsion = FDMExec->GetPropulsion();
86 : 24 : Aircraft = FDMExec->GetAircraft();
87 : 24 : Propagate = FDMExec->GetPropagate();
88 : 24 : Auxiliary = FDMExec->GetAuxiliary();
89 : :
90 : 12 : PropertyManager = FDMExec->GetPropertyManager();
91 : :
92 : 24 : Name = engine_element->GetAttributeValue("name");
93 : :
94 : 12 : Load(engine_element, PropertyManager, to_string(EngineNumber)); // Call ModelFunctions loader
95 : :
96 : : // Find and set engine location
97 : :
98 : 12 : local_element = engine_element->GetParent()->FindElement("location");
99 [ # # ][ + - ]: 12 : if (local_element) location = local_element->FindElementTripletConvertTo("IN");
100 : 0 : else cerr << "No engine location found for this engine." << endl;
101 : :
102 : 12 : local_element = engine_element->GetParent()->FindElement("orient");
103 [ # # ][ - + ]: 12 : if (local_element) orientation = local_element->FindElementTripletConvertTo("RAD");
104 : : // else cerr << "No engine orientation found for this engine." << endl;
105 : : // Jon: The engine orientation has a default and is not normally used.
106 : :
107 : : SetPlacement(location, orientation);
108 : :
109 : : // Load thruster
110 : 12 : local_element = engine_element->GetParent()->FindElement("thruster");
111 [ # # + - ]: 12 : if (local_element) {
112 [ # # ][ - + ]: 12 : if (!LoadThruster(local_element)) exit(-1);
113 : : } else {
114 : 0 : cerr << "No thruster definition supplied with engine definition." << endl;
115 : : }
116 : :
117 : : // Build and initialize the feed tank vector.
118 [ # # ][ + + ]: 84 : for (i=0; i<(Propulsion->GetNumTanks()); i++) {
119 : 72 : SourceTanks.push_back(0);
120 : : }
121 : :
122 : : // Load feed tank[s] references
123 : 12 : local_element = engine_element->GetParent()->FindElement("feed");
124 [ # # + - ]: 12 : if (local_element) {
125 [ # # ][ + + ]: 34 : while (local_element) {
126 : 22 : int tankID = (int)local_element->GetDataAsNumber();
127 : 44 : FGTank* tank = Propulsion->GetTank(tankID);
128 [ # # ][ + - ]: 22 : if (tank) {
129 : : AddFeedTank(tankID, tank->GetPriority());
130 : 22 : FuelDensity = tank->GetDensity();
131 : : } else {
132 : : cerr << "Feed tank " << tankID <<
133 : 0 : " specified in engine definition does not exist." << endl;
134 : : }
135 : 22 : local_element = engine_element->GetParent()->FindNextElement("feed");
136 : : }
137 : : } else {
138 : 0 : cerr << "No feed tank specified in engine definition." << endl;
139 : : }
140 : :
141 : 12 : string property_name, base_property_name;
142 : 24 : base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber);
143 : :
144 : 24 : property_name = base_property_name + "/set-running";
145 : 12 : PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetRunning, &FGEngine::SetRunning );
146 : 24 : property_name = base_property_name + "/thrust-lbs";
147 : 12 : PropertyManager->Tie( property_name.c_str(), Thruster, &FGThruster::GetThrust);
148 : 24 : property_name = base_property_name + "/fuel-flow-rate-pps";
149 : 12 : PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelFlowRate);
150 : :
151 : 12 : PostLoad(engine_element, PropertyManager, to_string(EngineNumber));
152 : :
153 : : //cout << "Engine[" << EngineNumber << "] using fuel density: " << FuelDensity << endl;
154 : :
155 : 12 : Debug(0);
156 : 12 : }
157 : :
158 : : //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
159 : :
160 : 12 : FGEngine::~FGEngine()
161 : : {
162 [ # # ][ # # ]: 12 : delete Thruster;
[ + - ]
163 : 12 : Debug(1);
164 [ # # ][ # # ]: 24 : }
[ - + ]
165 : :
166 : : //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167 : :
168 : 12 : void FGEngine::ResetToIC(void)
169 : : {
170 : 12 : Throttle = 0.0;
171 : 12 : Mixture = 1.0;
172 : 12 : Starter = false;
173 : 12 : FuelExpended = 0.0;
174 : 12 : Starved = Running = Cranking = false;
175 : 12 : PctPower = 0.0;
176 : 12 : TrimMode = false;
177 : 12 : FuelFlow_gph = 0.0;
178 : 12 : FuelFlow_pph = 0.0;
179 : 12 : FuelFlowRate = 0.0;
180 : 12 : FuelFreeze = false;
181 : 12 : }
182 : :
183 : : //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
184 : : // This base class function should be called from within the
185 : : // derived class' Calculate() function before any other calculations are done.
186 : : // This base class method removes fuel from the fuel tanks as appropriate,
187 : : // and sets the starved flag if necessary.
188 : : // This version of the fuel consumption code should never see an oxidizer tank.
189 : :
190 : 0 : void FGEngine::ConsumeFuel(void)
191 : : {
192 [ # # ]: 0 : if (FuelFreeze) return;
193 [ # # ]: 0 : if (TrimMode) return;
194 : :
195 : : unsigned int i;
196 : : double Fshortage, FuelNeeded;
197 : : FGTank* Tank;
198 : 0 : unsigned int TanksWithFuel = 0;
199 : 0 : Fshortage = FuelNeeded = 0.0;
200 : : double FuelToBurn;
201 : 0 : unsigned int CurrentPriority = 1;
202 : : vector <int> FeedList;
203 : 0 : Starved = false;
204 : :
205 : 0 : FuelToBurn = CalcFuelNeed();
206 [ # # ]: 0 : if (FuelToBurn == 0.0) return;
207 : :
208 : : // Count how many fuel tanks with the current priority level have fuel.
209 : : // If none, then try next lower priority. Build the feed list.
210 [ # # ][ # # ]: 0 : while ((TanksWithFuel == 0) && (CurrentPriority <= Propulsion->GetNumTanks())) {
[ # # ]
211 [ # # ]: 0 : for (i=0; i<Propulsion->GetNumTanks(); i++) {
212 [ # # ]: 0 : if (SourceTanks[i] != 0) {
213 : 0 : Tank = Propulsion->GetTank(i);
214 [ # # ]: 0 : if (Tank->GetType() == FGTank::ttFUEL) {
215 [ # # ][ # # ]: 0 : if ((Tank->GetContents() > 0.0) && ((unsigned int)Tank->GetPriority() == CurrentPriority)) {
[ # # ]
216 : 0 : ++TanksWithFuel;
217 : 0 : FeedList.push_back(i);
218 : : }
219 : : } else {
220 : 0 : cerr << "No oxidizer tanks should be used for this engine type." << endl;
221 : : }
222 : : }
223 : : }
224 [ # # ]: 0 : if (TanksWithFuel == 0) CurrentPriority++;
225 : : }
226 : :
227 : : // No fuel found at any priority!
228 [ # # ]: 0 : if (TanksWithFuel == 0) {
229 : 0 : Starved = true;
230 : : return;
231 : : }
232 : :
233 : : // Remove equal amount of fuel from each feed tank.
234 : 0 : FuelNeeded = FuelToBurn/TanksWithFuel;
235 [ # # ]: 0 : for (i=0; i<FeedList.size(); i++) {
236 : 0 : Tank = Propulsion->GetTank(FeedList[i]);
237 : 0 : Tank->Drain(FuelNeeded);
238 : 0 : }
239 : :
240 : : }
241 : :
242 : : //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
243 : :
244 : 0 : double FGEngine::CalcFuelNeed(void)
245 : : {
246 : 0 : double dT = FDMExec->GetDeltaT()*Propulsion->GetRate();
247 : 0 : FuelFlowRate = SLFuelFlowMax*PctPower;
248 : 0 : FuelExpended = FuelFlowRate*dT;
249 : 0 : return FuelExpended;
250 : : }
251 : :
252 : : //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
253 : :
254 : 0 : void FGEngine::SetPlacement(FGColumnVector3& location, FGColumnVector3& orientation)
255 : : {
256 : 12 : X = location(eX);
257 : 12 : Y = location(eY);
258 : 12 : Z = location(eZ);
259 : 12 : EnginePitch = orientation(ePitch);
260 : 12 : EngineYaw = orientation (eYaw);
261 : 0 : }
262 : :
263 : : //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
264 : :
265 : 0 : void FGEngine::AddFeedTank(int tkID, int priority)
266 : : {
267 : 44 : SourceTanks[tkID] = priority;
268 : 0 : }
269 : :
270 : : //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
271 : :
272 : 648060 : FGColumnVector3& FGEngine::GetBodyForces(void)
273 : : {
274 : 648060 : return Thruster->GetBodyForces();
275 : : }
276 : :
277 : : //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
278 : :
279 : 648060 : FGColumnVector3& FGEngine::GetMoments(void)
280 : : {
281 : 648060 : return Thruster->GetMoments();
282 : : }
283 : :
284 : : //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
285 : :
286 : 12 : bool FGEngine::LoadThruster(Element *thruster_element)
287 : : {
288 : 12 : string token, fullpath, localpath;
289 : 12 : string thruster_filename, thruster_fullpathname, thrType;
290 : 12 : string enginePath = FDMExec->GetEnginePath();
291 : 12 : string aircraftPath = FDMExec->GetFullAircraftPath();
292 : 12 : ifstream thruster_file;
293 : 12 : FGColumnVector3 location, orientation;
294 : 12 : string separator = "/";
295 : :
296 : 24 : fullpath = enginePath + separator;
297 : 24 : localpath = aircraftPath + separator + "Engines" + separator;
298 : :
299 : 24 : thruster_filename = thruster_element->GetAttributeValue("file");
300 [ + - ]: 12 : if ( !thruster_filename.empty()) {
301 : 24 : thruster_fullpathname = fullpath + thruster_filename + ".xml";
302 : 12 : thruster_file.open(thruster_fullpathname.c_str());
303 [ - + ]: 12 : if ( !thruster_file.is_open()) {
304 : 0 : thruster_fullpathname = localpath + thruster_filename + ".xml";
305 : 0 : thruster_file.open(thruster_fullpathname.c_str());
306 [ # # ]: 0 : if ( !thruster_file.is_open()) {
307 : 0 : cerr << "Could not open thruster file: " << thruster_filename << ".xml" << endl;
308 : 0 : return false;
309 : : } else {
310 : 0 : thruster_file.close();
311 : : }
312 : : } else {
313 : 12 : thruster_file.close();
314 : : }
315 : : } else {
316 : 0 : cerr << "No thruster filename given." << endl;
317 : 0 : return false;
318 : : }
319 : :
320 : 24 : document = LoadXMLDocument(thruster_fullpathname);
321 : 12 : document->SetParent(thruster_element);
322 : :
323 : 12 : thrType = document->GetName();
324 : :
325 [ - + ]: 12 : if (thrType == "propeller") {
326 : 0 : Thruster = new FGPropeller(FDMExec, document, EngineNumber);
327 [ + - ]: 12 : } else if (thrType == "nozzle") {
328 : 12 : Thruster = new FGNozzle(FDMExec, document, EngineNumber);
329 [ # # ]: 0 : } else if (thrType == "rotor") {
330 : 0 : Thruster = new FGRotor(FDMExec, document, EngineNumber);
331 [ # # ]: 0 : } else if (thrType == "direct") {
332 : 0 : Thruster = new FGThruster( FDMExec, document, EngineNumber);
333 : : }
334 : :
335 : 12 : Thruster->SetdeltaT(FDMExec->GetDeltaT() * Propulsion->GetRate());
336 : :
337 : 12 : Debug(2);
338 : 12 : return true;
339 : : }
340 : :
341 : : //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
342 : : // The bitmasked value choices are as follows:
343 : : // unset: In this case (the default) JSBSim would only print
344 : : // out the normally expected messages, essentially echoing
345 : : // the config files as they are read. If the environment
346 : : // variable is not set, debug_lvl is set to 1 internally
347 : : // 0: This requests JSBSim not to output any messages
348 : : // whatsoever.
349 : : // 1: This value explicity requests the normal JSBSim
350 : : // startup messages
351 : : // 2: This value asks for a message to be printed out when
352 : : // a class is instantiated
353 : : // 4: When this value is set, a message is displayed when a
354 : : // FGModel object executes its Run() method
355 : : // 8: When this value is set, various runtime state variables
356 : : // are printed out periodically
357 : : // 16: When set various parameters are sanity checked and
358 : : // a message is printed out when they go out of bounds
359 : :
360 : 36 : void FGEngine::Debug(int from)
361 : : {
362 [ + - ]: 36 : if (debug_lvl <= 0) return;
363 : :
364 [ + - ]: 36 : if (debug_lvl & 1) { // Standard console startup message output
365 : : if (from == 0) { // Constructor
366 : :
367 : : }
368 [ + + ]: 36 : if (from == 2) { // After thruster loading
369 : 12 : cout << " X = " << Thruster->GetLocationX() << endl;
370 : 12 : cout << " Y = " << Thruster->GetLocationY() << endl;
371 : 12 : cout << " Z = " << Thruster->GetLocationZ() << endl;
372 : 24 : cout << " Pitch = " << radtodeg*Thruster->GetAnglesToBody(ePitch) << " degrees" << endl;
373 : 24 : cout << " Yaw = " << radtodeg*Thruster->GetAnglesToBody(eYaw) << " degrees" << endl;
374 : : }
375 : : }
376 [ - + ]: 36 : if (debug_lvl & 2 ) { // Instantiation/Destruction notification
377 [ # # ]: 0 : if (from == 0) cout << "Instantiated: FGEngine" << endl;
378 [ # # ]: 0 : if (from == 1) cout << "Destroyed: FGEngine" << endl;
379 : : }
380 : 36 : if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
381 : : }
382 : 36 : if (debug_lvl & 8 ) { // Runtime state variables
383 : : }
384 : 36 : if (debug_lvl & 16) { // Sanity checking
385 : : }
386 [ - + ]: 36 : if (debug_lvl & 64) {
387 [ # # ]: 0 : if (from == 0) { // Constructor
388 : 0 : cout << IdSrc << endl;
389 : 0 : cout << IdHdr << endl;
390 : : }
391 : : }
392 : : }
393 [ + + ][ + - ]: 12 : }
|