41#include "input_output/FGXMLElement.h"
52 : TankNumber(tank_number)
54 string token, strFuelName;
60 InitialTemperature = Temperature = -9999.0;
61 Ixx = Iyy = Izz = 0.0;
63 Radius = Contents = Standpipe = Length = InnerRadius = 0.0;
65 InitialStandpipe = 0.0;
66 Capacity = 0.00001; UnusableVol = 0.0;
67 Priority = InitialPriority = 1;
69 vXYZ_drain.InitMatrix();
70 ixx_unit = iyy_unit = izz_unit = 1.0;
71 grainType = gtUNKNOWN;
74 if (type ==
"FUEL") Type = ttFUEL;
75 else if (type ==
"OXIDIZER") Type = ttOXIDIZER;
76 else Type = ttUNKNOWN;
84 log <<
"No location found for this tank.\n";
116 SetPriority( InitialPriority );
120 log <<
"Tank capacity must not be zero. Reset to 0.00001 lbs!\n";
126 err <<
"Tank capacity (" << Capacity
127 <<
" lbs) is lower than the amount of unusable fuel (" <<
GetUnusable()
128 <<
" lbs) for tank " << tank_number
129 <<
"! Did you accidentally swap unusable and capacity?\n";
132 if (Contents > Capacity) {
134 err <<
"Tank content (" << Contents
135 <<
" lbs) is greater than tank capacity (" << Capacity
136 <<
" lbs) for tank " << tank_number
137 <<
"! Did you accidentally swap contents and capacity?\n";
142 log << el->
ReadFrom() <<
"Tank content (" << Contents
143 <<
" lbs) is lower than the amount of unusable fuel (" <<
GetUnusable()
144 <<
" lbs) for tank " << tank_number <<
"\n";
147 PctFull = 100.0*Contents/Capacity;
155 if (strGType ==
"CYLINDRICAL") grainType = gtCYLINDRICAL;
156 else if (strGType ==
"ENDBURNING") grainType = gtENDBURNING;
157 else if (strGType ==
"FUNCTION") {
158 grainType = gtFUNCTION;
166 throw(
"For tank "+to_string(TankNumber)+
" and when grain_config is specified an ixx must be specified when the FUNCTION grain type is specified.");
177 err <<
"For tank " << TankNumber
178 <<
" and when grain_config is specified an iyy must be specified when the FUNCTION grain type is specified.\n";
190 err <<
"For tank " << TankNumber
191 <<
" and when grain_config is specified an izz must be specified when the FUNCTION grain type is specified.\n";
197 log <<
"Unknown propellant grain type specified\n";
209 if (Radius <= InnerRadius) {
211 err <<
"The bore diameter should be smaller than the total grain diameter!\n";
214 Volume = M_PI * Length * (Radius*Radius - InnerRadius*InnerRadius);
217 Volume = M_PI * Length * Radius * Radius;
225 err <<
"Unknown grain type found in this rocket engine definition.\n";
229 Density = (Capacity*lbtoslug)/Volume;
234 if (Temperature != -9999.0) InitialTemperature = Temperature =
FahrenheitToCelsius(Temperature);
235 Area = 40.0 * pow(Capacity/1975, 0.666666667);
240 bind(PropertyManager.get());
256 SetTemperature( InitialTemperature );
257 SetStandpipe ( InitialStandpipe );
258 SetContents ( InitialContents );
259 PctFull = 100.0*Contents/Capacity;
260 SetPriority( InitialPriority );
268 return vXYZ_drain + (Contents/Capacity)*(vXYZ - vXYZ_drain);
273double FGTank::GetXYZ(
int idx)
const
275 return vXYZ_drain(idx) + (Contents/Capacity)*(vXYZ(idx)-vXYZ_drain(idx));
282 double remaining = Contents - used;
290 remaining = Contents;
293 PctFull = 100.0*Contents/Capacity;
301double FGTank::Fill(
double amount)
303 double overage = 0.0;
307 if (Contents > Capacity) {
308 overage = Contents - Capacity;
312 PctFull = Contents/Capacity*100.0;
322void FGTank::SetContents(
double amount)
325 if (Contents > Capacity) {
329 PctFull = Contents/Capacity*100.0;
337void FGTank::SetContentsGallons(
double gallons)
339 SetContents(gallons * Density);
347 if(ExternalFlow < 0.)
Drain( -ExternalFlow *dt);
348 else Fill(ExternalFlow * dt);
350 if (Temperature == -9999.0)
return 0.0;
351 double HeatCapacity = 900.0;
352 double TempFlowFactor = 1.115;
353 double Tdiff = TAT_C - Temperature;
355 if (fabs(Tdiff) > 0.1 && Contents > 0.01) {
356 dTemp = (TempFlowFactor * Area * Tdiff * dt) / (Contents * HeatCapacity);
359 return Temperature += (dTemp + dTemp);
379void FGTank::CalculateInertias(
void)
381 double Mass = Contents*lbtoslug;
383 double Rad2 = Radius*Radius;
385 if (grainType != gtUNKNOWN) {
388 Volume = (Contents*lbtoslug)/Density;
389 }
else if (Contents <= 0.0) {
393 err <<
"Solid propellant grain density is zero!\n";
399 InnerRadius = sqrt(Rad2 - Volume/(M_PI * Length));
400 RadSumSqr = (Rad2 + InnerRadius*InnerRadius)/144.0;
401 Ixx = 0.5*Mass*RadSumSqr;
402 Iyy = Mass*(3.0*RadSumSqr + Length*Length/144.0)/12.0;
406 Length = Volume/(M_PI*Rad2);
407 Ixx = 0.5*Mass*Rad2/144.0;
408 Iyy = Mass*(3.0*Rad2 + Length*Length)/(144.0*12.0);
412 Ixx = function_ixx->
GetValue()*ixx_unit;
413 Iyy = function_iyy->
GetValue()*iyy_unit;
414 Izz = function_izz->
GetValue()*izz_unit;
419 err <<
"Unknown grain type found.\n";
426 if (Radius > 0.0) Ixx = Iyy = Izz = Mass * InertiaFactor * 0.4 * Radius * Radius / 144.0;
435 if (name ==
"AVGAS")
return 6.02;
436 else if (name ==
"JET-A")
return 6.74;
437 else if (name ==
"JET-A1")
return 6.74;
438 else if (name ==
"JET-B")
return 6.48;
439 else if (name ==
"JP-1")
return 6.76;
440 else if (name ==
"JP-2")
return 6.38;
441 else if (name ==
"JP-3")
return 6.34;
442 else if (name ==
"JP-4")
return 6.48;
443 else if (name ==
"JP-5")
return 6.81;
444 else if (name ==
"JP-6")
return 6.55;
445 else if (name ==
"JP-7")
return 6.61;
446 else if (name ==
"JP-8")
return 6.66;
447 else if (name ==
"JP-8+100")
return 6.66;
450 else if (name ==
"RP-1")
return 6.73;
451 else if (name ==
"T-1")
return 6.88;
452 else if (name ==
"ETHANOL")
return 6.58;
453 else if (name ==
"HYDRAZINE")
return 8.61;
454 else if (name ==
"F-34")
return 6.66;
455 else if (name ==
"F-35")
return 6.74;
456 else if (name ==
"F-40")
return 6.48;
457 else if (name ==
"F-44")
return 6.81;
458 else if (name ==
"AVTAG")
return 6.48;
459 else if (name ==
"AVCAT")
return 6.81;
462 log <<
"Unknown fuel type specified: "<< name <<
"\n";
470void FGTank::bind(FGPropertyManager* PropertyManager)
472 string property_name, base_property_name;
473 base_property_name = CreateIndexedPropertyName(
"propulsion/tank", TankNumber);
474 property_name = base_property_name +
"/contents-lbs";
476 &FGTank::SetContents );
477 property_name = base_property_name +
"/unusable-volume-gal";
480 property_name = base_property_name +
"/pct-full";
482 property_name = base_property_name +
"/density-lbs_per_gal";
485 property_name = base_property_name +
"/priority";
486 PropertyManager->Tie( property_name.c_str(), (
FGTank*)
this, &FGTank::GetPriority,
487 &FGTank::SetPriority );
488 property_name = base_property_name +
"/external-flow-rate-pps";
489 PropertyManager->Tie( property_name.c_str(), (
FGTank*)
this, &FGTank::GetExternalFlow,
490 &FGTank::SetExternalFlow );
491 property_name = base_property_name +
"/local-ixx-slug_ft2";
492 PropertyManager->Tie( property_name.c_str(), (
FGTank*)
this, &FGTank::GetIxx);
493 property_name = base_property_name +
"/local-iyy-slug_ft2";
494 PropertyManager->Tie( property_name.c_str(), (
FGTank*)
this, &FGTank::GetIyy);
495 property_name = base_property_name +
"/local-izz-slug_ft2";
496 PropertyManager->Tie( property_name.c_str(), (
FGTank*)
this, &FGTank::GetIzz);
498 property_name = base_property_name +
"/x-position";
499 PropertyManager->Tie(property_name.c_str(), (
FGTank*)
this, &FGTank::GetLocationX, &FGTank::SetLocationX);
500 property_name = base_property_name +
"/y-position";
501 PropertyManager->Tie(property_name.c_str(), (
FGTank*)
this, &FGTank::GetLocationY, &FGTank::SetLocationY);
502 property_name = base_property_name +
"/z-position";
503 PropertyManager->Tie(property_name.c_str(), (
FGTank*)
this, &FGTank::GetLocationZ, &FGTank::SetLocationZ);
526void FGTank::Debug(
int from)
528 if (debug_lvl <= 0)
return;
545 FGLogging log(LogLevel::DEBUG);
546 log <<
" " << Name <<
" (" << type <<
") tank holds " << Capacity <<
" lbs. " << type <<
"\n";
547 log <<
" currently at " << PctFull <<
"% of maximum capacity\n";
548 log <<
" Tank location (X, Y, Z): " << vXYZ(eX) <<
", " << vXYZ(eY) <<
", " << vXYZ(eZ) <<
"\n";
549 log <<
" Effective radius: " << Radius <<
" inches\n";
550 log <<
" Initial temperature: " << Temperature <<
" Fahrenheit\n";
551 log <<
" Priority: " << Priority <<
"\n";
554 if (debug_lvl & 2 ) {
555 FGLogging log(LogLevel::DEBUG);
556 if (from == 0) log <<
"Instantiated: FGTank\n";
557 if (from == 1) log <<
"Destroyed: FGTank\n";
559 if (debug_lvl & 4 ) {
561 if (debug_lvl & 8 ) {
563 if (debug_lvl & 16) {
565 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.
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
std::string FindElementValue(const std::string &el="")
Searches for the named element and returns the string data belonging to it.
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.
double FindElementValueAsNumber(const std::string &el="")
Searches for the named element and returns the data belonging to it as a number.
This class implements a 3 element column vector.
Encapsulates the JSBSim simulation executive.
std::shared_ptr< FGPropertyManager > GetPropertyManager(void) const
Returns a pointer to the property manager object.
Represents a mathematical function.
double GetValue(void) const override
Retrieves the value of the function object.
static constexpr double FahrenheitToCelsius(double fahrenheit)
Converts from degrees Fahrenheit to degrees Celsius.
double GetDensity(void) const
Returns the fuel density.
double ProcessFuelName(const std::string &name)
Returns the density of a named fuel type.
double GetContents(void) const
Gets the contents of the tank.
FGTank(FGFDMExec *exec, Element *el, int tank_number)
Constructor.
void ResetToIC(void)
Resets the tank parameters to the initial conditions.
double Drain(double used)
Removes fuel from the tank.
double GetUnusable(void) const
Returns the amount of unusable fuel in the tank.
double Calculate(double dt, double TempC)
Performs local, tanks-specific calculations, such as fuel temperature.
void SetUnusableVolume(double volume)
Sets the volume of unusable fuel in the tank.
double GetPctFull(void) const
Gets the tank fill level.
double GetUnusableVolume(void) const
Returns the unusable volume of fuel in the tank.
Main namespace for the JSBSim Flight Dynamics Model.