43#include "FGMassBalance.h"
45#include "input_output/FGXMLElement.h"
46#include "input_output/FGLog.h"
57FGMassBalance::FGMassBalance(FGFDMExec* fdmex)
60 Name =
"FGMassBalance";
61 Weight = EmptyWeight = Mass = 0.0;
63 vbaseXYZcg.InitMatrix();
65 vLastXYZcg.InitMatrix();
66 vDeltaXYZcg.InitMatrix();
71 Propagate = fdmex->GetPropagate();
80FGMassBalance::~FGMassBalance()
82 for(
auto pm: PointMasses) delete pm;
89bool FGMassBalance::InitModel(
void)
91 if (!FGModel::InitModel())
return false;
93 vLastXYZcg.InitMatrix();
94 vDeltaXYZcg.InitMatrix();
101static FGMatrix33 ReadInertiaMatrix(Element* document)
103 double bixx, biyy, bizz, bixy, bixz, biyz;
105 bixx = biyy = bizz = bixy = bixz = biyz = 0.0;
106 if (document->FindElement(
"ixx"))
107 bixx = document->FindElementValueAsNumberConvertTo(
"ixx",
"SLUG*FT2");
108 if (document->FindElement(
"iyy"))
109 biyy = document->FindElementValueAsNumberConvertTo(
"iyy",
"SLUG*FT2");
110 if (document->FindElement(
"izz"))
111 bizz = document->FindElementValueAsNumberConvertTo(
"izz",
"SLUG*FT2");
112 if (document->FindElement(
"ixy"))
113 bixy = document->FindElementValueAsNumberConvertTo(
"ixy",
"SLUG*FT2");
114 if (document->FindElement(
"ixz"))
115 bixz = document->FindElementValueAsNumberConvertTo(
"ixz",
"SLUG*FT2");
116 if (document->FindElement(
"iyz"))
117 biyz = document->FindElementValueAsNumberConvertTo(
"iyz",
"SLUG*FT2");
121 if (document->GetAttributeValue(
"negated_crossproduct_inertia") ==
string(
"false"))
122 return FGMatrix33( bixx, bixy, -bixz,
126 return FGMatrix33( bixx, -bixy, bixz,
133bool FGMassBalance::Load(Element* document)
135 string element_name =
"";
137 Name =
"Mass Properties Model: " + document->GetAttributeValue(
"name");
140 if (!FGModel::Upload(document,
true))
143 SetAircraftBaseInertias(ReadInertiaMatrix(document));
144 if (document->FindElement(
"emptywt")) {
145 EmptyWeight = document->FindElementValueAsNumberConvertTo(
"emptywt",
"LBS");
148 Element *element = document->FindElement(
"location");
150 element_name = element->GetAttributeValue(
"name");
151 if (element_name ==
"CG") vbaseXYZcg = element->FindElementTripletConvertTo(
"IN");
152 element = document->FindNextElement(
"location");
158 element = document->FindElement(
"pointmass");
160 AddPointMass(element);
161 element = document->FindNextElement(
"pointmass");
164 double ChildFDMWeight = 0.0;
165 for (
size_t fdm=0; fdm<FDMExec->
GetFDMCount(); fdm++) {
166 if (FDMExec->
GetChildFDM(fdm)->mated) ChildFDMWeight += FDMExec->
GetChildFDM(fdm)->exec->GetMassBalance()->GetWeight();
169 Weight = EmptyWeight + in.TanksWeight + GetTotalPointMassWeight()
170 + in.GasMass*slugtolb + ChildFDMWeight;
172 Mass = lbtoslug*Weight;
174 PostLoad(document, FDMExec);
182bool FGMassBalance::Run(
bool Holding)
184 double denom, k1, k2, k3, k4, k5, k6;
185 double Ixx, Iyy, Izz, Ixy, Ixz, Iyz;
187 if (FGModel::Run(Holding))
return true;
188 if (Holding)
return false;
192 double ChildFDMWeight = 0.0;
193 for (
size_t fdm=0; fdm<FDMExec->
GetFDMCount(); fdm++) {
194 if (FDMExec->
GetChildFDM(fdm)->mated) ChildFDMWeight += FDMExec->
GetChildFDM(fdm)->exec->GetMassBalance()->GetWeight();
197 Weight = EmptyWeight + in.TanksWeight + GetTotalPointMassWeight()
198 + in.GasMass*slugtolb + ChildFDMWeight;
200 Mass = lbtoslug*Weight;
204 vXYZcg = (EmptyWeight*vbaseXYZcg
205 + GetPointMassMoment()
207 + in.GasMoment) / Weight;
211 if (vLastXYZcg.Magnitude() == 0.0) vLastXYZcg = vXYZcg;
212 vDeltaXYZcg = vXYZcg - vLastXYZcg;
213 vDeltaXYZcgBody = StructuralToBody(vLastXYZcg) - StructuralToBody(vXYZcg);
219 Propagate->NudgeBodyLocation(vDeltaXYZcgBody);
226 mJ += GetPointmassInertia( lbtoslug * EmptyWeight, vbaseXYZcg );
228 mJ += CalculatePMInertias();
229 mJ += in.TankInertia;
242 k1 = (Iyy*Izz - Iyz*Iyz);
243 k2 = (Iyz*Ixz + Ixy*Izz);
244 k3 = (Ixy*Iyz + Iyy*Ixz);
246 denom = 1.0/(Ixx*k1 - Ixy*k2 - Ixz*k3 );
250 k4 = (Izz*Ixx - Ixz*Ixz)*denom;
251 k5 = (Ixy*Ixz + Iyz*Ixx)*denom;
252 k6 = (Ixx*Iyy - Ixy*Ixy)*denom;
254 mJinv = { k1, k2, k3,
267void FGMassBalance::AddPointMass(
Element* el)
273 err <<
"Pointmass " << pointmass_name <<
" has no location." << endl;
280 PointMass *pm =
new PointMass(w, vXYZ);
281 pm->SetName(pointmass_name);
285 double radius=0, length=0;
287 Element* radius_element = form_element->FindElement(
"radius");
288 Element* length_element = form_element->FindElement(
"length");
289 if (radius_element) radius = form_element->FindElementValueAsNumberConvertTo(
"radius",
"FT");
290 if (length_element) length = form_element->FindElementValueAsNumberConvertTo(
"length",
"FT");
291 if (shape ==
"tube") {
292 pm->SetPointMassShapeType(PointMass::esTube);
293 pm->SetRadius(radius);
294 pm->SetLength(length);
295 pm->CalculateShapeInertia();
296 }
else if (shape ==
"cylinder") {
297 pm->SetPointMassShapeType(PointMass::esCylinder);
298 pm->SetRadius(radius);
299 pm->SetLength(length);
300 pm->CalculateShapeInertia();
301 }
else if (shape ==
"sphere") {
302 pm->SetPointMassShapeType(PointMass::esSphere);
303 pm->SetRadius(radius);
304 pm->CalculateShapeInertia();
305 }
else if (shape ==
"ball") {
306 pm->SetPointMassShapeType(PointMass::esBall);
307 pm->SetRadius(radius);
308 pm->CalculateShapeInertia();
313 pm->SetPointMassShapeType(PointMass::esUnspecified);
314 pm->SetPointMassMoI(ReadInertiaMatrix(el));
317 pm->bind(PropertyManager.get(), PointMasses.size());
318 PointMasses.push_back(pm);
323double FGMassBalance::GetTotalPointMassWeight(
void)
const
325 double PM_total_weight = 0.0;
327 for(
auto pm: PointMasses)
328 PM_total_weight += pm->Weight;
330 return PM_total_weight;
335const FGColumnVector3& FGMassBalance::GetPointMassMoment(
void)
337 PointMassCG.InitMatrix();
339 for (
auto pm: PointMasses)
340 PointMassCG += pm->Weight * pm->Location;
347const FGMatrix33& FGMassBalance::CalculatePMInertias(
void)
349 if (PointMasses.empty())
return pmJ;
353 for (
auto pm: PointMasses) {
354 pmJ += GetPointmassInertia( lbtoslug * pm->Weight, pm->Location );
355 pmJ += pm->GetPointMassInertia();
390 inchtoft*(r(2)-vXYZcg(2)),
391 inchtoft*(vXYZcg(3)-r(3)));
396void FGMassBalance::bind(
void)
398 PropertyManager->Tie(
"inertia/mass-slugs",
this, &FGMassBalance::GetMass);
399 PropertyManager->Tie(
"inertia/weight-lbs",
this, &FGMassBalance::GetWeight);
400 PropertyManager->Tie(
"inertia/empty-weight-lbs",
this, &FGMassBalance::GetEmptyWeight);
401 PropertyManager->Tie(
"inertia/cg-x-in",
this, eX, &FGMassBalance::GetXYZcg);
402 PropertyManager->Tie(
"inertia/cg-y-in",
this, eY, &FGMassBalance::GetXYZcg);
403 PropertyManager->Tie(
"inertia/cg-z-in",
this, eZ, &FGMassBalance::GetXYZcg);
404 PropertyManager->Tie(
"inertia/ixx-slugs_ft2",
this, &FGMassBalance::GetIxx);
405 PropertyManager->Tie(
"inertia/iyy-slugs_ft2",
this, &FGMassBalance::GetIyy);
406 PropertyManager->Tie(
"inertia/izz-slugs_ft2",
this, &FGMassBalance::GetIzz);
407 PropertyManager->Tie(
"inertia/ixy-slugs_ft2",
this, &FGMassBalance::GetIxy);
408 PropertyManager->Tie(
"inertia/ixz-slugs_ft2",
this, &FGMassBalance::GetIxz);
409 PropertyManager->Tie(
"inertia/iyz-slugs_ft2",
this, &FGMassBalance::GetIyz);
410 PropertyManager->Tie<
FGMassBalance,
int>(
"inertia/print-mass-properties",
this,
411 nullptr, &FGMassBalance::GetMassPropertiesReport);
418void FGMassBalance::PointMass::bind(FGPropertyManager* PropertyManager,
420 string tmp = CreateIndexedPropertyName(
"inertia/pointmass-weight-lbs", num);
421 PropertyManager->Tie( tmp.c_str(),
this, &PointMass::GetPointMassWeight,
422 &PointMass::SetPointMassWeight);
424 tmp = CreateIndexedPropertyName(
"inertia/pointmass-location-X-inches", num);
425 PropertyManager->Tie( tmp.c_str(),
this, eX, &PointMass::GetPointMassLocation,
426 &PointMass::SetPointMassLocation);
427 tmp = CreateIndexedPropertyName(
"inertia/pointmass-location-Y-inches", num);
428 PropertyManager->Tie( tmp.c_str(),
this, eY, &PointMass::GetPointMassLocation,
429 &PointMass::SetPointMassLocation);
430 tmp = CreateIndexedPropertyName(
"inertia/pointmass-location-Z-inches", num);
431 PropertyManager->Tie( tmp.c_str(),
this, eZ, &PointMass::GetPointMassLocation,
432 &PointMass::SetPointMassLocation);
437void FGMassBalance::GetMassPropertiesReport(
int i)
439 FGLogging out(LogLevel::STDOUT);
440 out << endl << LogFormat::BLUE << LogFormat::BOLD
441 <<
" Mass Properties Report (English units: lbf, in, slug-ft^2)"
442 << LogFormat::RESET << endl;
443 out <<
" " << LogFormat::UNDERLINE_ON <<
" Weight CG-X CG-Y"
444 <<
" CG-Z Ixx Iyy Izz"
445 <<
" Ixy Ixz Iyz" << LogFormat::UNDERLINE_OFF << endl;
446 out << fixed << setprecision(1);
447 out << LogFormat::BOLD << setw(34) << left <<
" Base Vehicle " << LogFormat::NORMAL
448 << right << setw(12) << EmptyWeight
449 << setw(8) << vbaseXYZcg(eX) << setw(8) << vbaseXYZcg(eY) << setw(8) << vbaseXYZcg(eZ)
450 << setw(12) << baseJ(1,1) << setw(12) << baseJ(2,2) << setw(12) << baseJ(3,3)
451 << setw(12) << baseJ(1,2) << setw(12) << baseJ(1,3) << setw(12) << baseJ(2,3) << endl;
453 for (
unsigned int i=0;i<PointMasses.size();i++) {
454 PointMass* pm = PointMasses[i];
455 double pmweight = pm->GetPointMassWeight();
456 out << LogFormat::BOLD << left << setw(4) << i << setw(30) << pm->GetName() << LogFormat::NORMAL
457 << right << setw(12) << pmweight << setw(8) << pm->GetLocation()(eX)
458 << setw(8) << pm->GetLocation()(eY) << setw(8) << pm->GetLocation()(eZ)
459 << setw(12) << pm->GetPointMassMoI(1,1) << setw(12) << pm->GetPointMassMoI(2,2) << setw(12) << pm->GetPointMassMoI(3,3)
460 << setw(12) << pm->GetPointMassMoI(1,2) << setw(12) << pm->GetPointMassMoI(1,3) << setw(12) << pm->GetPointMassMoI(2,3) << endl;
463 out << FDMExec->GetPropulsionTankReport();
465 out <<
" " << LogFormat::UNDERLINE_ON << setw(136) <<
" " << LogFormat::UNDERLINE_OFF << endl;
466 out << LogFormat::BOLD << left << setw(30) <<
" Total: " << right << setw(14) << Weight
467 << setw(8) << vXYZcg(eX)
468 << setw(8) << vXYZcg(eY)
469 << setw(8) << vXYZcg(eZ)
470 << setw(12) << mJ(1,1)
471 << setw(12) << mJ(2,2)
472 << setw(12) << mJ(3,3)
473 << setw(12) << mJ(1,2)
474 << setw(12) << mJ(1,3)
475 << setw(12) << mJ(2,3)
476 << LogFormat::NORMAL << endl;
498void FGMassBalance::Debug(
int from)
500 if (debug_lvl <= 0)
return;
504 FGLogging log(LogLevel::DEBUG);
505 log << endl <<
" Mass and Balance:" << endl << fixed;
506 log <<
" baseIxx: " << baseJ(1,1) <<
" slug-ft2" << endl;
507 log <<
" baseIyy: " << baseJ(2,2) <<
" slug-ft2" << endl;
508 log <<
" baseIzz: " << baseJ(3,3) <<
" slug-ft2" << endl;
509 log <<
" baseIxy: " << baseJ(1,2) <<
" slug-ft2" << endl;
510 log <<
" baseIxz: " << baseJ(1,3) <<
" slug-ft2" << endl;
511 log <<
" baseIyz: " << baseJ(2,3) <<
" slug-ft2" << endl;
512 log <<
" Empty Weight: " << EmptyWeight <<
" lbm" << endl;
513 log <<
" CG (x, y, z): " << vbaseXYZcg << endl;
515 for (
unsigned int i=0; i<PointMasses.size(); i++) {
516 log <<
" Point Mass Object: " << PointMasses[i]->Weight <<
" lbs. at "
517 <<
"X, Y, Z (in.): " << PointMasses[i]->Location(eX) <<
" "
518 << PointMasses[i]->Location(eY) <<
" "
519 << PointMasses[i]->Location(eZ) << endl;
523 if (debug_lvl & 2 ) {
524 FGLogging log(LogLevel::DEBUG);
525 if (from == 0) log <<
"Instantiated: FGMassBalance" << endl;
526 if (from == 1) log <<
"Destroyed: FGMassBalance" << endl;
528 if (debug_lvl & 4 ) {
530 if (debug_lvl & 8 ) {
532 if (debug_lvl & 16) {
534 FGLogging log(LogLevel::DEBUG);
535 if (EmptyWeight <= 0.0 || EmptyWeight > 1e9)
536 log <<
"MassBalance::EmptyWeight out of bounds: " << EmptyWeight << endl;
537 if (Weight <= 0.0 || Weight > 1e9)
538 log <<
"MassBalance::Weight out of bounds: " << Weight << endl;
539 if (Mass <= 0.0 || Mass > 1e9)
540 log <<
"MassBalance::Mass out of bounds: " << Mass << endl;
543 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.