]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/models/FGMassBalance.cpp
sync. with JSBSim CVS again
[flightgear.git] / src / FDM / JSBSim / models / FGMassBalance.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3  Module:       FGMassBalance.cpp
4  Author:       Jon S. Berndt
5  Date started: 09/12/2000
6  Purpose:      This module models weight and balance
7
8  ------------- Copyright (C) 2000  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
18  details.
19
20  You should have received a copy of the GNU Lesser General Public License along with
21  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22  Place - Suite 330, Boston, MA  02111-1307, USA.
23
24  Further information about the GNU Lesser General Public License can also be found on
25  the world wide web at http://www.gnu.org.
26
27 FUNCTIONAL DESCRIPTION
28 --------------------------------------------------------------------------------
29
30 This class models the change in weight and balance of the aircraft due to fuel
31 burnoff, etc.
32
33 HISTORY
34 --------------------------------------------------------------------------------
35 09/12/2000  JSB  Created
36
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38 INCLUDES
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
40
41 #include "FGMassBalance.h"
42 #include "FGPropulsion.h"
43 #include "FGBuoyantForces.h"
44 #include "input_output/FGPropertyManager.h"
45
46 namespace JSBSim {
47
48 static const char *IdSrc = "$Id$";
49 static const char *IdHdr = ID_MASSBALANCE;
50
51 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
52 CLASS IMPLEMENTATION
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
54
55
56 FGMassBalance::FGMassBalance(FGFDMExec* fdmex) : FGModel(fdmex)
57 {
58   Name = "FGMassBalance";
59   Weight = EmptyWeight = Mass = 0.0;
60
61   vbaseXYZcg.InitMatrix(0.0);
62   vXYZcg.InitMatrix(0.0);
63   vLastXYZcg.InitMatrix(0.0);
64   vDeltaXYZcg.InitMatrix(0.0);
65   baseJ.InitMatrix();
66   mJ.InitMatrix();
67   mJinv.InitMatrix();
68   pmJ.InitMatrix();
69
70   bind();
71
72   Debug(0);
73 }
74
75 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76
77 FGMassBalance::~FGMassBalance()
78 {
79   for (unsigned int i=0; i<PointMasses.size(); i++) delete PointMasses[i];
80   PointMasses.clear();
81
82   Debug(1);
83 }
84
85 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86
87 bool FGMassBalance::InitModel(void)
88 {
89   if (!FGModel::InitModel()) return false;
90
91   vLastXYZcg.InitMatrix(0.0);
92   vDeltaXYZcg.InitMatrix(0.0);
93
94   return true;
95 }
96
97 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98
99 bool FGMassBalance::Load(Element* el)
100 {
101   Element *element;
102   string element_name = "";
103   double bixx, biyy, bizz, bixy, bixz, biyz;
104
105   FGModel::Load(el); // Perform base class Load.
106
107   bixx = biyy = bizz = bixy = bixz = biyz = 0.0;
108   if (el->FindElement("ixx"))
109     bixx = el->FindElementValueAsNumberConvertTo("ixx", "SLUG*FT2");
110   if (el->FindElement("iyy"))
111     biyy = el->FindElementValueAsNumberConvertTo("iyy", "SLUG*FT2");
112   if (el->FindElement("izz"))
113     bizz = el->FindElementValueAsNumberConvertTo("izz", "SLUG*FT2");
114   if (el->FindElement("ixy"))
115     bixy = el->FindElementValueAsNumberConvertTo("ixy", "SLUG*FT2");
116   if (el->FindElement("ixz"))
117     bixz = el->FindElementValueAsNumberConvertTo("ixz", "SLUG*FT2");
118   if (el->FindElement("iyz"))
119     biyz = el->FindElementValueAsNumberConvertTo("iyz", "SLUG*FT2");
120   SetAircraftBaseInertias(FGMatrix33(  bixx,  -bixy,  bixz,
121                                       -bixy,  biyy,  -biyz,
122                                        bixz,  -biyz,  bizz ));
123   EmptyWeight = el->FindElementValueAsNumberConvertTo("emptywt", "LBS");
124
125   element = el->FindElement("location");
126   while (element) {
127     element_name = element->GetAttributeValue("name");
128     if (element_name == "CG") vbaseXYZcg = element->FindElementTripletConvertTo("IN");
129     element = el->FindNextElement("location");
130   }
131
132 // Find all POINTMASS elements that descend from this METRICS branch of the
133 // config file.
134
135   element = el->FindElement("pointmass");
136   while (element) {
137     AddPointMass(element);
138     element = el->FindNextElement("pointmass");
139   }
140
141   double ChildFDMWeight = 0.0;
142   for (int fdm=0; fdm<FDMExec->GetFDMCount(); fdm++) {
143     if (FDMExec->GetChildFDM(fdm)->mated) ChildFDMWeight += FDMExec->GetChildFDM(fdm)->exec->GetMassBalance()->GetWeight();
144   }
145
146   Weight = EmptyWeight + Propulsion->GetTanksWeight() + GetTotalPointMassWeight()
147     + BuoyantForces->GetGasMass()*slugtolb + ChildFDMWeight;
148
149   Mass = lbtoslug*Weight;
150
151   Debug(2);
152   return true;
153 }
154
155 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
156
157 bool FGMassBalance::Run(void)
158 {
159   double denom, k1, k2, k3, k4, k5, k6;
160   double Ixx, Iyy, Izz, Ixy, Ixz, Iyz;
161
162   if (FGModel::Run()) return true;
163   if (FDMExec->Holding()) return false;
164
165   double ChildFDMWeight = 0.0;
166   for (int fdm=0; fdm<FDMExec->GetFDMCount(); fdm++) {
167     if (FDMExec->GetChildFDM(fdm)->mated) ChildFDMWeight += FDMExec->GetChildFDM(fdm)->exec->GetMassBalance()->GetWeight();
168   }
169
170   Weight = EmptyWeight + Propulsion->GetTanksWeight() + GetTotalPointMassWeight()
171     + BuoyantForces->GetGasMass()*slugtolb + ChildFDMWeight;
172
173   Mass = lbtoslug*Weight;
174
175 // Calculate new CG
176
177   vXYZcg = (Propulsion->GetTanksMoment() + EmptyWeight*vbaseXYZcg
178             + GetPointMassMoment()
179             + BuoyantForces->GetGasMassMoment()) / Weight;
180
181   // Track frame-by-frame delta CG, and move the EOM-tracked location
182   // by this amount.
183   if (vLastXYZcg.Magnitude() == 0.0) vLastXYZcg = vXYZcg;
184   vDeltaXYZcg = vXYZcg - vLastXYZcg;
185   vDeltaXYZcgBody = StructuralToBody(vLastXYZcg) - StructuralToBody(vXYZcg);
186   vLastXYZcg = vXYZcg;
187   Propagate->NudgeBodyLocation(vDeltaXYZcgBody);
188
189 // Calculate new total moments of inertia
190
191   // At first it is the base configuration inertia matrix ...
192   mJ = baseJ;
193   // ... with the additional term originating from the parallel axis theorem.
194   mJ += GetPointmassInertia( lbtoslug * EmptyWeight, vbaseXYZcg );
195   // Then add the contributions from the additional pointmasses.
196   mJ += CalculatePMInertias();
197   mJ += Propulsion->CalculateTankInertias();
198   mJ += BuoyantForces->GetGasMassInertia();
199
200   Ixx = mJ(1,1);
201   Iyy = mJ(2,2);
202   Izz = mJ(3,3);
203   Ixy = -mJ(1,2);
204   Ixz = -mJ(1,3);
205   Iyz = -mJ(2,3);
206
207 // Calculate inertia matrix inverse (ref. Stevens and Lewis, "Flight Control & Simulation")
208
209   k1 = (Iyy*Izz - Iyz*Iyz);
210   k2 = (Iyz*Ixz + Ixy*Izz);
211   k3 = (Ixy*Iyz + Iyy*Ixz);
212
213   denom = 1.0/(Ixx*k1 - Ixy*k2 - Ixz*k3 );
214   k1 = k1*denom;
215   k2 = k2*denom;
216   k3 = k3*denom;
217   k4 = (Izz*Ixx - Ixz*Ixz)*denom;
218   k5 = (Ixy*Ixz + Iyz*Ixx)*denom;
219   k6 = (Ixx*Iyy - Ixy*Ixy)*denom;
220
221   mJinv.InitMatrix( k1, k2, k3,
222                     k2, k4, k5,
223                     k3, k5, k6 );
224
225   Debug(0);
226
227   return false;
228 }
229
230 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
231
232 void FGMassBalance::AddPointMass(Element* el)
233 {
234   Element* loc_element = el->FindElement("location");
235   string pointmass_name = el->GetAttributeValue("name");
236   if (!loc_element) {
237     cerr << "Pointmass " << pointmass_name << " has no location." << endl;
238     exit(-1);
239   }
240
241   double w = el->FindElementValueAsNumberConvertTo("weight", "LBS");
242   FGColumnVector3 vXYZ = loc_element->FindElementTripletConvertTo("IN");
243
244   PointMasses.push_back(new PointMass(w, vXYZ));
245   PointMasses.back()->bind(PropertyManager, PointMasses.size()-1);
246 }
247
248 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
249
250 double FGMassBalance::GetTotalPointMassWeight(void)
251 {
252   double PM_total_weight = 0.0;
253
254   for (unsigned int i=0; i<PointMasses.size(); i++) {
255     PM_total_weight += PointMasses[i]->Weight;
256   }
257   return PM_total_weight;
258 }
259
260 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
261
262 FGColumnVector3& FGMassBalance::GetPointMassMoment(void)
263 {
264   PointMassCG.InitMatrix();
265
266   for (unsigned int i=0; i<PointMasses.size(); i++) {
267     PointMassCG += PointMasses[i]->Weight*PointMasses[i]->Location;
268   }
269   return PointMassCG;
270 }
271
272 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
273
274 FGMatrix33& FGMassBalance::CalculatePMInertias(void)
275 {
276   unsigned int size;
277
278   size = PointMasses.size();
279   if (size == 0) return pmJ;
280
281   pmJ = FGMatrix33();
282
283   for (unsigned int i=0; i<size; i++)
284     pmJ += GetPointmassInertia( lbtoslug * PointMasses[i]->Weight, PointMasses[i]->Location );
285
286   return pmJ;
287 }
288
289 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
290
291 FGColumnVector3 FGMassBalance::StructuralToBody(const FGColumnVector3& r) const
292 {
293   // Under the assumption that in the structural frame the:
294   //
295   // - X-axis is directed afterwards,
296   // - Y-axis is directed towards the right,
297   // - Z-axis is directed upwards,
298   //
299   // (as documented in http://jsbsim.sourceforge.net/JSBSimCoordinates.pdf)
300   // we have to subtract first the center of gravity of the plane which
301   // is also defined in the structural frame:
302   //
303   //   FGColumnVector3 cgOff = r - vXYZcg;
304   //
305   // Next, we do a change of units:
306   //
307   //   cgOff *= inchtoft;
308   //
309   // And then a 180 degree rotation is done about the Y axis so that the:
310   //
311   // - X-axis is directed forward,
312   // - Y-axis is directed towards the right,
313   // - Z-axis is directed downward.
314   //
315   // This is needed because the structural and body frames are 180 degrees apart.
316
317   return FGColumnVector3(inchtoft*(vXYZcg(1)-r(1)),
318                          inchtoft*(r(2)-vXYZcg(2)),
319                          inchtoft*(vXYZcg(3)-r(3)));
320 }
321
322 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
323
324 void FGMassBalance::bind(void)
325 {
326   typedef double (FGMassBalance::*PMF)(int) const;
327   PropertyManager->Tie("inertia/mass-slugs", this,
328                        &FGMassBalance::GetMass);
329   PropertyManager->Tie("inertia/weight-lbs", this,
330                        &FGMassBalance::GetWeight);
331   PropertyManager->Tie("inertia/empty-weight-lbs", this,
332     &FGMassBalance::GetWeight, &FGMassBalance::SetEmptyWeight);
333   PropertyManager->Tie("inertia/cg-x-in", this,1,
334                        (PMF)&FGMassBalance::GetXYZcg);
335   PropertyManager->Tie("inertia/cg-y-in", this,2,
336                        (PMF)&FGMassBalance::GetXYZcg);
337   PropertyManager->Tie("inertia/cg-z-in", this,3,
338                        (PMF)&FGMassBalance::GetXYZcg);
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 void FGMassBalance::Debug(int from)
361 {
362   if (debug_lvl <= 0) return;
363
364   if (debug_lvl & 1) { // Standard console startup message output
365     if (from == 2) { // Loading
366       cout << endl << "  Mass and Balance:" << endl;
367       cout << "    baseIxx: " << baseJ(1,1) << " slug-ft2" << endl;
368       cout << "    baseIyy: " << baseJ(2,2) << " slug-ft2" << endl;
369       cout << "    baseIzz: " << baseJ(3,3) << " slug-ft2" << endl;
370       cout << "    baseIxy: " << baseJ(1,2) << " slug-ft2" << endl;
371       cout << "    baseIxz: " << baseJ(1,3) << " slug-ft2" << endl;
372       cout << "    baseIyz: " << baseJ(2,3) << " slug-ft2" << endl;
373       cout << "    EmptyWeight: " << EmptyWeight << " lbm" << endl;
374       cout << "    CG (x, y, z): " << vbaseXYZcg << endl;
375       // ToDo: Need to add point mass outputs here
376       for (unsigned int i=0; i<PointMasses.size(); i++) {
377         cout << "    Point Mass Object: " << PointMasses[i]->Weight << " lbs. at "
378                    << "X, Y, Z (in.): " << PointMasses[i]->Location(eX) << "  "
379                    << PointMasses[i]->Location(eY) << "  "
380                    << PointMasses[i]->Location(eZ) << endl;
381       }
382     }
383   }
384   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
385     if (from == 0) cout << "Instantiated: FGMassBalance" << endl;
386     if (from == 1) cout << "Destroyed:    FGMassBalance" << endl;
387   }
388   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
389   }
390   if (debug_lvl & 8 ) { // Runtime state variables
391   }
392   if (debug_lvl & 16) { // Sanity checking
393     if (from == 2) {
394       if (EmptyWeight <= 0.0 || EmptyWeight > 1e9)
395         cout << "MassBalance::EmptyWeight out of bounds: " << EmptyWeight << endl;
396       if (Weight <= 0.0 || Weight > 1e9)
397         cout << "MassBalance::Weight out of bounds: " << Weight << endl;
398       if (Mass <= 0.0 || Mass > 1e9)
399         cout << "MassBalance::Mass out of bounds: " << Mass << endl;
400     }
401   }
402   if (debug_lvl & 64) {
403     if (from == 0) { // Constructor
404       cout << IdSrc << endl;
405       cout << IdHdr << endl;
406     }
407   }
408 }
409 }