for (int i=0; i<N; i++) {
if (node->getChild(i)->nChildren() ) {
checkTied( (FGPropertyManager*)node->getChild(i) );
- } else if ( node->getChild(i)->isTied() ) {
+ }
+ if ( node->getChild(i)->isTied() ) {
name = ((FGPropertyManager*)node->getChild(i))->GetFullyQualifiedName();
node->Untie(name);
}
{
Frame = 0;
- FirstModel = 0;
Error = 0;
GroundCallback = 0;
State = 0;
// class needs valid pointers to the above
// model classes
- // Initialize models so they can communicate with each other
-
- Atmosphere->InitModel();
- FCS->InitModel();
- Propulsion->InitModel();
- MassBalance->InitModel();
- Aerodynamics->InitModel();
- Inertial->InitModel();
- GroundReactions->InitModel();
- ExternalReactions->InitModel();
- BuoyantForces->InitModel();
- Aircraft->InitModel();
- Propagate->InitModel();
- Auxiliary->InitModel();
- Input->InitModel();
-
- IC = new FGInitialCondition(this);
-
// Schedule a model. The second arg (the integer) is the pass number. For
- // instance, the atmosphere model could get executed every fifth pass it is called
- // by the executive. IC and Trim objects are NOT scheduled.
-
+ // instance, the atmosphere model could get executed every fifth pass it is called.
+
Schedule(Input, 1);
- Schedule(Atmosphere, 1);
+ Schedule(Atmosphere, 30);
Schedule(FCS, 1);
Schedule(Propulsion, 1);
Schedule(MassBalance, 1);
Schedule(Propagate, 1);
Schedule(Auxiliary, 1);
+ // Initialize models so they can communicate with each other
+
+ vector <FGModel*>::iterator it;
+ for (it = Models.begin(); it != Models.end(); ++it) (*it)->InitModel();
+
+ IC = new FGInitialCondition(this);
+
modelLoaded = false;
return result;
delete GroundCallback;
- FirstModel = 0L;
Error = 0;
State = 0;
Auxiliary = 0;
Script = 0;
+ Models.clear();
+
modelLoaded = false;
return modelLoaded;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-int FGFDMExec::Schedule(FGModel* model, int rate)
+void FGFDMExec::Schedule(FGModel* model, int rate)
{
- FGModel* model_iterator;
-
- model_iterator = FirstModel;
-
- if (model_iterator == 0L) { // this is the first model
-
- FirstModel = model;
- FirstModel->NextModel = 0L;
- FirstModel->SetRate(rate);
-
- } else { // subsequent model
-
- while (model_iterator->NextModel != 0L) {
- model_iterator = model_iterator->NextModel;
- }
- model_iterator->NextModel = model;
- model_iterator->NextModel->SetRate(rate);
-
- }
-
- return 0;
+ model->SetRate(rate);
+ Models.push_back(model);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGFDMExec::Run(void)
{
bool success=true;
- FGModel* model_iterator;
-
- model_iterator = FirstModel;
- if (model_iterator == 0L) return false;
Debug(2);
ChildFDMList[i]->Run();
}
- // returns true if success
- // false if complete
+ // returns true if success, false if complete
if (Script != 0 && !State->IntegrationSuspended()) success = Script->RunScript();
- while (model_iterator != 0L) {
- model_iterator->Run();
- model_iterator = model_iterator->NextModel;
- }
+ vector <FGModel*>::iterator it;
+ for (it = Models.begin(); it != Models.end(); ++it) (*it)->Run();
Frame++;
if (!Holding()) State->IncrTime();
void FGFDMExec::ResetToInitialConditions(void)
{
- FGModel* model_iterator;
-
- model_iterator = FirstModel;
- if (model_iterator == 0L || Constructing) return;
+ if (Constructing) return;
- while (model_iterator != 0L) {
- model_iterator->InitModel();
- model_iterator = model_iterator->NextModel;
- }
+ vector <FGModel*>::iterator it;
+ for (it = Models.begin(); it != Models.end(); ++it) (*it)->InitModel();
RunIC();
if (Script) Script->ResetEvents();
@param model A pointer to the model being scheduled.
@param rate The rate at which to execute the model as described above.
@return Currently returns 0 always. */
- int Schedule(FGModel* model, int rate);
+ void Schedule(FGModel* model, int rate);
/** This function executes each scheduled model in succession.
@return true if successful, false if sim should be ended */
static FGPropertyManager *master;
- FGModel* FirstModel;
FGGroundCallback* GroundCallback;
FGState* State;
FGAtmosphere* Atmosphere;
vector <string> PropertyCatalog;
vector <FGOutput*> Outputs;
vector <childData*> ChildFDMList;
+ vector <FGModel*> Models;
bool ReadFileHeader(Element*);
bool ReadChild(Element*);
#include <sstream>
#include <iomanip>
#include <cstdlib>
+#include <cmath>
#include "FGFunction.h"
#include "FGTable.h"
#include "FGPropertyValue.h"
string operation, property_name;
cached = false;
cachedValue = -HUGE_VAL;
+ invlog2val = 1.0/log10(2.0);
property_string = "property";
value_string = "value";
quotient_string = "quotient";
pow_string = "pow";
exp_string = "exp";
+ log2_string = "log2";
+ ln_string = "ln";
+ log10_string = "log10";
abs_string = "abs";
sin_string = "sin";
cos_string = "cos";
Type = eQuotient;
} else if (operation == pow_string) {
Type = ePow;
+ } else if (operation == log2_string) {
+ Type = eLog2;
+ } else if (operation == ln_string) {
+ Type = eLn;
+ } else if (operation == log10_string) {
+ Type = eLog10;
} else if (operation == abs_string) {
Type = eAbs;
} else if (operation == sin_string) {
operation == quotient_string ||
operation == pow_string ||
operation == exp_string ||
+ operation == log2_string ||
+ operation == ln_string ||
+ operation == log10_string ||
operation == abs_string ||
operation == sin_string ||
operation == cos_string ||
case eExp:
temp = exp(temp);
break;
+ case eLog2:
+ if (temp > 0.00) temp = log10(temp)*invlog2val;
+ else temp = -HUGE_VAL;
+ break;
+ case eLn:
+ if (temp > 0.00) temp = log(temp);
+ else temp = -HUGE_VAL;
+ break;
+ case eLog10:
+ if (temp > 0.00) temp = log10(temp);
+ else temp = -HUGE_VAL;
+ break;
case eAbs:
temp = fabs(temp);
break;
void FGFunction::bind(void)
{
if ( !Name.empty() ) {
- string tmp = PropertyManager->mkPropertyName(Prefix + Name, false); // Allow upper case
+ string tmp;
+ if (Prefix.empty())
+ tmp = PropertyManager->mkPropertyName(Name, false); // Allow upper case
+ else
+ tmp = PropertyManager->mkPropertyName(Prefix + "/" + Name, false); // Allow upper case
+
PropertyManager->Tie( tmp, this, &FGFunction::GetValue);
}
}
- quotient (takes 2 args)
- pow (takes 2 args)
- exp (takes 2 args)
+- log2 (takes 1 arg)
+- ln (takes 1 arg)
+- log10 (takes 1 arg)
- abs (takes n args)
- sin (takes 1 arg)
- cos (takes 1 arg)
std::vector <FGParameter*> Parameters;
FGPropertyManager* const PropertyManager;
bool cached;
+ double invlog2val;
std::string Prefix;
std::string description_string;
std::string property_string;
std::string quotient_string;
std::string pow_string;
std::string exp_string;
+ std::string log2_string;
+ std::string ln_string;
+ std::string log10_string;
std::string abs_string;
std::string sin_string;
std::string cos_string;
double cachedValue;
enum functionType {eTopLevel=0, eProduct, eDifference, eSum, eQuotient, ePow,
eExp, eAbs, eSin, eCos, eTan, eASin, eACos, eATan, eATan2,
- eMin, eMax, eAvg, eFrac, eInteger, eMod, eRandom} Type;
+ eMin, eMax, eAvg, eFrac, eInteger, eMod, eRandom, eLog2, eLn, eLog10} Type;
std::string Name;
void bind(void);
void Debug(int from);
#include "FGMatrix33.h"
#include "FGColumnVector3.h"
+#include <sstream>
+#include <iomanip>
#include <iostream>
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+string FGMatrix33::Dump(const string& delimiter) const
+{
+ ostringstream buffer;
+ buffer << std::setw(18) << std::setprecision(16) << Entry(1,1) << delimiter;
+ buffer << std::setw(18) << std::setprecision(16) << Entry(1,2) << delimiter;
+ buffer << std::setw(18) << std::setprecision(16) << Entry(1,3) << delimiter;
+ buffer << std::setw(18) << std::setprecision(16) << Entry(2,1) << delimiter;
+ buffer << std::setw(18) << std::setprecision(16) << Entry(2,2) << delimiter;
+ buffer << std::setw(18) << std::setprecision(16) << Entry(2,3) << delimiter;
+ buffer << std::setw(18) << std::setprecision(16) << Entry(3,1) << delimiter;
+ buffer << std::setw(18) << std::setprecision(16) << Entry(3,2) << delimiter;
+ buffer << std::setw(18) << std::setprecision(16) << Entry(3,3);
+ return buffer.str();
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
ostream& operator<<(ostream& os, const FGMatrix33& M)
{
for (unsigned int i=1; i<=M.Rows(); i++) {
*/
~FGMatrix33(void) { Debug(1); }
+ /** Prints the contents of the matrix.
+ @param delimeter the item separator (tab or comma)
+ @return a string with the delimeter-separated contents of the matrix */
+ std::string Dump(const std::string& delimeter) const;
+
/** Read access the entries of the matrix.
@param row Row index.
@param col Column index.
delete[] Coeff;
- for (i=0; i<variables.size(); i++)
- delete variables[i];
-
delete AeroRPShift;
Debug(1);
bool FGAerodynamics::Run(void)
{
- unsigned int axis_ctr, ctr, i;
+ unsigned int axis_ctr, ctr;
double alpha, twovel;
if (FGModel::Run()) return true;
if (FDMExec->Holding()) return false; // if paused don't execute
+ RunPreFunctions();
+
// calculate some oft-used quantities for speed
twovel = 2*Auxiliary->GetVt();
vFw.InitMatrix();
vFnative.InitMatrix();
- // Tell the variable functions to cache their values, so while the aerodynamic
- // functions are being calculated for each axis, these functions do not get
- // calculated each time, but instead use the values that have already
- // been calculated for this frame.
-
- for (i=0; i<variables.size(); i++) variables[i]->cacheValue(true);
-
for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) {
for (ctr=0; ctr < Coeff[axis_ctr].size(); ctr++) {
vFnative(axis_ctr+1) += Coeff[axis_ctr][ctr]->GetValue();
}
}
+ RunPostFunctions();
+
return false;
}
document = element;
}
- FGModel::Load(element); // Perform base class Load
+ FGModel::Load(element); // Perform base class Pre-Load
DetermineAxisSystem(); // Detemine if Lift/Side/Drag, etc. is used.
AeroRPShift = new FGFunction(PropertyManager, function_element);
}
- function_element = document->FindElement("function");
- while (function_element) {
- variables.push_back( new FGFunction(PropertyManager, function_element) );
- function_element = document->FindNextElement("function");
- }
-
axis_element = document->FindElement("axis");
while (axis_element) {
CoeffArray ca;
axis_element = document->FindNextElement("axis");
}
+ FGModel::PostLoad(element); // Perform base class Post-Load
+
return true;
}
bool firstime = true;
unsigned int axis, sd;
- for (sd = 0; sd < variables.size(); sd++) {
- if (firstime) {
- firstime = false;
- } else {
- CoeffStrings += delimeter;
- }
- CoeffStrings += variables[sd]->GetName();
- }
-
for (axis = 0; axis < 6; axis++) {
for (sd = 0; sd < Coeff[axis].size(); sd++) {
if (firstime) {
{
ostringstream buf;
- buf.precision(6);
- for (unsigned int sd = 0; sd < variables.size(); sd++) {
- if (buf.tellp() > 0) buf << delimeter;
- buf << setw(9) << variables[sd]->GetValue();
- }
-
for (unsigned int axis = 0; axis < 6; axis++) {
for (unsigned int sd = 0; sd < Coeff[axis].size(); sd++) {
if (buf.tellp() > 0) buf << delimeter;
if (FGModel::Run()) return true;
if (FDMExec->Holding()) return false;
+ RunPreFunctions();
+
vForces.InitMatrix();
if (!HoldDown) {
vForces += Aerodynamics->GetForces();
vNwcg = Aerodynamics->GetTb2w() * vNcg;
vNwcg(3) = -1*vNwcg(3) + 1;
+ RunPostFunctions();
+
return false;
}
}
}
+ FGModel::PostLoad(el);
+
Debug(2);
return true;
if (FGModel::Run()) return true;
if (FDMExec->Holding()) return false;
+ RunPreFunctions();
+
T_dev = 0.0;
h = Propagate->GetAltitudeASL();
CalculateDerived();
}
+ RunPostFunctions();
+
Debug(2);
return false;
}
if (FGModel::Run()) return true; // return true if error returned from base class
if (FDMExec->Holding()) return false;
+ RunPreFunctions();
+
const FGColumnVector3& vPQR = Propagate->GetPQR();
const FGColumnVector3& vUVW = Propagate->GetUVW();
const FGColumnVector3& vUVWdot = Propagate->GetUVWdot();
CalculateRelativePosition();
+ RunPostFunctions();
+
return false;
}
if (FDMExec->Holding()) return false; // if paused don't execute
if (NoneDefined) return true;
+ RunPreFunctions();
+
vTotalForces.InitMatrix();
vTotalMoments.InitMatrix();
vTotalMoments += Cells[i]->GetMoments();
}
+ RunPostFunctions();
+
return false;
}
gas_cell_element = document->FindNextElement("gas_cell");
}
+ FGModel::PostLoad(element);
+
return true;
}
force_element = el->FindNextElement("force");
}
+ FGModel::PostLoad(el);
+
return true;
}
if (FDMExec->Holding()) return false; // if paused don't execute
if (NoneDefined) return true;
+ RunPreFunctions();
+
vTotalForces.InitMatrix();
vTotalMoments.InitMatrix();
vTotalMoments += Forces[i]->GetMoments();
}
+ RunPostFunctions();
+
return false;
}
if (FGModel::Run()) return true; // fast exit if nothing to do
if (FDMExec->Holding()) return false;
+ RunPreFunctions();
+
for (i=0; i<ThrottlePos.size(); i++) ThrottlePos[i] = ThrottleCmd[i];
for (i=0; i<MixturePos.size(); i++) MixturePos[i] = MixtureCmd[i];
for (i=0; i<PropAdvance.size(); i++) PropAdvance[i] = PropAdvanceCmd[i];
// Execute Flight Control System
for (i=0; i<FCSComponents.size(); i++) FCSComponents[i]->Run();
+ RunPostFunctions();
+
return false;
}
if (FGModel::Run()) return true;
if (FDMExec->Holding()) return false;
+ RunPreFunctions();
+
vForces.InitMatrix();
vMoments.InitMatrix();
vMoments += lGear[i]->GetMoments();
}
+ RunPostFunctions();
+
return false;
}
for (unsigned int i=0; i<lGear.size();i++) lGear[i]->bind();
+ FGModel::PostLoad(el);
+
return true;
}
for (unsigned int i=0;i<lGear.size();i++) {
if (lGear[i]->IsBogey()) {
FGLGear *gear = lGear[i];
- buf << (gear->GetWOW() ? "1, " : "0, ")
+ buf << (gear->GetWOW() ? "1" : "0") << delimeter
<< setprecision(5) << gear->GetCompLen() << delimeter
<< setprecision(6) << gear->GetCompVel() << delimeter
<< setprecision(10) << gear->GetCompForce() << delimeter
if (FGModel::Run()) return true;
if (FDMExec->Holding()) return false;
+ RunPreFunctions();
+
// Gravitation accel
double r = Propagate->GetRadius();
gAccel = GetGAccel(r);
earthPosAngle += State->Getdt()*RotationRate;
+ RunPostFunctions();
+
return false;
}
// return false if no error
// This model DOES execute if "Exec->Holding"
+ RunPreFunctions();
+
data = socket->Receive(); // get socket transmission if present
if (data.size() > 0) {
}
}
+ RunPostFunctions();
+
return false;
}
Mass = lbtoslug*Weight;
+ FGModel::PostLoad(el);
+
Debug(2);
return true;
}
if (FGModel::Run()) return true;
if (FDMExec->Holding()) return false;
+ RunPreFunctions();
+
double ChildFDMWeight = 0.0;
for (int fdm=0; fdm<FDMExec->GetFDMCount(); fdm++) {
if (FDMExec->GetChildFDM(fdm)->mated) ChildFDMWeight += FDMExec->GetChildFDM(fdm)->exec->GetMassBalance()->GetWeight();
k2, k4, k5,
k3, k5, k6 );
+ RunPostFunctions();
+
Debug(0);
return false;
for (unsigned int i=0; i<interface_properties.size(); i++) delete interface_properties[i];
interface_properties.clear();
+ for (unsigned int i=0; i<PreFunctions.size(); i++) delete PreFunctions[i];
+ for (unsigned int i=0; i<PostFunctions.size(); i++) delete PostFunctions[i];
+
if (debug_lvl & 2) cout << "Destroyed: FGModel" << endl;
}
bool FGModel::Load(Element* el)
{
// Interface properties are all stored in the interface properties array.
-
string interface_property_string = "";
Element *property_element = el->FindElement("property");
- if (property_element && debug_lvl > 0) cout << endl << " Declared properties" << endl << endl;
+ if (property_element && debug_lvl > 0) cout << endl << " Declared properties"
+ << endl << endl;
while (property_element) {
interface_property_string = property_element->GetDataLine();
if (PropertyManager->HasNode(interface_property_string)) {
- cerr << " Property " << interface_property_string << " is already defined." << endl;
+ cerr << " Property " << interface_property_string
+ << " is already defined." << endl;
} else {
double value=0.0;
if ( ! property_element->GetAttributeValue("value").empty())
interface_properties.push_back(new double(value));
PropertyManager->Tie(interface_property_string, interface_properties.back());
if (debug_lvl > 0)
- cout << " " << interface_property_string << " (initial value: " << value << ")" << endl;
+ cout << " " << interface_property_string << " (initial value: "
+ << value << ")" << endl << endl;
}
property_element = el->FindNextElement("property");
}
+ // End of interface property loading logic
+
+ // Load model pre-functions, if any
+
+ Element *function = el->FindElement("function");
+ while (function) {
+ if (function->GetAttributeValue("type") == "pre") {
+ PreFunctions.push_back(new FGFunction(PropertyManager, function));
+ } else if (function->GetAttributeValue("type").empty()) { // Assume pre-function
+ string funcname = function->GetAttributeValue("name");
+ PreFunctions.push_back(new FGFunction(PropertyManager, function));
+ }
+ function = el->FindNextElement("function");
+ }
+
return true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+void FGModel::PostLoad(Element* el)
+{
+ // Load model post-functions, if any
+
+ Element *function = el->FindElement("function");
+ while (function) {
+ if (function->GetAttributeValue("type") == "post") {
+ PostFunctions.push_back(new FGFunction(PropertyManager, function));
+ }
+ function = el->FindNextElement("function");
+ }
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Tell the Functions to cache values, so when the function values
+// are being used in the model, the functions do not get
+// calculated each time, but instead use the values that have already
+// been calculated for this frame.
+
+void FGModel::RunPreFunctions(void)
+{
+ vector <FGFunction*>::iterator it;
+ for (it = PreFunctions.begin(); it != PreFunctions.end(); it++)
+ (*it)->GetValue();
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Tell the Functions to cache values, so when the function values
+// are being used in the model, the functions do not get
+// calculated each time, but instead use the values that have already
+// been calculated for this frame.
+
+void FGModel::RunPostFunctions(void)
+{
+ vector <FGFunction*>::iterator it;
+ for (it = PostFunctions.begin(); it != PostFunctions.end(); it++)
+ (*it)->GetValue();
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
bool FGModel::Run()
{
if (debug_lvl & 4) cout << "Entering Run() for model " << Name << endl;
- if (exe_ctr++ >= rate) exe_ctr = 1;
+ if (rate == 1) return false; // Fast exit if nothing to do
+
+ if (exe_ctr >= rate) exe_ctr = 1;
- if (exe_ctr == 1) return false;
+ if (exe_ctr++ == 1) return false;
else return true;
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGJSBBase.h"
+#include "math/FGFunction.h"
#include <string>
#include <vector>
/// Destructor
~FGModel();
- /** Loads this model.
- @param el a pointer to the element
- @return true if model is successfully loaded*/
- virtual bool Load(Element* el);
-
FGModel* NextModel;
std::string Name;
int exe_ctr;
int rate;
+ void RunPreFunctions(void);
+ void RunPostFunctions(void);
+
+ /** Loads this model.
+ @param el a pointer to the element
+ @return true if model is successfully loaded*/
+ virtual bool Load(Element* el);
+
+ void PostLoad(Element* el);
+
virtual void Debug(int from);
FGFDMExec* FDMExec;
FGPropagate* Propagate;
FGAuxiliary* Auxiliary;
FGPropertyManager* PropertyManager;
+ std::vector <FGFunction*> PreFunctions;
+ std::vector <FGFunction*> PostFunctions;
std::vector <double*> interface_properties;
};
if (FGModel::Run()) return true;
if (enabled && !State->IntegrationSuspended()&& !FDMExec->Holding()) {
+ RunPreFunctions();
if (Type == otSocket) {
SocketOutput();
} else if (Type == otFlightGear) {
} else {
// Not a valid type of output
}
+ RunPostFunctions();
}
return false;
}
}
if (SubSystems & ssForces) {
outstream << delimeter;
- outstream << Aerodynamics->GetvFw() << delimeter;
+ outstream << Aerodynamics->GetvFw().Dump(delimeter) << delimeter;
outstream << Aerodynamics->GetLoD() << delimeter;
- outstream << Aerodynamics->GetForces() << delimeter;
- outstream << Propulsion->GetForces() << delimeter;
- outstream << GroundReactions->GetForces() << delimeter;
- outstream << ExternalReactions->GetForces() << delimeter;
- outstream << BuoyantForces->GetForces() << delimeter;
+ outstream << Aerodynamics->GetForces().Dump(delimeter) << delimeter;
+ outstream << Propulsion->GetForces().Dump(delimeter) << delimeter;
+ outstream << GroundReactions->GetForces().Dump(delimeter) << delimeter;
+ outstream << ExternalReactions->GetForces().Dump(delimeter) << delimeter;
+ outstream << BuoyantForces->GetForces().Dump(delimeter) << delimeter;
outstream << Aircraft->GetForces().Dump(delimeter);
}
if (SubSystems & ssMoments) {
outstream << delimeter;
- outstream << Aerodynamics->GetMoments() << delimeter;
- outstream << Propulsion->GetMoments() << delimeter;
- outstream << GroundReactions->GetMoments() << delimeter;
- outstream << ExternalReactions->GetMoments() << delimeter;
- outstream << BuoyantForces->GetMoments() << delimeter;
+ outstream << Aerodynamics->GetMoments().Dump(delimeter) << delimeter;
+ outstream << Propulsion->GetMoments().Dump(delimeter) << delimeter;
+ outstream << GroundReactions->GetMoments().Dump(delimeter) << delimeter;
+ outstream << ExternalReactions->GetMoments().Dump(delimeter) << delimeter;
+ outstream << BuoyantForces->GetMoments().Dump(delimeter) << delimeter;
outstream << Aircraft->GetMoments().Dump(delimeter);
}
if (SubSystems & ssAtmosphere) {
}
if (SubSystems & ssMassProps) {
outstream << delimeter;
- outstream << MassBalance->GetJ() << delimeter;
+ outstream << MassBalance->GetJ().Dump(delimeter) << delimeter;
outstream << MassBalance->GetMass() << delimeter;
- outstream << MassBalance->GetXYZcg();
+ outstream << MassBalance->GetXYZcg().Dump(delimeter);
}
if (SubSystems & ssPropagate) {
outstream.precision(14);
if (FGModel::Run()) return true; // Fast return if we have nothing to do ...
if (FDMExec->Holding()) return false;
+ RunPreFunctions();
+
RecomputeLocalTerrainRadius();
// Calculate current aircraft radius from center of planet
last2_vLocationDot = last_vLocationDot;
last_vLocationDot = vLocationDot;
+ RunPreFunctions();
+
Debug(2);
return false;
}
if (FGModel::Run()) return true;
if (FDMExec->Holding()) return false;
+ RunPreFunctions();
+
double dt = State->Getdt();
vForces.InitMatrix();
if (refuel) DoRefuel( dt * rate );
if (dump) DumpFuel( dt * rate );
+ RunPostFunctions();
+
return false;
}
if (el->FindElement("dump-rate"))
DumpRate = el->FindElementValueAsNumberConvertTo("dump-rate", "LBS/MIN");
+ FGModel::PostLoad(el);
return true;
}
if (FGModel::Run()) return true;
if (FDMExec->Holding()) return false;
+ RunPreFunctions();
+
//do temp, pressure, and density first
if (!useExternal) {
// get sea-level values
CalculateDerived();
+ RunPostFunctions();
+
Debug(2);
return false;
SLFuelFlowMax = 0.0;
MaxThrottle = 1.0;
MinThrottle = 0.0;
+ FuelDensity = 6.0;
unsigned int i;
ResetToIC(); // initialize dynamic terms
if (local_element) {
while (local_element) {
int tankID = (int)local_element->GetDataAsNumber();
- AddFeedTank( tankID , Propulsion->GetTank(tankID)->GetPriority());
+ FGTank* tank = Propulsion->GetTank(tankID);
+ AddFeedTank( tankID , tank->GetPriority());
+ FuelDensity = tank->GetDensity();
local_element = engine_element->GetParent()->FindNextElement("feed");
}
} else {
property_name = base_property_name + "/fuel-flow-rate-pps";
PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelFlowRate);
+ //cout << "Engine[" << EngineNumber << "] using fuel density: " << FuelDensity << endl;
+
Debug(0);
}
@endcode
<pre>
NOTES:
- Engines feed from all tanks equally.
-
+
Not all thruster types can be matched with a given engine type. See the class
documentation for engine and thruster classes.
</pre>
double FuelFlow_gph;
double FuelFlow_pph;
+ double FuelDensity;
FGFDMExec* FDMExec;
FGState* State;
SLOxiFlowMax = 0.0;
BuildupTime = 0.0;
It = 0.0;
+ ThrustVariation = 0.0;
+ TotalIspVariation = 0.0;
// Defaults
MinThrottle = 0.0;
if (el->FindElement("sloxiflowmax"))
SLOxiFlowMax = el->FindElementValueAsNumberConvertTo("sloxiflowmax", "LBS/SEC");
+ // If there is a thrust table element, this is a solid propellant engine.
thrust_table_element = el->FindElement("thrust_table");
if (thrust_table_element) {
ThrustTable = new FGTable(PropertyManager, thrust_table_element);
+ Element* variation_element = el->FindElement("variation");
+ if (variation_element) {
+ if (variation_element->FindElement("thrust")) {
+ ThrustVariation = variation_element->FindElementValueAsNumber("thrust");
+ }
+ if (variation_element->FindElement("total_isp")) {
+ TotalIspVariation = variation_element->FindElementValueAsNumber("total_isp");
+ }
+ }
}
bindmodel();
}
}
- VacThrust = ThrustTable->GetValue(TotalEngineFuelBurned);
+ VacThrust = ThrustTable->GetValue(TotalEngineFuelBurned)
+ * (ThrustVariation + 1)
+ * (TotalIspVariation + 1);
if (BurnTime <= BuildupTime && BuildupTime > 0.0) {
VacThrust *= sin((BurnTime/BuildupTime)*M_PI/2.0);
// VacThrust *= (1-cos((BurnTime/BuildupTime)*M_PI))/2.0; // 1 - cos approach
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+//
+// The FuelFlowRate can be affected by the TotalIspVariation value (settable
+// in a config file or via properties). The TotalIspVariation parameter affects
+// thrust, but the thrust determines fuel flow rate, so it must be adjusted
+// for Total Isp Variation.
double FGRocket::CalcFuelNeed(void)
{
if (ThrustTable != 0L) { // Thrust table given - infers solid fuel
FuelFlowRate = VacThrust/Isp; // This calculates wdot (weight flow rate in lbs/sec)
+ FuelFlowRate /= (1 + TotalIspVariation);
} else {
FuelFlowRate = SLFuelFlowMax*PctPower;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-// This funciton should tie properties to rocket engine specific properties
+// This function should tie properties to rocket engine specific properties
// that are not bound in the base class (FGEngine) code.
//
void FGRocket::bindmodel()
property_name = base_property_name + "/total-impulse";
PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetTotalImpulse);
- property_name = base_property_name + "/oxi-flow-rate-pps";
- PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetOxiFlowRate);
property_name = base_property_name + "/vacuum-thrust_lbs";
PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetVacThrust);
+
+ if (ThrustTable) { // Solid rocket motor
+ property_name = base_property_name + "/thrust-variation_pct";
+ PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetThrustVariation,
+ &FGRocket::SetThrustVariation);
+ property_name = base_property_name + "/total-isp-variation_pct";
+ PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetTotalIspVariation,
+ &FGRocket::SetTotalIspVariation);
+ } else { // Liquid rocket motor
+ property_name = base_property_name + "/oxi-flow-rate-pps";
+ PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetOxiFlowRate);
+ }
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
std::string GetEngineLabels(const std::string& delimiter);
std::string GetEngineValues(const std::string& delimiter);
+ /** Sets the thrust variation for a solid rocket engine.
+ Solid propellant rocket motor thrust characteristics are typically
+ defined at 70 degrees F temperature. At any other temperature,
+ performance will be different. Warmer propellant grain will
+ burn quicker and at higher thrust. Total motor impulse is
+ not changed for change in thrust.
+ @param var the variation in percent. That is, a 2 percent
+ variation would be specified as 0.02. A positive 2% variation
+ in thrust would increase the thrust by 2%, and shorten the burn time. */
+ void SetThrustVariation(double var) {ThrustVariation = var;}
+
+ /** Sets the variation in total motor energy.
+ The total energy present in a solid rocket motor can be modified
+ (such as might happen with manufacturing variations) by setting
+ the total Isp variation.
+ @param var the variation in percent. That is, a 2 percent
+ variation would be specified as 0.02. This variation will
+ affect the total thrust, but not the burn time.*/
+ void SetTotalIspVariation(double var) {TotalIspVariation = var;}
+
+ /** Returns the thrust variation, if any. */
+ double GetThrustVariation(void) const {return ThrustVariation;}
+
+ /** Returns the Total Isp variation, if any. */
+ double GetTotalIspVariation(void) const {return TotalIspVariation;}
+
private:
/** Reduces the fuel in the active tanks by the amount required.
This function should be called from within the
double It;
double MxR; // Mixture Ratio
double BurnTime;
+ double ThrustVariation;
+ double TotalIspVariation;
double VacThrust;
double previousFuelNeedPerTank;
double previousOxiNeedPerTank;
FGTank::FGTank(FGFDMExec* exec, Element* el, int tank_number)
: TankNumber(tank_number), Exec(exec)
{
- string token;
+ string token, strFuelName;
Element* element;
Element* element_Grain;
Area = 1.0;
InitialPriority = Priority = el->FindElementValueAsNumber("priority");
if (el->FindElement("density"))
Density = el->FindElementValueAsNumberConvertTo("density", "LBS/GAL");
+ if (el->FindElement("type"))
+ strFuelName = el->FindElementValue("type");
+
SetPriority( InitialPriority ); // this will also set the Selected flag
if (Temperature != -9999.0) InitialTemperature = Temperature = FahrenheitToCelsius(Temperature);
Area = 40.0 * pow(Capacity/1975, 0.666666667);
+ // A named fuel type will override a previous density value
+ if (!strFuelName.empty()) Density = ProcessFuelName(strFuelName);
+
Debug(0);
}
}
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+double FGTank::ProcessFuelName(std::string const& name)
+{
+ if (name == "AVGAS") return 6.02;
+ else if (name == "JET-A") return 6.74;
+ else if (name == "JET-A1") return 6.74;
+ else if (name == "JET-B") return 6.48;
+ else if (name == "JP-1") return 6.76;
+ else if (name == "JP-2") return 6.38;
+ else if (name == "JP-3") return 6.34;
+ else if (name == "JP-4") return 6.48;
+ else if (name == "JP-5") return 6.81;
+ else if (name == "JP-6") return 6.55;
+ else if (name == "JP-7") return 6.61;
+ else if (name == "JP-8") return 6.66;
+ else if (name == "JP-8+100") return 6.66;
+ //else if (name == "JP-9") return 6.74;
+ //else if (name == "JPTS") return 6.74;
+ else if (name == "RP-1") return 6.73;
+ else if (name == "T-1") return 6.88;
+ else if (name == "ETHANOL") return 6.58;
+ else if (name == "HYDRAZINE")return 8.61;
+ else if (name == "F-34") return 6.66;
+ else if (name == "F-35") return 6.74;
+ else if (name == "F-40") return 6.48;
+ else if (name == "F-44") return 6.81;
+ else if (name == "AVTAG") return 6.48;
+ else if (name == "AVCAT") return 6.81;
+ else {
+ cerr << "Unknown fuel type specified: "<< name << endl;
+ }
+
+ return 6.6;
+}
+
+
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print
<standpipe unit="{LBS | KG"}> {number} </standpipe>
<priority> {integer} </priority>
<density unit="{KG/L | LBS/GAL}"> {number} </density>
+ <type> {string} </type> <!-- will override previous density setting -->
</tank>
@endcode
- \b standpipe - Minimum contents to which tank can dump, defaults to pounds.
- \b priority - Establishes feed sequence of tank. "1" is the highest priority.
- \b density - Density of liquid tank contents.
+- \b type - Named fuel type. One of AVGAS, JET-A, JET-A1, JET-B, JP-1, JP-2, JP-3,
+- \b JP-4, JP-5, JP-6, JP-7, JP-8, JP-8+100, RP-1, T-1, ETHANOL, HYDRAZINE,
+- \b F-34, F-35, F-40, F-44, AVTAG, AVCAT
location:
- \b x - Location of tank on aircraft's x-axis, defaults to inches.
is given, otherwise 32 degrees F is returned. */
double GetTemperature(void) {return CelsiusToFahrenheit(Temperature);}
+ /** Returns the density of a named fuel type.
+ @return the density, in lbs/gal, or 6.6 if name cannot be resolved. */
+ double ProcessFuelName(std::string const& name);
+
double GetIxx(void) {return Ixx;}
double GetIyy(void) {return Iyy;}
double GetIzz(void) {return Izz;}
int GetPriority(void) const {return Priority;}
void SetPriority(int p) { Priority = p; Selected = p>0 ? true:false; }
+ double GetDensity(void) const {return Density;}
+ void SetDensity(double d) { Density = d; }
+
const FGColumnVector3 GetXYZ(void);
const double GetXYZ(int idx);