]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/models/FGMassBalance.cpp
Merge branch 'jmt/gps'
[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 #include <iostream>
46 #include <cstdlib>
47
48 using namespace std;
49
50 namespace JSBSim {
51
52 static const char *IdSrc = "$Id$";
53 static const char *IdHdr = ID_MASSBALANCE;
54
55 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56 CLASS IMPLEMENTATION
57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
58
59
60 FGMassBalance::FGMassBalance(FGFDMExec* fdmex) : FGModel(fdmex)
61 {
62   Name = "FGMassBalance";
63   Weight = EmptyWeight = Mass = 0.0;
64
65   vbaseXYZcg.InitMatrix(0.0);
66   vXYZcg.InitMatrix(0.0);
67   vLastXYZcg.InitMatrix(0.0);
68   vDeltaXYZcg.InitMatrix(0.0);
69   baseJ.InitMatrix();
70   mJ.InitMatrix();
71   mJinv.InitMatrix();
72   pmJ.InitMatrix();
73
74   bind();
75
76   Debug(0);
77 }
78
79 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80
81 FGMassBalance::~FGMassBalance()
82 {
83   for (unsigned int i=0; i<PointMasses.size(); i++) delete PointMasses[i];
84   PointMasses.clear();
85
86   Debug(1);
87 }
88
89 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90
91 bool FGMassBalance::InitModel(void)
92 {
93   if (!FGModel::InitModel()) return false;
94
95   vLastXYZcg.InitMatrix(0.0);
96   vDeltaXYZcg.InitMatrix(0.0);
97
98   return true;
99 }
100
101 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102
103 bool FGMassBalance::Load(Element* el)
104 {
105   Element *element;
106   string element_name = "";
107   double bixx, biyy, bizz, bixy, bixz, biyz;
108
109   FGModel::Load(el); // Perform base class Load.
110
111   bixx = biyy = bizz = bixy = bixz = biyz = 0.0;
112   if (el->FindElement("ixx"))
113     bixx = el->FindElementValueAsNumberConvertTo("ixx", "SLUG*FT2");
114   if (el->FindElement("iyy"))
115     biyy = el->FindElementValueAsNumberConvertTo("iyy", "SLUG*FT2");
116   if (el->FindElement("izz"))
117     bizz = el->FindElementValueAsNumberConvertTo("izz", "SLUG*FT2");
118   if (el->FindElement("ixy"))
119     bixy = el->FindElementValueAsNumberConvertTo("ixy", "SLUG*FT2");
120   if (el->FindElement("ixz"))
121     bixz = el->FindElementValueAsNumberConvertTo("ixz", "SLUG*FT2");
122   if (el->FindElement("iyz"))
123     biyz = el->FindElementValueAsNumberConvertTo("iyz", "SLUG*FT2");
124   SetAircraftBaseInertias(FGMatrix33(  bixx,  -bixy,  bixz,
125                                       -bixy,  biyy,  -biyz,
126                                        bixz,  -biyz,  bizz ));
127   EmptyWeight = el->FindElementValueAsNumberConvertTo("emptywt", "LBS");
128
129   element = el->FindElement("location");
130   while (element) {
131     element_name = element->GetAttributeValue("name");
132     if (element_name == "CG") vbaseXYZcg = element->FindElementTripletConvertTo("IN");
133     element = el->FindNextElement("location");
134   }
135
136 // Find all POINTMASS elements that descend from this METRICS branch of the
137 // config file.
138
139   element = el->FindElement("pointmass");
140   while (element) {
141     AddPointMass(element);
142     element = el->FindNextElement("pointmass");
143   }
144
145   double ChildFDMWeight = 0.0;
146   for (int fdm=0; fdm<FDMExec->GetFDMCount(); fdm++) {
147     if (FDMExec->GetChildFDM(fdm)->mated) ChildFDMWeight += FDMExec->GetChildFDM(fdm)->exec->GetMassBalance()->GetWeight();
148   }
149
150   Weight = EmptyWeight + Propulsion->GetTanksWeight() + GetTotalPointMassWeight()
151     + BuoyantForces->GetGasMass()*slugtolb + ChildFDMWeight;
152
153   Mass = lbtoslug*Weight;
154
155   FGModel::PostLoad(el);
156
157   Debug(2);
158   return true;
159 }
160
161 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
162
163 bool FGMassBalance::Run(void)
164 {
165   double denom, k1, k2, k3, k4, k5, k6;
166   double Ixx, Iyy, Izz, Ixy, Ixz, Iyz;
167
168   if (FGModel::Run()) return true;
169   if (FDMExec->Holding()) return false;
170
171   RunPreFunctions();
172
173   double ChildFDMWeight = 0.0;
174   for (int fdm=0; fdm<FDMExec->GetFDMCount(); fdm++) {
175     if (FDMExec->GetChildFDM(fdm)->mated) ChildFDMWeight += FDMExec->GetChildFDM(fdm)->exec->GetMassBalance()->GetWeight();
176   }
177
178   Weight = EmptyWeight + Propulsion->GetTanksWeight() + GetTotalPointMassWeight()
179     + BuoyantForces->GetGasMass()*slugtolb + ChildFDMWeight;
180
181   Mass = lbtoslug*Weight;
182
183 // Calculate new CG
184
185   vXYZcg = (Propulsion->GetTanksMoment() + EmptyWeight*vbaseXYZcg
186             + GetPointMassMoment()
187             + BuoyantForces->GetGasMassMoment()) / Weight;
188
189   // Track frame-by-frame delta CG, and move the EOM-tracked location
190   // by this amount.
191   if (vLastXYZcg.Magnitude() == 0.0) vLastXYZcg = vXYZcg;
192   vDeltaXYZcg = vXYZcg - vLastXYZcg;
193   vDeltaXYZcgBody = StructuralToBody(vLastXYZcg) - StructuralToBody(vXYZcg);
194   vLastXYZcg = vXYZcg;
195   Propagate->NudgeBodyLocation(vDeltaXYZcgBody);
196
197 // Calculate new total moments of inertia
198
199   // At first it is the base configuration inertia matrix ...
200   mJ = baseJ;
201   // ... with the additional term originating from the parallel axis theorem.
202   mJ += GetPointmassInertia( lbtoslug * EmptyWeight, vbaseXYZcg );
203   // Then add the contributions from the additional pointmasses.
204   mJ += CalculatePMInertias();
205   mJ += Propulsion->CalculateTankInertias();
206   mJ += BuoyantForces->GetGasMassInertia();
207
208   Ixx = mJ(1,1);
209   Iyy = mJ(2,2);
210   Izz = mJ(3,3);
211   Ixy = -mJ(1,2);
212   Ixz = -mJ(1,3);
213   Iyz = -mJ(2,3);
214
215 // Calculate inertia matrix inverse (ref. Stevens and Lewis, "Flight Control & Simulation")
216
217   k1 = (Iyy*Izz - Iyz*Iyz);
218   k2 = (Iyz*Ixz + Ixy*Izz);
219   k3 = (Ixy*Iyz + Iyy*Ixz);
220
221   denom = 1.0/(Ixx*k1 - Ixy*k2 - Ixz*k3 );
222   k1 = k1*denom;
223   k2 = k2*denom;
224   k3 = k3*denom;
225   k4 = (Izz*Ixx - Ixz*Ixz)*denom;
226   k5 = (Ixy*Ixz + Iyz*Ixx)*denom;
227   k6 = (Ixx*Iyy - Ixy*Ixy)*denom;
228
229   mJinv.InitMatrix( k1, k2, k3,
230                     k2, k4, k5,
231                     k3, k5, k6 );
232
233   RunPostFunctions();
234
235   Debug(0);
236
237   return false;
238 }
239
240 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241
242 void FGMassBalance::AddPointMass(Element* el)
243 {
244   Element* loc_element = el->FindElement("location");
245   string pointmass_name = el->GetAttributeValue("name");
246   if (!loc_element) {
247     cerr << "Pointmass " << pointmass_name << " has no location." << endl;
248     exit(-1);
249   }
250
251   double w = el->FindElementValueAsNumberConvertTo("weight", "LBS");
252   FGColumnVector3 vXYZ = loc_element->FindElementTripletConvertTo("IN");
253
254   PointMasses.push_back(new PointMass(w, vXYZ));
255   PointMasses.back()->bind(PropertyManager, PointMasses.size()-1);
256 }
257
258 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
259
260 double FGMassBalance::GetTotalPointMassWeight(void)
261 {
262   double PM_total_weight = 0.0;
263
264   for (unsigned int i=0; i<PointMasses.size(); i++) {
265     PM_total_weight += PointMasses[i]->Weight;
266   }
267   return PM_total_weight;
268 }
269
270 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
271
272 FGColumnVector3& FGMassBalance::GetPointMassMoment(void)
273 {
274   PointMassCG.InitMatrix();
275
276   for (unsigned int i=0; i<PointMasses.size(); i++) {
277     PointMassCG += PointMasses[i]->Weight*PointMasses[i]->Location;
278   }
279   return PointMassCG;
280 }
281
282 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
283
284 FGMatrix33& FGMassBalance::CalculatePMInertias(void)
285 {
286   unsigned int size;
287
288   size = PointMasses.size();
289   if (size == 0) return pmJ;
290
291   pmJ = FGMatrix33();
292
293   for (unsigned int i=0; i<size; i++)
294     pmJ += GetPointmassInertia( lbtoslug * PointMasses[i]->Weight, PointMasses[i]->Location );
295
296   return pmJ;
297 }
298
299 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
300
301 FGColumnVector3 FGMassBalance::StructuralToBody(const FGColumnVector3& r) const
302 {
303   // Under the assumption that in the structural frame the:
304   //
305   // - X-axis is directed afterwards,
306   // - Y-axis is directed towards the right,
307   // - Z-axis is directed upwards,
308   //
309   // (as documented in http://jsbsim.sourceforge.net/JSBSimCoordinates.pdf)
310   // we have to subtract first the center of gravity of the plane which
311   // is also defined in the structural frame:
312   //
313   //   FGColumnVector3 cgOff = r - vXYZcg;
314   //
315   // Next, we do a change of units:
316   //
317   //   cgOff *= inchtoft;
318   //
319   // And then a 180 degree rotation is done about the Y axis so that the:
320   //
321   // - X-axis is directed forward,
322   // - Y-axis is directed towards the right,
323   // - Z-axis is directed downward.
324   //
325   // This is needed because the structural and body frames are 180 degrees apart.
326
327   return FGColumnVector3(inchtoft*(vXYZcg(1)-r(1)),
328                          inchtoft*(r(2)-vXYZcg(2)),
329                          inchtoft*(vXYZcg(3)-r(3)));
330 }
331
332 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
333
334 void FGMassBalance::bind(void)
335 {
336   typedef double (FGMassBalance::*PMF)(int) const;
337   PropertyManager->Tie("inertia/mass-slugs", this,
338                        &FGMassBalance::GetMass);
339   PropertyManager->Tie("inertia/weight-lbs", this,
340                        &FGMassBalance::GetWeight);
341   PropertyManager->Tie("inertia/empty-weight-lbs", this,
342     &FGMassBalance::GetWeight, &FGMassBalance::SetEmptyWeight);
343   PropertyManager->Tie("inertia/cg-x-in", this,1,
344                        (PMF)&FGMassBalance::GetXYZcg);
345   PropertyManager->Tie("inertia/cg-y-in", this,2,
346                        (PMF)&FGMassBalance::GetXYZcg);
347   PropertyManager->Tie("inertia/cg-z-in", this,3,
348                        (PMF)&FGMassBalance::GetXYZcg);
349 }
350
351 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
352
353 void FGMassBalance::PointMass::bind(FGPropertyManager* PropertyManager, int num) {
354   string tmp = CreateIndexedPropertyName("inertia/pointmass-weight-lbs", num);
355   PropertyManager->Tie( tmp.c_str(), this, &PointMass::GetPointMassWeight,
356                                        &PointMass::SetPointMassWeight);
357
358   tmp = CreateIndexedPropertyName("inertia/pointmass-location-X-inches", num);
359   PropertyManager->Tie( tmp.c_str(), this, eX, &PointMass::GetPointMassLocation,
360                                            &PointMass::SetPointMassLocation);
361   tmp = CreateIndexedPropertyName("inertia/pointmass-location-Y-inches", num);
362   PropertyManager->Tie( tmp.c_str(), this, eY, &PointMass::GetPointMassLocation,
363                                            &PointMass::SetPointMassLocation);
364   tmp = CreateIndexedPropertyName("inertia/pointmass-location-Z-inches", num);
365   PropertyManager->Tie( tmp.c_str(), this, eZ, &PointMass::GetPointMassLocation,
366                                            &PointMass::SetPointMassLocation);
367 }
368
369 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
370 //    The bitmasked value choices are as follows:
371 //    unset: In this case (the default) JSBSim would only print
372 //       out the normally expected messages, essentially echoing
373 //       the config files as they are read. If the environment
374 //       variable is not set, debug_lvl is set to 1 internally
375 //    0: This requests JSBSim not to output any messages
376 //       whatsoever.
377 //    1: This value explicity requests the normal JSBSim
378 //       startup messages
379 //    2: This value asks for a message to be printed out when
380 //       a class is instantiated
381 //    4: When this value is set, a message is displayed when a
382 //       FGModel object executes its Run() method
383 //    8: When this value is set, various runtime state variables
384 //       are printed out periodically
385 //    16: When set various parameters are sanity checked and
386 //       a message is printed out when they go out of bounds
387
388 void FGMassBalance::Debug(int from)
389 {
390   if (debug_lvl <= 0) return;
391
392   if (debug_lvl & 1) { // Standard console startup message output
393     if (from == 2) { // Loading
394       cout << endl << "  Mass and Balance:" << endl;
395       cout << "    baseIxx: " << baseJ(1,1) << " slug-ft2" << endl;
396       cout << "    baseIyy: " << baseJ(2,2) << " slug-ft2" << endl;
397       cout << "    baseIzz: " << baseJ(3,3) << " slug-ft2" << endl;
398       cout << "    baseIxy: " << baseJ(1,2) << " slug-ft2" << endl;
399       cout << "    baseIxz: " << baseJ(1,3) << " slug-ft2" << endl;
400       cout << "    baseIyz: " << baseJ(2,3) << " slug-ft2" << endl;
401       cout << "    EmptyWeight: " << EmptyWeight << " lbm" << endl;
402       cout << "    CG (x, y, z): " << vbaseXYZcg << endl;
403       // ToDo: Need to add point mass outputs here
404       for (unsigned int i=0; i<PointMasses.size(); i++) {
405         cout << "    Point Mass Object: " << PointMasses[i]->Weight << " lbs. at "
406                    << "X, Y, Z (in.): " << PointMasses[i]->Location(eX) << "  "
407                    << PointMasses[i]->Location(eY) << "  "
408                    << PointMasses[i]->Location(eZ) << endl;
409       }
410     }
411   }
412   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
413     if (from == 0) cout << "Instantiated: FGMassBalance" << endl;
414     if (from == 1) cout << "Destroyed:    FGMassBalance" << endl;
415   }
416   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
417   }
418   if (debug_lvl & 8 ) { // Runtime state variables
419   }
420   if (debug_lvl & 16) { // Sanity checking
421     if (from == 2) {
422       if (EmptyWeight <= 0.0 || EmptyWeight > 1e9)
423         cout << "MassBalance::EmptyWeight out of bounds: " << EmptyWeight << endl;
424       if (Weight <= 0.0 || Weight > 1e9)
425         cout << "MassBalance::Weight out of bounds: " << Weight << endl;
426       if (Mass <= 0.0 || Mass > 1e9)
427         cout << "MassBalance::Mass out of bounds: " << Mass << endl;
428     }
429   }
430   if (debug_lvl & 64) {
431     if (from == 0) { // Constructor
432       cout << IdSrc << endl;
433       cout << IdHdr << endl;
434     }
435   }
436 }
437 }