JSBSim Flight Dynamics Model  1.2.0 (05 Nov 2023)
An Open Source Flight Dynamics and Control Software Library in C++
FGEngine.cpp
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
12  Software Foundation; either version 2 of the License, or (at your option) any
13  later version.
14 
15  This program is distributed in the hope that it will be useful, but WITHOUT ANY
16  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
17  PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18  details.
19 
20  You should have received a copy of the GNU Lesser General Public License along
21  with this program; if not, write to the Free Software Foundation, Inc., 59
22  Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24  Further information about the GNU Lesser General Public License can also be
25  found on the world wide web at http://www.gnu.org.
26 
27 FUNCTIONAL DESCRIPTION
28 --------------------------------------------------------------------------------
29 See header file.
30 
31 HISTORY
32 --------------------------------------------------------------------------------
33 01/21/99 JSB Created
34 09/03/99 JSB Changed Rocket thrust equation to correct -= Thrust instead of
35  += Thrust (thanks to Tony Peden)
36 
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38 INCLUDES
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
40 
41 #include "FGEngine.h"
42 #include "FGPropeller.h"
43 #include "FGNozzle.h"
44 #include "FGRotor.h"
45 #include "input_output/FGXMLElement.h"
46 
47 using namespace std;
48 
49 namespace JSBSim {
50 
51 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
52 CLASS IMPLEMENTATION
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
54 
55 FGEngine::FGEngine(int engine_number, struct Inputs& input)
56  : in(input), EngineNumber(engine_number)
57 {
58  Type = etUnknown;
59  SLFuelFlowMax = 0.0;
60  FuelExpended = 0.0;
61  MaxThrottle = 1.0;
62  MinThrottle = 0.0;
63  FuelDensity = 6.02;
64  Debug(0);
65 }
66 
67 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
68 
69 FGEngine::~FGEngine()
70 {
71  delete Thruster;
72  Debug(1);
73 }
74 
75 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76 
77 void FGEngine::ResetToIC(void)
78 {
79  Starter = false;
80  FuelExpended = 0.0;
81  Starved = Running = Cranking = false;
82  PctPower = 0.0;
83  FuelFlow_gph = 0.0;
84  FuelFlow_pph = 0.0;
85  FuelFlowRate = 0.0;
86  FuelFreeze = false;
87  FuelUsedLbs = 0.0;
88  Thruster->ResetToIC();
89 }
90 
91 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 
93 double FGEngine::CalcFuelNeed(void)
94 {
95  FuelFlowRate = SLFuelFlowMax*PctPower;
96  FuelExpended = FuelFlowRate*in.TotalDeltaT;
97  if (!Starved) FuelUsedLbs += FuelExpended;
98  return FuelExpended;
99 }
100 
101 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102 
103 unsigned int FGEngine::GetSourceTank(unsigned int i) const
104 {
105  if (i < SourceTanks.size()) {
106  return SourceTanks[i];
107  } else {
108  throw("No such source tank is available for this engine");
109  }
110 }
111 
112 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113 
114 double FGEngine::GetThrust(void) const
115 {
116  return Thruster->GetThrust();
117 }
118 
119 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120 
121 const FGColumnVector3& FGEngine::GetBodyForces(void)
122 {
123  return Thruster->GetBodyForces();
124 }
125 
126 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127 
128 const FGColumnVector3& FGEngine::GetMoments(void)
129 {
130  return Thruster->GetMoments();
131 }
132 
133 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
134 
135 void FGEngine::LoadThrusterInputs()
136 {
137  Thruster->in.TotalDeltaT = in.TotalDeltaT;
138  Thruster->in.H_agl = in.H_agl;
139  Thruster->in.PQRi = in.PQRi;
140  Thruster->in.AeroPQR = in.AeroPQR;
141  Thruster->in.AeroUVW = in.AeroUVW;
142  Thruster->in.Density = in.Density;
143  Thruster->in.Pressure = in.Pressure;
144  Thruster->in.Soundspeed = in.Soundspeed;
145  Thruster->in.Alpha = in.alpha;
146  Thruster->in.Beta = in.beta;
147  Thruster->in.Vt = in.Vt;
148 }
149 
150 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151 
152 void FGEngine::LoadThruster(FGFDMExec* exec, Element *thruster_element)
153 {
154  if (thruster_element->FindElement("propeller")) {
155  Element *document = thruster_element->FindElement("propeller");
156  Thruster = new FGPropeller(exec, document, EngineNumber);
157  } else if (thruster_element->FindElement("nozzle")) {
158  Element *document = thruster_element->FindElement("nozzle");
159  Thruster = new FGNozzle(exec, document, EngineNumber);
160  } else if (thruster_element->FindElement("rotor")) {
161  Element *document = thruster_element->FindElement("rotor");
162  Thruster = new FGRotor(exec, document, EngineNumber);
163  } else if (thruster_element->FindElement("direct")) {
164  Element *document = thruster_element->FindElement("direct");
165  Thruster = new FGThruster(exec, document, EngineNumber);
166  } else {
167  cerr << thruster_element->ReadFrom() << " Unknown thruster type" << endl;
168  throw("Failed to load the thruster");
169  }
170 
171  Debug(2);
172 }
173 
174 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
175 
176 bool FGEngine::Load(FGFDMExec *exec, Element *engine_element)
177 {
178  Element* parent_element = engine_element->GetParent();
179  Element* local_element;
180  FGColumnVector3 location, orientation;
181 
182  auto PropertyManager = exec->GetPropertyManager();
183 
184  Name = engine_element->GetAttributeValue("name");
185 
186  // Call ModelFunctions loader
187  FGModelFunctions::Load(engine_element, exec, to_string((int)EngineNumber));
188 
189  // If engine location and/or orientation is supplied issue a warning since they
190  // are ignored. What counts is the location and orientation of the thruster.
191  local_element = parent_element->FindElement("location");
192  if (local_element)
193  cerr << local_element->ReadFrom()
194  << "Engine location ignored, only thruster location is used." << endl;
195 
196  local_element = parent_element->FindElement("orient");
197  if (local_element)
198  cerr << local_element->ReadFrom()
199  << "Engine orientation ignored, only thruster orientation is used." << endl;
200 
201  // Load thruster
202  local_element = parent_element->FindElement("thruster");
203  if (local_element) {
204  try {
205  LoadThruster(exec, local_element);
206  } catch (std::string& str) {
207  throw("Error loading engine " + Name + ". " + str);
208  }
209  } else {
210  cerr << "No thruster definition supplied with engine definition." << endl;
211  }
212 
213  ResetToIC(); // initialize dynamic terms
214 
215  // Load feed tank[s] references
216  local_element = parent_element->FindElement("feed");
217  while (local_element) {
218  int tankID = (int)local_element->GetDataAsNumber();
219  SourceTanks.push_back(tankID);
220  local_element = parent_element->FindNextElement("feed");
221  }
222 
223  string property_name, base_property_name;
224  base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber);
225 
226  property_name = base_property_name + "/set-running";
227  PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetRunning, &FGEngine::SetRunning );
228  property_name = base_property_name + "/thrust-lbs";
229  PropertyManager->Tie( property_name.c_str(), Thruster, &FGThruster::GetThrust);
230  property_name = base_property_name + "/fuel-flow-rate-pps";
231  PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelFlowRate);
232  property_name = base_property_name + "/fuel-flow-rate-gph";
233  PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelFlowRateGPH);
234  property_name = base_property_name + "/fuel-used-lbs";
235  PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelUsedLbs);
236 
237  PostLoad(engine_element, exec, to_string((int)EngineNumber));
238 
239  Debug(0);
240 
241  return true;
242 }
243 
244 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
245 // The bitmasked value choices are as follows:
246 // unset: In this case (the default) JSBSim would only print
247 // out the normally expected messages, essentially echoing
248 // the config files as they are read. If the environment
249 // variable is not set, debug_lvl is set to 1 internally
250 // 0: This requests JSBSim not to output any messages
251 // whatsoever.
252 // 1: This value explicity requests the normal JSBSim
253 // startup messages
254 // 2: This value asks for a message to be printed out when
255 // a class is instantiated
256 // 4: When this value is set, a message is displayed when a
257 // FGModel object executes its Run() method
258 // 8: When this value is set, various runtime state variables
259 // are printed out periodically
260 // 16: When set various parameters are sanity checked and
261 // a message is printed out when they go out of bounds
262 
263 void FGEngine::Debug(int from)
264 {
265  if (debug_lvl <= 0) return;
266 
267  if (debug_lvl & 1) { // Standard console startup message output
268  if (from == 0) { // Constructor
269 
270  }
271  if (from == 2) { // After thruster loading
272  cout << " X = " << Thruster->GetLocationX() << endl;
273  cout << " Y = " << Thruster->GetLocationY() << endl;
274  cout << " Z = " << Thruster->GetLocationZ() << endl;
275  cout << " Pitch = " << radtodeg*Thruster->GetAnglesToBody(ePitch) << " degrees" << endl;
276  cout << " Yaw = " << radtodeg*Thruster->GetAnglesToBody(eYaw) << " degrees" << endl;
277  }
278  }
279  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
280  if (from == 0) cout << "Instantiated: FGEngine" << endl;
281  if (from == 1) cout << "Destroyed: FGEngine" << endl;
282  }
283  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
284  }
285  if (debug_lvl & 8 ) { // Runtime state variables
286  }
287  if (debug_lvl & 16) { // Sanity checking
288  }
289  if (debug_lvl & 64) {
290  if (from == 0) { // Constructor
291  }
292  }
293 }
294 }