45#include "FGMassBalance.h"
47#include "input_output/FGXMLElement.h"
48#include "input_output/FGLog.h"
59FGMassBalance::FGMassBalance(FGFDMExec* fdmex)
60 : FGModel(fdmex,
"FGMassBalance")
62 Weight = EmptyWeight = Mass = 0.0;
64 vbaseXYZcg.InitMatrix();
66 vLastXYZcg.InitMatrix();
67 vDeltaXYZcg.InitMatrix();
72 Propagate = fdmex->GetPropagate();
81FGMassBalance::~FGMassBalance()
83 for(
auto pm: PointMasses) delete pm;
90bool FGMassBalance::InitModel(
void)
92 if (!FGModel::InitModel())
return false;
95 vLastXYZcg.InitMatrix(std::numeric_limits<double>::quiet_NaN());
96 vDeltaXYZcg.InitMatrix();
103static FGMatrix33 ReadInertiaMatrix(Element* document)
105 double bixx, biyy, bizz, bixy, bixz, biyz;
107 bixx = biyy = bizz = bixy = bixz = biyz = 0.0;
108 if (document->FindElement(
"ixx"))
109 bixx = document->FindElementValueAsNumberConvertTo(
"ixx",
"SLUG*FT2");
110 if (document->FindElement(
"iyy"))
111 biyy = document->FindElementValueAsNumberConvertTo(
"iyy",
"SLUG*FT2");
112 if (document->FindElement(
"izz"))
113 bizz = document->FindElementValueAsNumberConvertTo(
"izz",
"SLUG*FT2");
114 if (document->FindElement(
"ixy"))
115 bixy = document->FindElementValueAsNumberConvertTo(
"ixy",
"SLUG*FT2");
116 if (document->FindElement(
"ixz"))
117 bixz = document->FindElementValueAsNumberConvertTo(
"ixz",
"SLUG*FT2");
118 if (document->FindElement(
"iyz"))
119 biyz = document->FindElementValueAsNumberConvertTo(
"iyz",
"SLUG*FT2");
123 if (document->GetAttributeValue(
"negated_crossproduct_inertia") ==
string(
"false"))
124 return FGMatrix33( bixx, bixy, -bixz,
128 return FGMatrix33( bixx, -bixy, bixz,
135bool FGMassBalance::Load(Element* document)
137 string element_name =
"";
139 Name =
"Mass Properties Model: " + document->GetAttributeValue(
"name");
142 if (!FGModel::Upload(document,
true))
145 SetAircraftBaseInertias(ReadInertiaMatrix(document));
146 if (document->FindElement(
"emptywt")) {
147 EmptyWeight = document->FindElementValueAsNumberConvertTo(
"emptywt",
"LBS");
150 Element *element = document->FindElement(
"location");
152 element_name = element->GetAttributeValue(
"name");
153 if (element_name ==
"CG") vbaseXYZcg = element->FindElementTripletConvertTo(
"IN");
154 element = document->FindNextElement(
"location");
160 element = document->FindElement(
"pointmass");
162 AddPointMass(element);
163 element = document->FindNextElement(
"pointmass");
166 double ChildFDMWeight = 0.0;
167 for (
size_t fdm=0; fdm<FDMExec->
GetFDMCount(); fdm++) {
168 if (FDMExec->
GetChildFDM(fdm)->mated) ChildFDMWeight += FDMExec->
GetChildFDM(fdm)->exec->GetMassBalance()->GetWeight();
171 Weight = EmptyWeight + in.TanksWeight + GetTotalPointMassWeight()
172 + in.GasMass*slugtolb + ChildFDMWeight;
174 Mass = lbtoslug*Weight;
176 PostLoad(document, FDMExec);
184bool FGMassBalance::Run(
bool Holding)
186 double denom, k1, k2, k3, k4, k5, k6;
187 double Ixx, Iyy, Izz, Ixy, Ixz, Iyz;
189 if (FGModel::Run(Holding))
return true;
190 if (Holding)
return false;
194 double ChildFDMWeight = 0.0;
195 for (
size_t fdm=0; fdm<FDMExec->
GetFDMCount(); fdm++) {
196 if (FDMExec->
GetChildFDM(fdm)->mated) ChildFDMWeight += FDMExec->
GetChildFDM(fdm)->exec->GetMassBalance()->GetWeight();
199 Weight = EmptyWeight + in.TanksWeight + GetTotalPointMassWeight()
200 + in.GasMass*slugtolb + ChildFDMWeight;
202 Mass = lbtoslug*Weight;
206 vXYZcg = (EmptyWeight*vbaseXYZcg
207 + GetPointMassMoment()
209 + in.GasMoment) / Weight;
213 if (std::isnan(vLastXYZcg(eX))) vLastXYZcg = vXYZcg;
214 vDeltaXYZcg = vXYZcg - vLastXYZcg;
215 vDeltaXYZcgBody = StructuralToBody(vLastXYZcg) - StructuralToBody(vXYZcg);
221 Propagate->NudgeBodyLocation(vDeltaXYZcgBody);
228 mJ += GetPointmassInertia( lbtoslug * EmptyWeight, vbaseXYZcg );
230 mJ += CalculatePMInertias();
231 mJ += in.TankInertia;
244 k1 = (Iyy*Izz - Iyz*Iyz);
245 k2 = (Iyz*Ixz + Ixy*Izz);
246 k3 = (Ixy*Iyz + Iyy*Ixz);
248 denom = 1.0/(Ixx*k1 - Ixy*k2 - Ixz*k3 );
252 k4 = (Izz*Ixx - Ixz*Ixz)*denom;
253 k5 = (Ixy*Ixz + Iyz*Ixx)*denom;
254 k6 = (Ixx*Iyy - Ixy*Ixy)*denom;
256 mJinv = { k1, k2, k3,
269void FGMassBalance::AddPointMass(
Element* el)
275 err <<
"Pointmass " << pointmass_name <<
" has no location." << endl;
282 PointMass *pm =
new PointMass(w, vXYZ);
283 pm->SetName(pointmass_name);
287 double radius=0, length=0;
289 Element* radius_element = form_element->FindElement(
"radius");
290 Element* length_element = form_element->FindElement(
"length");
291 if (radius_element) radius = form_element->FindElementValueAsNumberConvertTo(
"radius",
"FT");
292 if (length_element) length = form_element->FindElementValueAsNumberConvertTo(
"length",
"FT");
293 if (shape ==
"tube") {
294 pm->SetPointMassShapeType(PointMass::esTube);
295 pm->SetRadius(radius);
296 pm->SetLength(length);
297 pm->CalculateShapeInertia();
298 }
else if (shape ==
"cylinder") {
299 pm->SetPointMassShapeType(PointMass::esCylinder);
300 pm->SetRadius(radius);
301 pm->SetLength(length);
302 pm->CalculateShapeInertia();
303 }
else if (shape ==
"sphere") {
304 pm->SetPointMassShapeType(PointMass::esSphere);
305 pm->SetRadius(radius);
306 pm->CalculateShapeInertia();
307 }
else if (shape ==
"ball") {
308 pm->SetPointMassShapeType(PointMass::esBall);
309 pm->SetRadius(radius);
310 pm->CalculateShapeInertia();
315 pm->SetPointMassShapeType(PointMass::esUnspecified);
316 pm->SetPointMassMoI(ReadInertiaMatrix(el));
319 pm->bind(PropertyManager.get(), PointMasses.size());
320 PointMasses.push_back(pm);
325double FGMassBalance::GetTotalPointMassWeight(
void)
const
327 double PM_total_weight = 0.0;
329 for(
auto pm: PointMasses)
330 PM_total_weight += pm->Weight;
332 return PM_total_weight;
337const FGColumnVector3& FGMassBalance::GetPointMassMoment(
void)
339 PointMassCG.InitMatrix();
341 for (
auto pm: PointMasses)
342 PointMassCG += pm->Weight * pm->Location;
349const FGMatrix33& FGMassBalance::CalculatePMInertias(
void)
351 if (PointMasses.empty())
return pmJ;
355 for (
auto pm: PointMasses) {
356 pmJ += GetPointmassInertia( lbtoslug * pm->Weight, pm->Location );
357 pmJ += pm->GetPointMassInertia();
392 inchtoft*(r(2)-vXYZcg(2)),
393 inchtoft*(vXYZcg(3)-r(3)));
398void FGMassBalance::bind(
void)
400 PropertyManager->Tie(
"inertia/mass-slugs",
this, &FGMassBalance::GetMass);
401 PropertyManager->Tie(
"inertia/weight-lbs",
this, &FGMassBalance::GetWeight);
402 PropertyManager->Tie(
"inertia/empty-weight-lbs",
this, &FGMassBalance::GetEmptyWeight);
403 PropertyManager->Tie(
"inertia/cg-x-in",
this, eX, &FGMassBalance::GetXYZcg);
404 PropertyManager->Tie(
"inertia/cg-y-in",
this, eY, &FGMassBalance::GetXYZcg);
405 PropertyManager->Tie(
"inertia/cg-z-in",
this, eZ, &FGMassBalance::GetXYZcg);
406 PropertyManager->Tie(
"inertia/ixx-slugs_ft2",
this, &FGMassBalance::GetIxx);
407 PropertyManager->Tie(
"inertia/iyy-slugs_ft2",
this, &FGMassBalance::GetIyy);
408 PropertyManager->Tie(
"inertia/izz-slugs_ft2",
this, &FGMassBalance::GetIzz);
409 PropertyManager->Tie(
"inertia/ixy-slugs_ft2",
this, &FGMassBalance::GetIxy);
410 PropertyManager->Tie(
"inertia/ixz-slugs_ft2",
this, &FGMassBalance::GetIxz);
411 PropertyManager->Tie(
"inertia/iyz-slugs_ft2",
this, &FGMassBalance::GetIyz);
412 PropertyManager->Tie<
FGMassBalance,
int>(
"inertia/print-mass-properties",
this,
413 nullptr, &FGMassBalance::GetMassPropertiesReport);
420void FGMassBalance::PointMass::bind(FGPropertyManager* PropertyManager,
422 string tmp = CreateIndexedPropertyName(
"inertia/pointmass-weight-lbs", num);
423 PropertyManager->Tie( tmp.c_str(),
this, &PointMass::GetPointMassWeight,
424 &PointMass::SetPointMassWeight);
426 tmp = CreateIndexedPropertyName(
"inertia/pointmass-location-X-inches", num);
427 PropertyManager->Tie( tmp.c_str(),
this, eX, &PointMass::GetPointMassLocation,
428 &PointMass::SetPointMassLocation);
429 tmp = CreateIndexedPropertyName(
"inertia/pointmass-location-Y-inches", num);
430 PropertyManager->Tie( tmp.c_str(),
this, eY, &PointMass::GetPointMassLocation,
431 &PointMass::SetPointMassLocation);
432 tmp = CreateIndexedPropertyName(
"inertia/pointmass-location-Z-inches", num);
433 PropertyManager->Tie( tmp.c_str(),
this, eZ, &PointMass::GetPointMassLocation,
434 &PointMass::SetPointMassLocation);
439void FGMassBalance::GetMassPropertiesReport(
int i)
441 FGLogging out(LogLevel::STDOUT);
442 out << endl << LogFormat::BLUE << LogFormat::BOLD
443 <<
" Mass Properties Report (English units: lb, in, slug-ft^2)"
444 << LogFormat::RESET << endl;
445 out <<
" " << LogFormat::UNDERLINE_ON <<
" Mass CG-X CG-Y"
446 <<
" CG-Z Ixx Iyy Izz"
447 <<
" Ixy Ixz Iyz" << LogFormat::UNDERLINE_OFF << endl;
448 out << fixed << setprecision(1);
449 out << setw(34) << left <<
" Base Vehicle " << right << setw(12) << EmptyWeight
450 << setw(8) << vbaseXYZcg(eX) << setw(8) << vbaseXYZcg(eY) << setw(8) << vbaseXYZcg(eZ)
451 << setw(12) << baseJ(1,1) << setw(12) << baseJ(2,2) << setw(12) << baseJ(3,3)
452 << setw(12) << baseJ(1,2) << setw(12) << baseJ(1,3) << setw(12) << baseJ(2,3) << endl;
454 for (
unsigned int i=0;i<PointMasses.size();i++) {
455 PointMass* pm = PointMasses[i];
456 double pmweight = pm->GetPointMassWeight();
457 out << left << setw(4) << i << setw(30) << pm->GetName()
458 << right << setw(12) << pmweight << setw(8) << pm->GetLocation()(eX)
459 << setw(8) << pm->GetLocation()(eY) << setw(8) << pm->GetLocation()(eZ)
460 << setw(12) << pm->GetPointMassMoI(1,1) << setw(12) << pm->GetPointMassMoI(2,2) << setw(12) << pm->GetPointMassMoI(3,3)
461 << setw(12) << pm->GetPointMassMoI(1,2) << setw(12) << pm->GetPointMassMoI(1,3) << setw(12) << pm->GetPointMassMoI(2,3) << endl;
464 const string tank_report = FDMExec->GetPropulsionTankReport();
466 if (!tank_report.empty()) {
467 out <<
"\n " << setw(34) << left << LogFormat::UNDERLINE_ON << LogFormat::BOLD
468 <<
"Tanks Content" << LogFormat::RESET << endl
472 out <<
" " << LogFormat::UNDERLINE_ON << setw(136) <<
" " << LogFormat::UNDERLINE_OFF << endl;
473 out << LogFormat::BOLD << left << setw(34) <<
" Total: " << right << setw(12) << Weight
474 << setw(8) << vXYZcg(eX)
475 << setw(8) << vXYZcg(eY)
476 << setw(8) << vXYZcg(eZ)
477 << setw(12) << mJ(1,1)
478 << setw(12) << mJ(2,2)
479 << setw(12) << mJ(3,3)
480 << setw(12) << mJ(1,2)
481 << setw(12) << mJ(1,3)
482 << setw(12) << mJ(2,3)
483 << LogFormat::NORMAL << endl;
505void FGMassBalance::Debug(
int from)
507 if (debug_lvl <= 0)
return;
511 FGLogging log(LogLevel::DEBUG);
512 log << endl <<
" Mass and Balance:" << endl << fixed;
513 log <<
" baseIxx: " << baseJ(1,1) <<
" slug-ft2" << endl;
514 log <<
" baseIyy: " << baseJ(2,2) <<
" slug-ft2" << endl;
515 log <<
" baseIzz: " << baseJ(3,3) <<
" slug-ft2" << endl;
516 log <<
" baseIxy: " << baseJ(1,2) <<
" slug-ft2" << endl;
517 log <<
" baseIxz: " << baseJ(1,3) <<
" slug-ft2" << endl;
518 log <<
" baseIyz: " << baseJ(2,3) <<
" slug-ft2" << endl;
519 log <<
" Empty Mass: " << EmptyWeight <<
" lbm" << endl;
520 log <<
" CG (x, y, z): " << vbaseXYZcg << endl;
522 for (
unsigned int i=0; i<PointMasses.size(); i++) {
523 log <<
" Point Mass Object: " << PointMasses[i]->Weight <<
" lbs. at "
524 <<
"X, Y, Z (in.): " << PointMasses[i]->Location(eX) <<
" "
525 << PointMasses[i]->Location(eY) <<
" "
526 << PointMasses[i]->Location(eZ) << endl;
530 if (debug_lvl & 2 ) {
531 FGLogging log(LogLevel::DEBUG);
532 if (from == 0) log <<
"Instantiated: FGMassBalance" << endl;
533 if (from == 1) log <<
"Destroyed: FGMassBalance" << endl;
535 if (debug_lvl & 4 ) {
537 if (debug_lvl & 8 ) {
539 if (debug_lvl & 16) {
541 FGLogging log(LogLevel::DEBUG);
542 if (EmptyWeight <= 0.0 || EmptyWeight > 1e9)
543 log <<
"MassBalance::EmptyWeight out of bounds: " << EmptyWeight << endl;
544 if (Weight <= 0.0 || Weight > 1e9)
545 log <<
"MassBalance::Weight out of bounds: " << Weight << endl;
546 if (Mass <= 0.0 || Mass > 1e9)
547 log <<
"MassBalance::Mass out of bounds: " << Mass << endl;
550 if (debug_lvl & 64) {
Element * FindElement(const std::string &el="")
Searches for a specified element.
FGColumnVector3 FindElementTripletConvertTo(const std::string &target_units)
Composes a 3-element column vector for the supplied location or orientation.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
double FindElementValueAsNumberConvertTo(const std::string &el, const std::string &target_units)
Searches for the named element and converts and returns the data belonging to it.
This class implements a 3 element column vector.
bool GetHoldDown(void) const
Gets the value of the property forces/hold-down.
size_t GetFDMCount(void) const
Gets the number of child FDMs.
auto GetChildFDM(int i) const
Gets a particular child FDM.
Models weight, balance and moment of inertia information.
Main namespace for the JSBSim Flight Dynamics Model.