modelLoaded = false;
IsSlave = false;
holding = false;
+ Terminate = false;
// Multiple FDM's are stopped for now. We need to ensure that
// the "user" instance always gets the zeroeth instance number,
Constructing = true;
typedef int (FGFDMExec::*iPMF)(void) const;
instance->Tie("simulation/do_trim", this, (iPMF)0, &FGFDMExec::DoTrim);
+ instance->Tie("simulation/terminate", (int *)&Terminate);
Constructing = false;
}
FGFDMExec::~FGFDMExec()
{
instance->Untie("simulation/do_trim");
+ instance->Untie("simulation/terminate");
try {
DeAllocate();
bool FGFDMExec::Run(void)
{
- bool success = false;
+ bool success=false;
FGModel* model_iterator;
model_iterator = FirstModel;
Frame++;
if (!Holding()) State->IncrTime();
+ if (Terminate) success = false;
return (success);
}
string token;
string aircraftCfgFileName;
string separator = "/";
+ Element* element = 0L;
+
+ modelName = model; // Set the class modelName attribute
# ifdef macintosh
separator = ";";
if (addModelToPath) FullAircraftPath += separator + model;
aircraftCfgFileName = FullAircraftPath + separator + model + ".xml";
- FGXMLParse *XMLParse = new FGXMLParse();
- Element* element = 0L;
- Element* document;
-
- ifstream input_file(aircraftCfgFileName.c_str());
-
- if (!input_file.is_open()) { // file open failed
- cerr << "Could not open file " << aircraftCfgFileName.c_str() << endl;
- return false;
- }
-
- readXML(input_file, *XMLParse);
- document = XMLParse->GetDocument();
-
- modelName = model;
-
if (modelLoaded) {
DeAllocate();
Allocate();
}
+ document = LoadXMLDocument(aircraftCfgFileName); // "document" is a class member
ReadPrologue(document);
element = document->GetElement();
int node_idx = 0;
char int_buf[10];
- for (int i=0; i<pcs->node->nChildren(); i++) {
+ for (unsigned int i=0; i<pcs->node->nChildren(); i++) {
pcsNew->base_string = pcs->base_string + "/" + pcs->node->getChild(i)->getName();
node_idx = pcs->node->getChild(i)->getIndex();
sprintf(int_buf, "[%d]", node_idx);
#include <initialization/FGInitialCondition.h>
#include <FGJSBBase.h>
#include <input_output/FGPropertyManager.h>
-#include <input_output/FGXMLParse.h>
#include <input_output/FGGroundCallback.h>
+#include <input_output/FGXMLFileRead.h>
#include <models/FGPropagate.h>
#include <vector>
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
-class FGFDMExec : public FGJSBBase
+class FGFDMExec : public FGJSBBase, public FGXMLFileRead
{
public:
int Error;
unsigned int Frame;
unsigned int IdFDM;
+ unsigned short Terminate;
bool holding;
bool Constructing;
bool modelLoaded;
const double FGJSBBase::lbtoslug = 1.0/slugtolb;
const string FGJSBBase::needed_cfg_version = "2.0";
-const string FGJSBBase::JSBSim_version = "0.9.12 "__DATE__" "__TIME__;
+const string FGJSBBase::JSBSim_version = "Pre-1.0 "__DATE__" "__TIME__;
-std::queue <FGJSBBase::Message*> FGJSBBase::Messages;
+std::queue <FGJSBBase::Message> FGJSBBase::Messages;
FGJSBBase::Message FGJSBBase::localMsg;
unsigned int FGJSBBase::messageId = 0;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-FGJSBBase::Message* FGJSBBase::PutMessage(Message* msg)
+void FGJSBBase::PutMessage(const Message& msg)
{
Messages.push(msg);
- return msg;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-FGJSBBase::Message* FGJSBBase::PutMessage(const string& text)
+void FGJSBBase::PutMessage(const string& text)
{
- Message *msg = new Message();
- msg->text = text;
- msg->messageId = messageId++;
- msg->subsystem = "FDM";
- msg->type = Message::eText;
+ Message msg;
+ msg.text = text;
+ msg.messageId = messageId++;
+ msg.subsystem = "FDM";
+ msg.type = Message::eText;
Messages.push(msg);
- return msg;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-FGJSBBase::Message* FGJSBBase::PutMessage(const string& text, bool bVal)
+void FGJSBBase::PutMessage(const string& text, bool bVal)
{
- Message *msg = new Message();
- msg->text = text;
- msg->messageId = messageId++;
- msg->subsystem = "FDM";
- msg->type = Message::eBool;
- msg->bVal = bVal;
+ Message msg;
+ msg.text = text;
+ msg.messageId = messageId++;
+ msg.subsystem = "FDM";
+ msg.type = Message::eBool;
+ msg.bVal = bVal;
Messages.push(msg);
- return msg;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-FGJSBBase::Message* FGJSBBase::PutMessage(const string& text, int iVal)
+void FGJSBBase::PutMessage(const string& text, int iVal)
{
- Message *msg = new Message();
- msg->text = text;
- msg->messageId = messageId++;
- msg->subsystem = "FDM";
- msg->type = Message::eInteger;
- msg->bVal = (iVal != 0);
+ Message msg;
+ msg.text = text;
+ msg.messageId = messageId++;
+ msg.subsystem = "FDM";
+ msg.type = Message::eInteger;
+ msg.bVal = (iVal != 0);
Messages.push(msg);
- return msg;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-FGJSBBase::Message* FGJSBBase::PutMessage(const string& text, double dVal)
+void FGJSBBase::PutMessage(const string& text, double dVal)
{
- Message *msg = new Message();
- msg->text = text;
- msg->messageId = messageId++;
- msg->subsystem = "FDM";
- msg->type = Message::eDouble;
- msg->bVal = (dVal != 0.0);
+ Message msg;
+ msg.text = text;
+ msg.messageId = messageId++;
+ msg.subsystem = "FDM";
+ msg.type = Message::eDouble;
+ msg.bVal = (dVal != 0.0);
Messages.push(msg);
- return msg;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-FGJSBBase::Message* FGJSBBase::ReadMessage(void)
+int FGJSBBase::SomeMessages(void)
{
- if (!Messages.empty()) return Messages.front();
- else return NULL;
+ return !Messages.empty();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGJSBBase::Message* FGJSBBase::ProcessMessage(void)
{
- if (!Messages.empty())
- localMsg = *(Messages.front());
- else
- return NULL;
+ if (Messages.empty()) return NULL;
+ localMsg = Messages.front();
Messages.pop();
return &localMsg;
}
~FGJSBBase() {};
/// JSBSim Message structure
- typedef struct Msg {
+ class Message {
+ public:
unsigned int fdmId;
unsigned int messageId;
string text;
bool bVal;
int iVal;
double dVal;
- } Message;
+ };
///@name JSBSim console output highlighting terms.
//@{
/** Places a Message structure on the Message queue.
@param msg pointer to a Message structure
@return pointer to a Message structure */
- Message* PutMessage(Message* msg);
+void PutMessage(const Message& msg);
/** Creates a message with the given text and places it on the queue.
@param text message text
@return pointer to a Message structure */
- Message* PutMessage(const string& text);
+ void PutMessage(const string& text);
/** Creates a message with the given text and boolean value and places it on the queue.
@param text message text
@param bVal boolean value associated with the message
@return pointer to a Message structure */
- Message* PutMessage(const string& text, bool bVal);
+void PutMessage(const string& text, bool bVal);
/** Creates a message with the given text and integer value and places it on the queue.
@param text message text
@param iVal integer value associated with the message
@return pointer to a Message structure */
- Message* PutMessage(const string& text, int iVal);
+void PutMessage(const string& text, int iVal);
/** Creates a message with the given text and double value and places it on the queue.
@param text message text
@param dVal double value associated with the message
@return pointer to a Message structure */
- Message* PutMessage(const string& text, double dVal);
+void PutMessage(const string& text, double dVal);
/** Reads the message on the queue (but does not delete it).
- @return pointer to a Message structure (or NULL if no mesage) */
- Message* ReadMessage(void);
+ @return 1 if some messages */
+ int SomeMessages(void);
/** Reads the message on the queue and removes it from the queue.
@return pointer to a Message structure (or NULL if no mesage) */
Message* ProcessMessage(void);
protected:
static Message localMsg;
- static std::queue <Message*> Messages;
+ static std::queue <Message> Messages;
void Debug(int from) {};
# endif
#endif
-#ifdef _WIN32
-//#define snprintf _snprintf
-#endif
-
#include "FGState.h"
namespace JSBSim {
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-void FGState::ReportState(void)
-{
-#if 0
-#if !defined(__BORLANDCPP__)
- char out[80], flap[10], gear[12];
-
- cout << endl << " JSBSim State" << endl;
- snprintf(out,80," Weight: %7.0f lbs. CG: %5.1f, %5.1f, %5.1f inches\n",
- FDMExec->GetMassBalance()->GetWeight(),
- FDMExec->GetMassBalance()->GetXYZcg(1),
- FDMExec->GetMassBalance()->GetXYZcg(2),
- FDMExec->GetMassBalance()->GetXYZcg(3));
- cout << out;
- if ( FCS->GetDfPos() <= 0.01)
- snprintf(flap,10,"Up");
- else
- snprintf(flap,10,"%2.0f",FCS->GetDfPos());
-
- if (FCS->GetGearPos() < 0.01)
- snprintf(gear,12,"Up");
- else if (FCS->GetGearPos() > 0.99)
- snprintf(gear,12,"Down");
- else
- snprintf(gear,12,"In Transit");
-
- snprintf(out,80, " Flaps: %3s Gear: %12s\n",flap,gear);
- cout << out;
- snprintf(out,80, " Speed: %4.0f KCAS Mach: %5.2f\n",
- Auxiliary->GetVcalibratedKTS(),
- Auxiliary->GetMach() );
- cout << out;
- snprintf(out,80, " Altitude: %7.0f ft. AGL Altitude: %7.0f ft.\n",
- Propagate->Geth(),
- Propagate->GetDistanceAGL() );
- cout << out;
- snprintf(out,80, " Angle of Attack: %6.2f deg Pitch Angle: %6.2f deg\n",
- Auxiliary->Getalpha()*radtodeg,
- Propagate->Gettht()*radtodeg );
- cout << out;
- snprintf(out,80, " Flight Path Angle: %6.2f deg Climb Rate: %5.0f ft/min\n",
- Auxiliary->GetGamma()*radtodeg,
- Propagate->Gethdot()*60 );
- cout << out;
- snprintf(out,80, " Normal Load Factor: %4.2f g's Pitch Rate: %5.2f deg/s\n",
- Aircraft->GetNlf(),
- Propagate->GetPQR(2)*radtodeg );
- cout << out;
- snprintf(out,80, " Heading: %3.0f deg true Sideslip: %5.2f deg Yaw Rate: %5.2f deg/s\n",
- Propagate->Getpsi()*radtodeg,
- Auxiliary->Getbeta()*radtodeg,
- Propagate->GetPQR(3)*radtodeg );
- cout << out;
- snprintf(out,80, " Bank Angle: %5.2f deg Roll Rate: %5.2f deg/s\n",
- Propagate->Getphi()*radtodeg,
- Propagate->GetPQR(1)*radtodeg );
- cout << out;
- snprintf(out,80, " Elevator: %5.2f deg Left Aileron: %5.2f deg Rudder: %5.2f deg\n",
- FCS->GetDePos(ofRad)*radtodeg,
- FCS->GetDaLPos(ofRad)*radtodeg,
- FCS->GetDrPos(ofRad)*radtodeg );
- cout << out;
- snprintf(out,80, " Throttle: %5.2f%c\n",
- FCS->GetThrottlePos(0)*100,'%' );
- cout << out;
-
- snprintf(out,80, " Wind Components: %5.2f kts head wind, %5.2f kts cross wind\n",
- Auxiliary->GetHeadWind()*fpstokts,
- Auxiliary->GetCrossWind()*fpstokts );
- cout << out;
-
- snprintf(out,80, " Ground Speed: %4.0f knots , Ground Track: %3.0f deg true\n",
- Auxiliary->GetVground()*fpstokts,
- Auxiliary->GetGroundTrack()*radtodeg );
- cout << out;
-#endif
-#endif
-}
-
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
void FGState::bind(void)
{
PropertyManager->Tie("sim-time-sec", this, &FGState::Getsim_time);
}
FGJSBBase::Message* msg;
- while (fdmex->ReadMessage()) {
+ while (fdmex->SomeMessages()) {
msg = fdmex->ProcessMessage();
switch (msg->type) {
case FGJSBBase::Message::eText:
} else {
trimmed->setBoolValue(true);
}
+#if 0
if (FGJSBBase::debug_lvl > 0)
- State->ReportState();
+ State->ReportState(); /* FIXME: Function not implemented */
+#endif
delete fgtrim;
bool FGInitialCondition::Load(string rstfile, bool useStoredPath)
{
string resetDef;
- ifstream initialization_file;
- FGXMLParse initialization_file_parser;
- Element *document, *el;
int n;
string sep = "/";
resetDef = rstfile;
}
- initialization_file.open(resetDef.c_str());
- if ( !initialization_file.is_open()) {
- cerr << "Could not open initialization file: " << resetDef << endl;
- return false;
- }
+ document = LoadXMLDocument(resetDef);
- readXML(initialization_file, initialization_file_parser);
- document = initialization_file_parser.GetDocument(); // document holds the
- // initialization description
if (document->GetName() != string("initialize")) {
cerr << "File: " << resetDef << " is not a reset file" << endl;
exit(-1);
if (document->FindElement("vground"))
SetVgroundKtsIC(document->FindElementValueAsNumberConvertTo("vground", "FT/SEC"));
if (document->FindElement("running")) {
- n = document->FindElementValueAsNumber("running");
+ n = int(document->FindElementValueAsNumber("running"));
if (n != 0) {
FGPropulsion* propulsion = fdmex->GetPropulsion();
- for(int i=0; i<propulsion->GetNumEngines(); i++) {
+ for(unsigned int i=0; i<propulsion->GetNumEngines(); i++) {
propulsion->GetEngine(i)->SetRunning(true);
}
}
#include <FGFDMExec.h>
#include <FGJSBBase.h>
#include <math/FGColumnVector3.h>
+#include <input_output/FGXMLFileRead.h>
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
-class FGInitialCondition : public FGJSBBase
+class FGInitialCondition : public FGJSBBase, public FGXMLFileRead
{
public:
/// Constructor
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-string FGPropertyManager::GetName( void ) {
+string FGPropertyManager::GetName( void )
+{
return string( getName() );
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+string FGPropertyManager::GetPrintableName( void )
+{
+ string temp_string(getName());
+ size_t initial_location=0;
+ size_t found_location;
+
+ found_location = temp_string.rfind("/");
+ if (found_location != string::npos)
+ temp_string = temp_string.substr(found_location);
+
+ found_location = temp_string.find('_',initial_location);
+ while (found_location != string::npos) {
+ temp_string.replace(found_location,1," ");
+ initial_location = found_location+1;
+ found_location = temp_string.find('_',initial_location);
+ }
+ return temp_string;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
string FGPropertyManager::GetFullyQualifiedName(void) {
vector<string> stack;
stack.push_back( getDisplayName(true) );
*/
string GetName( void );
+ /**
+ * Get the name of a node without underscores, etc.
+ */
+ string GetPrintableName( void );
+
/**
* Get the fully qualified name of a node
* This function is very slow, so is probably useful for debugging only.
FGScript::~FGScript()
{
- int i;
+ unsigned int i;
for (i=0; i<local_properties.size(); i++)
PropertyManager->Untie(local_properties[i]->title);
{
string aircraft="", initialize="", comparison = "", prop_name="";
string notifyPropertyName="";
- Element *document=0, *element=0, *run_element=0, *event_element=0;
+ Element *element=0, *run_element=0, *event_element=0;
Element *condition_element=0, *set_element=0, *delay_element=0;
Element *notify_element = 0L, *notify_property_element = 0L;
Element *property_element = 0L;
bool result = false;
double dt = 0.0, value = 0.0;
- FGXMLParse script_file_parser;
struct event *newEvent;
FGCondition *newCondition;
- ifstream script_file(script.c_str());
- if ( !script_file ) return false;
-
- readXML(script_file, script_file_parser);
- document = script_file_parser.GetDocument();
+ document = LoadXMLDocument(script);
if (document->GetName() != string("runscript")) {
cerr << "File: " << script << " is not a script file" << endl;
iEvent->Triggered = true;
} else if (iEvent->Persistent) {
iEvent->Triggered = false; // Reset the trigger for persistent events
+ iEvent->Notified = false; // Also reset the notification flag
}
if ((currentTime >= iEvent->StartTime) && iEvent->Triggered) {
- if (iEvent->Notify && iEvent->PrevTriggered != iEvent->Triggered) {
- cout << endl << " Event " << event_ctr << " (" << iEvent->Name << ")"
- << " executed at time: " << currentTime << endl;
- for (j=0; j<iEvent->NotifyProperties.size();j++) {
- cout << " " << iEvent->NotifyProperties[j]->GetName()
- << " = " << iEvent->NotifyProperties[j]->getDoubleValue() << endl;
- }
- cout << endl;
- }
-
for (i=0; i<iEvent->SetValue.size(); i++) {
if (iEvent->Transiting[i]) {
iEvent->TimeSpan = currentTime - iEvent->StartTime;
iEvent->SetParam[i]->setDoubleValue(newSetValue);
}
}
+
+ // Print notification values after setting them
+ if (iEvent->Notify && !iEvent->Notified) {
+ cout << endl << " Event " << event_ctr << " (" << iEvent->Name << ")"
+ << " executed at time: " << currentTime << endl;
+ for (j=0; j<iEvent->NotifyProperties.size();j++) {
+ cout << " " << iEvent->NotifyProperties[j]->GetName()
+ << " = " << iEvent->NotifyProperties[j]->getDoubleValue() << endl;
+ }
+ cout << endl;
+ iEvent->Notified = true;
+ }
+
}
iEvent++;
#include "FGFDMExec.h"
#include <math/FGCondition.h>
#include <vector>
+#include <input_output/FGXMLFileRead.h>
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
-class FGScript : public FGJSBBase
+class FGScript : public FGJSBBase, public FGXMLFileRead
{
public:
/// Default constructor
bool Triggered;
bool PrevTriggered;
bool Notify;
+ bool Notified;
double Delay;
double StartTime;
double TimeSpan;
PrevTriggered = false;
Persistent = false;
Delay = 0.0;
- Notify = false;
+ Notify = Notified = false;
Name = "";
StartTime = 0.0;
TimeSpan = 0.0;
double Element::FindElementValueAsNumberConvertTo(string el, string target_units)
{
Element* element = FindElement(el);
- double value;
- string supplied_units="";
- if (element) {
- value = element->GetDataAsNumber();
- supplied_units = element->GetAttributeValue("unit");
- if (!supplied_units.empty()) {
- if (convert.find(supplied_units) != convert.end()) {
- if (convert[supplied_units].find(target_units) != convert[supplied_units].end()) {
- value *= convert[supplied_units][target_units];
- } else {
- cerr << endl << "Target unit: \"" << target_units << "\" does not exist (typo?). Add new unit"
- << " conversion in FGXMLElement.cpp." << endl;
- exit(-1);
- }
- } else {
- cerr << endl << "Supplied unit: \"" << supplied_units << "\" does not exist (typo?). Add new unit"
- << " conversion in FGXMLElement.cpp." << endl;
- exit(-1);
- }
- }
- } else {
- cerr << "Attempting to get get non-existent element " << el << endl;
- return 0;
+ if (!element) {
+ cerr << "Attempting to get non-existent element " << el << endl;
+ exit(0);
+ }
+
+ string supplied_units = element->GetAttributeValue("unit");
+
+ if (!supplied_units.empty()) {
+ if (convert.find(supplied_units) == convert.end()) {
+ cerr << endl << "Supplied unit: \"" << supplied_units << "\" does not exist (typo?). Add new unit"
+ << " conversion in FGXMLElement.cpp." << endl;
+ exit(-1);
+ }
+ if (convert[supplied_units].find(target_units) == convert[supplied_units].end()) {
+ cerr << endl << "Supplied unit: \"" << supplied_units << "\" cannot be converted to "
+ << target_units << ". Add new unit conversion in FGXMLElement.cpp or fix typo" << endl;
+ exit(-1);
+ }
+ }
+
+ double value = element->GetDataAsNumber();
+ if (!supplied_units.empty()) {
+ value *= convert[supplied_units][target_units];
}
+
return value;
}
string target_units)
{
Element* element = FindElement(el);
- double value;
+
+ if (!element) {
+ cerr << "Attempting to get non-existent element " << el << endl;
+ exit(0);
+ }
+
+ if (!supplied_units.empty()) {
+ if (convert.find(supplied_units) == convert.end()) {
+ cerr << endl << "Supplied unit: \"" << supplied_units << "\" does not exist (typo?). Add new unit"
+ << " conversion in FGXMLElement.cpp." << endl;
+ exit(-1);
+ }
+ if (convert[supplied_units].find(target_units) == convert[supplied_units].end()) {
+ cerr << endl << "Supplied unit: \"" << supplied_units << "\" cannot be converted to "
+ << target_units << ". Add new unit conversion in FGXMLElement.cpp or fix typo" << endl;
+ exit(-1);
+ }
+ }
- if (element) {
- value = element->GetDataAsNumber();
- if (!supplied_units.empty()) {
- if (convert.find(supplied_units) != convert.end()) {
- if (convert[supplied_units].find(target_units) != convert[supplied_units].end()) {
- value *= convert[supplied_units][target_units];
- } else {
- cerr << endl << "Target unit: \"" << target_units << "\" does not exist (typo?). Add new unit"
- << " conversion in FGXMLElement.cpp." << endl;
- exit(-1);
- }
- } else {
- cerr << endl << "Supplied unit: \"" << supplied_units << "\" does not exist (typo?). Add new unit"
- << " conversion in FGXMLElement.cpp." << endl;
- exit(-1);
- }
- }
- } else {
- cerr << "Attempting to get get non-existent element " << el << endl;
- return 0;
+ double value = element->GetDataAsNumber();
+ if (!supplied_units.empty()) {
+ value *= convert[supplied_units][target_units];
}
+
return value;
}
double value=0.0;
string supplied_units = GetAttributeValue("unit");
+ if (!supplied_units.empty()) {
+ if (convert.find(supplied_units) == convert.end()) {
+ cerr << endl << "Supplied unit: \"" << supplied_units << "\" does not exist (typo?). Add new unit"
+ << " conversion in FGXMLElement.cpp." << endl;
+ exit(-1);
+ }
+ if (convert[supplied_units].find(target_units) == convert[supplied_units].end()) {
+ cerr << endl << "Supplied unit: \"" << supplied_units << "\" cannot be converted to "
+ << target_units << ". Add new unit conversion in FGXMLElement.cpp or fix typo" << endl;
+ exit(-1);
+ }
+ }
+
item = FindElement("x");
if (!item) item = FindElement("roll");
if (item) {
libInputOutput_a_SOURCES = FGGroundCallback.cpp FGPropertyManager.cpp FGScript.cpp FGXMLElement.cpp FGXMLParse.cpp FGfdmSocket.cpp
-noinst_HEADERS = FGGroundCallback.h FGPropertyManager.h FGScript.h FGXMLElement.h FGXMLParse.h FGfdmSocket.h
+noinst_HEADERS = FGGroundCallback.h FGPropertyManager.h FGScript.h FGXMLElement.h FGXMLParse.h FGfdmSocket.h FGXMLFileRead.h
INCLUDES = -I$(top_srcdir)/src/FDM/JSBSim
return *this;
}
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-FGColumnVector3 FGColumnVector3::multElementWise(const FGColumnVector3& V) const
-{
- return FGColumnVector3(Entry(1) * V(1), Entry(2) * V(2), Entry(3) * V(3));
-}
-
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print
is equal to zero it is left untouched. */
FGColumnVector3& Normalize(void);
- // ??? Is this something sensible ??
- FGColumnVector3 multElementWise(const FGColumnVector3& V) const;
-
// little trick here.
struct AssignRef {
AssignRef(FGColumnVector3& r, int i) : Ref(r), idx(i) {}
FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, string prefix)
: PropertyManager(propMan), Prefix(prefix)
{
- int i;
Element* element;
string operation, property_name;
int size = el->GetNumElements();
}
element = el->GetElement();
+ if (!element) {
+ cerr << fgred << highint << endl;
+ cerr << " No element was specified as an argument to the \"" << operation << "\" operation" << endl;
+ cerr << " This can happen when, for instance, a cos operation is specified and a " << endl;
+ cerr << " property name is given explicitly, but is not placed within a" << endl;
+ cerr << " <property></property> element tag pair." << endl;
+ cerr << reset;
+ exit(-2);
+ }
+
while (element) {
operation = element->GetName();
PropertyManager->Untie(tmp);
}
- for (int i=0; i<Parameters.size(); i++) {
+ for (unsigned int i=0; i<Parameters.size(); i++) {
delete Parameters[i];
}
}
double FGFunction::GetValue(void) const
{
- int i;
+ unsigned int i;
if (cached) return cachedValue;
case eTopLevel:
break;
case eProduct:
- for (i=1;i<Parameters.size();i++) temp *= Parameters[i]->GetValue();
+ for (i=1;i<Parameters.size();i++) {
+ temp *= Parameters[i]->GetValue();
+ }
break;
case eDifference:
- for (i=1;i<Parameters.size();i++) temp -= Parameters[i]->GetValue();
+ for (i=1;i<Parameters.size();i++) {
+ temp -= Parameters[i]->GetValue();
+ }
break;
case eSum:
- for (i=1;i<Parameters.size();i++) temp += Parameters[i]->GetValue();
+ for (i=1;i<Parameters.size();i++) {
+ temp += Parameters[i]->GetValue();
+ }
break;
case eQuotient:
temp /= Parameters[1]->GetValue();
Tables = t.Tables;
Data = Allocate();
- for (int r=0; r<=nRows; r++) {
- for (int c=0; c<=nCols; c++) {
+ for (unsigned int r=0; r<=nRows; r++) {
+ for (unsigned int c=0; c<=nCols; c++) {
Data[r][c] = t.Data[r][c];
}
}
FGTable::FGTable(FGPropertyManager* propMan, Element* el) : PropertyManager(propMan)
{
- int i;
+ unsigned int i;
stringstream buf;
string property_string;
double** FGTable::Allocate(void)
{
Data = new double*[nRows+1];
- for (int r=0; r<=nRows; r++) {
+ for (unsigned int r=0; r<=nRows; r++) {
Data[r] = new double[nCols+1];
- for (int c=0; c<=nCols; c++) {
+ for (unsigned int c=0; c<=nCols; c++) {
Data[r][c] = 0.0;
}
}
}
if (nTables > 0) {
- for (int i=0; i<nTables; i++) delete Tables[i];
+ for (unsigned int i=0; i<nTables; i++) delete Tables[i];
Tables.clear();
}
- for (int r=0; r<=nRows; r++) delete[] Data[r];
+ for (unsigned int r=0; r<=nRows; r++) delete[] Data[r];
delete[] Data;
Debug(1);
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-int FGTable::FindNumColumns(string test_line)
+unsigned int FGTable::FindNumColumns(string test_line)
{
// determine number of data columns in table (first column is row lookup - don't count)
- int position=0;
- int nCols=0;
+ size_t position=0;
+ unsigned int nCols=0;
while ((position = test_line.find_first_not_of(" \t", position)) != string::npos) {
nCols++;
position = test_line.find_first_of(" \t", position);
double FGTable::GetValue(double key) const
{
double Factor, Value, Span;
- int r=lastRowIndex;
+ unsigned int r = lastRowIndex;
//if the key is off the end of the table, just return the
//end-of-table value, do not extrapolate
}
// the key is somewhere in the middle, search for the right breakpoint
- // assume the correct breakpoint has not changed since last frame or
+ // The search is particularly efficient if
+ // the correct breakpoint has not changed since last frame or
// has only changed very little
- if ( r > 2 && Data[r-1][0] > key ) {
- while( Data[r-1][0] > key && r > 2) { r--; }
- } else if ( Data[r][0] < key ) {
- while( Data[r][0] <= key && r <= nRows) { r++; }
- }
+ while (r > 2 && Data[r-1][0] > key) { r--; }
+ while (r < nRows && Data[r][0] < key) { r++; }
lastRowIndex=r;
// make sure denominator below does not go to zero.
double FGTable::GetValue(double rowKey, double colKey) const
{
double rFactor, cFactor, col1temp, col2temp, Value;
- int r=lastRowIndex;
- int c=lastColumnIndex;
-
- if ( r > 2 && Data[r-1][0] > rowKey ) {
- while ( Data[r-1][0] > rowKey && r > 2) { r--; }
- } else if ( Data[r][0] < rowKey ) {
- while ( r <= nRows && Data[r][0] <= rowKey ) { r++; }
- if ( r > nRows ) r = nRows;
- }
+ unsigned int r = lastRowIndex;
+ unsigned int c = lastColumnIndex;
- if ( c > 2 && Data[0][c-1] > colKey ) {
- while( Data[0][c-1] > colKey && c > 2) { c--; }
- } else if ( Data[0][c] < colKey ) {
- while( Data[0][c] <= colKey && c <= nCols) { c++; }
- if ( c > nCols ) c = nCols;
- }
+ while(r > 2 && Data[r-1][0] > rowKey) { r--; }
+ while(r < nRows && Data[r] [0] < rowKey) { r++; }
+
+ while(c > 2 && Data[0][c-1] > colKey) { c--; }
+ while(c < nCols && Data[0][c] < colKey) { c++; }
lastRowIndex=r;
lastColumnIndex=c;
double FGTable::GetValue(double rowKey, double colKey, double tableKey) const
{
double Factor, Value, Span;
- int r=lastRowIndex;
+ unsigned int r = lastRowIndex;
//if the key is off the end (or before the beginning) of the table,
// just return the boundary-table value, do not extrapolate
}
// the key is somewhere in the middle, search for the right breakpoint
- // assume the correct breakpoint has not changed since last frame or
+ // The search is particularly efficient if
+ // the correct breakpoint has not changed since last frame or
// has only changed very little
- if ( r > 2 && Data[r-1][1] > tableKey ) {
- while( Data[r-1][1] > tableKey && r > 2) { r--; }
- } else if ( Data[r][1] < tableKey ) {
- while( Data[r][1] <= tableKey && r <= nRows) { r++; }
- }
+ while(r > 2 && Data[r-1][1] > tableKey) { r--; }
+ while(r < nRows && Data[r] [1] < tableKey) { r++; }
lastRowIndex=r;
// make sure denominator below does not go to zero.
int startRow=0;
int startCol=0;
- if (Type == tt1D || Type == tt3D) startRow = 1;
- if (Type == tt3D) startCol = 1;
+// In 1D table, no pseudo-row of column-headers (i.e. keys):
+ if (Type == tt1D) startRow = 1;
- for (int r=startRow; r<=nRows; r++) {
- for (int c=startCol; c<=nCols; c++) {
+ for (unsigned int r=startRow; r<=nRows; r++) {
+ for (unsigned int c=startCol; c<=nCols; c++) {
if (r != 0 || c != 0) {
in_stream >> Data[r][c];
}
break;
}
cout.precision(4);
- for (int r=startRow; r<=nRows; r++) {
+ for (unsigned int r=startRow; r<=nRows; r++) {
cout << " ";
- for (int c=startCol; c<=nCols; c++) {
+ for (unsigned int c=startCol; c<=nCols; c++) {
if (r == 0 && c == 0) {
cout << " ";
} else {
FGPropertyManager *lookupProperty[3];
double** Data;
vector <FGTable*> Tables;
- int FindNumColumns(string);
- int nRows, nCols, nTables, dimension;
+ unsigned int FindNumColumns(string);
+ unsigned int nRows, nCols, nTables, dimension;
int colCounter, rowCounter, tableCounter;
mutable int lastRowIndex, lastColumnIndex, lastTableIndex;
double** Allocate(void);
FGCondition.cpp
noinst_HEADERS = FGColumnVector3.h FGFunction.h FGLocation.h FGMatrix33.h \
- FGParameter.h FGPropertyValue.h FGQuaternion.h FGRealValue.h FGTable.h \
+ FGParameter.h FGPropertyValue.h FGQuaternion.h FGRealValue.h FGTable.h \
FGCondition.h
INCLUDES = -I$(top_srcdir)/src/FDM/JSBSim
void FGAerodynamics::unbind(void)
{
- unsigned i,j;
-
PropertyManager->Untie("forces/fbx-aero-lbs");
PropertyManager->Untie("forces/fby-aero-lbs");
PropertyManager->Untie("forces/fbz-aero-lbs");
PropertyManager->Untie("aero/alpha-wing-rad");
PropertyManager->Untie("aero/stall-hyst-norm");
PropertyManager->Untie("systems/stall-warn-norm");
-
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
lbarh = lbarv = 0.0;
vbarh = vbarv = 0.0;
WingIncidence = 0.0;
+ HoldDown = 0;
bind();
if (FDMExec->Holding()) return false;
vForces.InitMatrix();
- vForces += Aerodynamics->GetForces();
- vForces += Propulsion->GetForces();
- vForces += GroundReactions->GetForces();
+ if (!HoldDown) {
+ vForces += Aerodynamics->GetForces();
+ vForces += Propulsion->GetForces();
+ vForces += GroundReactions->GetForces();
+ }
vMoments.InitMatrix();
- vMoments += Aerodynamics->GetMoments();
- vMoments += Propulsion->GetMoments();
- vMoments += GroundReactions->GetMoments();
+ if (!HoldDown) {
+ vMoments += Aerodynamics->GetMoments();
+ vMoments += Propulsion->GetMoments();
+ vMoments += GroundReactions->GetMoments();
+ }
vBodyAccel = vForces/MassBalance->GetMass();
PropertyManager->Tie("metrics/lv-norm", this, &FGAircraft::Getlbarv);
PropertyManager->Tie("metrics/vbarh-norm", this, &FGAircraft::Getvbarh);
PropertyManager->Tie("metrics/vbarv-norm", this, &FGAircraft::Getvbarv);
+ PropertyManager->Tie("forces/hold-down", this, &FGAircraft::GetHoldDown, &FGAircraft::SetHoldDown);
PropertyManager->Tie("moments/l-total-lbsft", this, eL, (PMF)&FGAircraft::GetMoments);
PropertyManager->Tie("moments/m-total-lbsft", this, eM, (PMF)&FGAircraft::GetMoments);
PropertyManager->Tie("moments/n-total-lbsft", this, eN, (PMF)&FGAircraft::GetMoments);
PropertyManager->Untie("forces/fbx-total-lbs");
PropertyManager->Untie("forces/fby-total-lbs");
PropertyManager->Untie("forces/fbz-total-lbs");
+ PropertyManager->Untie("forces/hold-down");
PropertyManager->Untie("metrics/aero-rp-x-in");
PropertyManager->Untie("metrics/aero-rp-y-in");
PropertyManager->Untie("metrics/aero-rp-z-in");
inline double GetXYZvrp(int idx) const { return vXYZvrp(idx); }
inline double GetXYZep(int idx) const { return vXYZep(idx); }
inline void SetAircraftName(string name) {AircraftName = name;}
+ inline void SetHoldDown(int hd) {HoldDown = hd;}
+ inline int GetHoldDown(void) const {return HoldDown;}
void SetXYZrp(int idx, double value) {vXYZrp(idx) = value;}
double WingArea, WingSpan, cbar, WingIncidence;
double HTailArea, VTailArea, HTailArm, VTailArm;
double lbarh,lbarv,vbarh,vbarv;
+ int HoldDown;
string AircraftName;
void Debug(int from);
bool FGAuxiliary::Run()
{
- double A,B,D, hdot_Vt;
+ double A,B,D;
+
+ if (FGModel::Run()) return true; // return true if error returned from base class
+ if (FDMExec->Holding()) return false;
+
const FGColumnVector3& vPQR = Propagate->GetPQR();
const FGColumnVector3& vUVW = Propagate->GetUVW();
const FGColumnVector3& vUVWdot = Propagate->GetUVWdot();
const FGColumnVector3& vVel = Propagate->GetVel();
- if (FGModel::Run()) return true; // return true if error returned from base class
-
- if (FDMExec->Holding()) return false;
-
p = Atmosphere->GetPressure();
rhosl = Atmosphere->GetDensitySL();
psl = Atmosphere->GetPressureSL();
Vground = sqrt( vVel(eNorth)*vVel(eNorth) + vVel(eEast)*vVel(eEast) );
- if (vVel(eNorth) == 0) psigt = 0;
- else psigt = atan2(vVel(eEast), vVel(eNorth));
-
+ psigt = atan2(vVel(eEast), vVel(eNorth));
if (psigt < 0.0) psigt += 2*M_PI;
-
- if (Vground == 0.0) {
- if (vVel(eDown) == 0.0) gamma = 0.0;
- else if (vVel(eDown) < 0.0) gamma = 90.0*degtorad;
- else gamma = -90.0*degtorad;
- } else {
- gamma = atan2(-vVel(eDown), Vground);
- }
+ gamma = atan2(-vVel(eDown), Vground);
tat = sat*(1 + 0.2*Mach*Mach); // Total Temperature, isentropic flow
tatc = RankineToCelsius(tat);
#include <models/flight_control/FGKinemat.h>
#include <models/flight_control/FGFCSFunction.h>
#include <models/flight_control/FGSensor.h>
+#include <models/flight_control/FGActuator.h>
namespace JSBSim {
// Cycle through the sensor, autopilot, and flight control components
for (i=0; i<sensors.size(); i++) sensors[i]->Run();
- for (i=0; i<APComponents.size(); i++) APComponents[i]->Run();
+ for (i=0; i<APComponents.size(); i++) {
+ APComponents[i]->Run();
+ }
for (i=0; i<FCSComponents.size(); i++) FCSComponents[i]->Run();
return false;
{
string name, file, fname, interface_property_string;
vector <FGFCSComponent*> *Components;
- Element *document, *component_element, *property_element, *sensor_element;
+ Element *component_element, *property_element, *sensor_element;
Element *channel_element;
- ifstream* controls_file = new ifstream();
- FGXMLParse controls_file_parser;
Components=0;
// Determine if the FCS/Autopilot is defined inline in the aircraft configuration
cerr << "FCS/Autopilot does not appear to be defined inline nor in a file" << endl;
return false;
} else {
- controls_file->open(file.c_str());
- readXML(*controls_file, controls_file_parser);
- delete controls_file;
- document = controls_file_parser.GetDocument();
+ document = LoadXMLDocument(file);
}
} else {
document = el;
Components->push_back(new FGFCSFunction(this, component_element));
} else if (component_element->GetName() == string("pid")) {
Components->push_back(new FGPID(this, component_element));
+ } else if (component_element->GetName() == string("actuator")) {
+ Components->push_back(new FGActuator(this, component_element));
} else {
cerr << "Unknown FCS component: " << component_element->GetName() << endl;
}
for (comp = 0; comp < APComponents.size(); comp++)
{
- CompStrings += delimeter;
+ if (firstime) firstime = false;
+ else CompStrings += delimeter;
+
CompStrings += APComponents[comp]->GetName();
}
}
for (comp = 0; comp < APComponents.size(); comp++) {
- sprintf(buffer, "%s%9.6f", delimeter.c_str(), APComponents[comp]->GetOutput());
+ if (firstime) firstime = false;
+ else CompValues += delimeter;
+
+ sprintf(buffer, "%9.6f", APComponents[comp]->GetOutput());
CompValues += string(buffer);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+double FGFCS::GetDt(void)
+{
+ return FDMExec->GetDeltaT()*rate;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
void FGFCS::bind(void)
{
PropertyManager->Tie("fcs/aileron-cmd-norm", this, &FGFCS::GetDaCmd, &FGFCS::SetDaCmd);
#include <models/flight_control/FGFCSComponent.h>
#include <models/FGModel.h>
#include <models/FGLGear.h>
-#include <input_output/FGXMLElement.h>
+#include <input_output/FGXMLFileRead.h>
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
-class FGFCS : public FGModel {
+class FGFCS : public FGModel, public FGXMLFileRead
+{
public:
/** Constructor
void AddThrottle(void);
void AddGear(void);
+ double GetDt(void);
FGPropertyManager* GetPropertyManager(void) { return PropertyManager; }
FGGroundReactions::~FGGroundReactions(void)
{
- for (int i=0; i<lGear.size();i++) lGear[i].unbind();
+ for (unsigned int i=0; i<lGear.size();i++) lGear[i].unbind();
lGear.clear();
unbind();
bool FGGroundReactions::GetWOW(void)
{
bool result = false;
- for (int i=0; i<lGear.size(); i++) {
+ for (unsigned int i=0; i<lGear.size(); i++) {
if (lGear[i].IsBogey() && lGear[i].GetWOW()) {
result = true;
break;
contact_element = el->FindNextElement("contact");
}
- for (int i=0; i<lGear.size();i++) lGear[i].bind();
+ for (unsigned int i=0; i<lGear.size();i++) lGear[i].bind();
return true;
}
<< name << " stroke velocity (ft/sec)" << delimeter
<< name << " compress force (lbs)" << delimeter
<< name << " wheel side force (lbs)" << delimeter
- << name << " wheel velocity vec X (ft/sec)" << delimeter
- << name << " wheel velocity vec Y (ft/sec)" << delimeter
<< name << " wheel roll force (lbs)" << delimeter
<< name << " body X force (lbs)" << delimeter
<< name << " body Y force (lbs)" << delimeter
+ << name << " wheel velocity vec X (ft/sec)" << delimeter
+ << name << " wheel velocity vec Y (ft/sec)" << delimeter
+ << name << " wheel rolling velocity (ft/sec)" << delimeter
+ << name << " wheel side velocity (ft/sec)" << delimeter
<< name << " wheel slip (deg)" << delimeter;
}
}
<< setprecision(5) << gear.GetCompLen() << delimeter
<< setprecision(6) << gear.GetCompVel() << delimeter
<< setprecision(10) << gear.GetCompForce() << delimeter
- << setprecision(6) << gear.GetWheelVel(eX) << delimeter
- << gear.GetWheelVel(eY) << delimeter
<< gear.GetWheelSideForce() << delimeter
<< gear.GetWheelRollForce() << delimeter
<< gear.GetBodyXForce() << delimeter
<< gear.GetBodyYForce() << delimeter
+ << setprecision(6) << gear.GetWheelVel(eX) << delimeter
+ << gear.GetWheelVel(eY) << delimeter
+ << gear.GetWheelRollVel() << delimeter
+ << gear.GetWheelSideVel() << delimeter
<< gear.GetWheelSlipAngle() << delimeter;
}
}
string name="", fname="";
string property;
- port = element->GetAttributeValueAsNumber("port");
+ port = int(element->GetAttributeValueAsNumber("port"));
if (port == 0) {
cerr << endl << "No port assigned in input element" << endl;
} else {
if (el->FindElement("max_steer"))
maxSteerAngle = el->FindElementValueAsNumberConvertTo("max_steer", "DEG");
if (el->FindElement("retractable"))
- isRetractable = (int)el->FindElementValueAsNumber("retractable");
+ isRetractable = ((unsigned int)el->FindElementValueAsNumber("retractable"))>0.0?true:false;
ForceY_Table = 0;
force_table = el->FindElement("table");
// Compute the forces in the wheel ground plane.
- RollingForce = (1.0 - TirePressureNorm) * 30
- + vLocalForce(eZ) * BrakeFCoeff * (RollingWhlVel>=0?1.0:-1.0);
+ RollingForce = ((1.0 - TirePressureNorm) * 30
+ + vLocalForce(eZ) * BrakeFCoeff) * (RollingWhlVel>=0?1.0:-1.0);
+
SideForce = vLocalForce(eZ) * FCoeff;
// Transform these forces back to the local reference frame.
if ((fabs(RollingWhlVel) <= RFRV) && RFRV > 0) vForce(eX) *= fabs(RollingWhlVel)/RFRV;
if ((fabs(SideWhlVel) <= SFRV) && SFRV > 0) vForce(eY) *= fabs(SideWhlVel)/SFRV;
-// End experimental section for attentuating gear jitter
+// End section for attentuating gear jitter
vMoment = vWhlBodyVec * vForce;
SideWhlVel = vWhlVelVec(eY)*CosWheel - vWhlVelVec(eX)*SinWheel;
// Calculate tire slip angle.
- if (fabs(RollingWhlVel) < 0.02 && fabs(SideWhlVel) < 0.02) {
- WheelSlip = -SteerAngle*radtodeg;
- } else {
WheelSlip = atan2(SideWhlVel, fabs(RollingWhlVel))*radtodeg;
- }
-// Filter the wheel slip angle
+ // Filter the wheel slip angle
double SlipOutput, ca, cb, denom;
inline bool GetGearUnitDown(void) { return GearDown; }
inline double GetWheelSideForce(void) { return SideForce; }
inline double GetWheelRollForce(void) { return RollingForce; }
+ inline double GetWheelSideVel(void) { return SideWhlVel; }
+ inline double GetWheelRollVel(void) { return RollingWhlVel; }
inline double GetBodyXForce(void) { return vLocalForce(eX); }
inline double GetBodyYForce(void) { return vLocalForce(eY); }
inline double GetWheelSlipAngle(void) { return WheelSlip; }
EmptyWeight = el->FindElementValueAsNumberConvertTo("emptywt", "LBS");
element = el->FindElement("location");
- while (element) {
- element_name = element->GetAttributeValue("name");
- if (element_name == "CG") vbaseXYZcg = element->FindElementTripletConvertTo("IN");
- element = el->FindNextElement("location");
- }
+ while (element) {
+ element_name = element->GetAttributeValue("name");
+ if (element_name == "CG") vbaseXYZcg = element->FindElementTripletConvertTo("IN");
+ element = el->FindNextElement("location");
+ }
// Find all POINTMASS elements that descend from this METRICS branch of the
// config file.
&FGMassBalance::GetMass);
PropertyManager->Tie("inertia/weight-lbs", this,
&FGMassBalance::GetWeight);
- PropertyManager->Tie("inertia/cg-x-ft", this,1,
+ PropertyManager->Tie("inertia/cg-x-in", this,1,
(PMF)&FGMassBalance::GetXYZcg);
- PropertyManager->Tie("inertia/cg-y-ft", this,2,
+ PropertyManager->Tie("inertia/cg-y-in", this,2,
(PMF)&FGMassBalance::GetXYZcg);
- PropertyManager->Tie("inertia/cg-z-ft", this,3,
+ PropertyManager->Tie("inertia/cg-z-in", this,3,
(PMF)&FGMassBalance::GetXYZcg);
}
{
PropertyManager->Untie("inertia/mass-slugs");
PropertyManager->Untie("inertia/weight-lbs");
- PropertyManager->Untie("inertia/cg-x-ft");
- PropertyManager->Untie("inertia/cg-y-ft");
- PropertyManager->Untie("inertia/cg-z-ft");
+ PropertyManager->Untie("inertia/cg-x-in");
+ PropertyManager->Untie("inertia/cg-y-in");
+ PropertyManager->Untie("inertia/cg-z-in");
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cout << " EmptyWeight: " << EmptyWeight << " lbm" << endl;
cout << " CG (x, y, z): " << vbaseXYZcg << endl;
// ToDo: Need to add point mass outputs here
- for (int i=0; i<PointMasses.size(); i++) {
+ for (unsigned int i=0; i<PointMasses.size(); i++) {
cout << " Point Mass Object: " << PointMasses[i].Weight << " lbs. at "
<< "X, Y, Z (in.): " << PointMasses[i].Location(eX) << " "
<< PointMasses[i].Location(eY) << " "
ostream outstream(buffer);
+ outstream.precision(10);
+
if (dFirstPass) {
outstream << "Time";
if (SubSystems & ssSimulation) {
}
if (OutputProperties.size() > 0) {
for (unsigned int i=0;i<OutputProperties.size();i++) {
- outstream << delimeter << OutputProperties[i]->GetName();
+ outstream << delimeter << OutputProperties[i]->GetPrintableName();
}
}
}
if (SubSystems & ssRates) {
outstream << delimeter;
- outstream << Propagate->GetPQR().Dump(delimeter) << delimeter;
- outstream << Propagate->GetPQRdot().Dump(delimeter);
+ outstream << (radtodeg*Propagate->GetPQR()).Dump(delimeter) << delimeter;
+ outstream << (radtodeg*Propagate->GetPQRdot()).Dump(delimeter);
}
if (SubSystems & ssVelocities) {
outstream << delimeter;
if (SubSystems & ssPropagate) {
outstream << delimeter;
outstream << Propagate->Geth() << delimeter;
- outstream << Propagate->GetEuler().Dump(delimeter) << delimeter;
+ outstream << (radtodeg*Propagate->GetEuler()).Dump(delimeter) << delimeter;
outstream << Auxiliary->Getalpha(inDegrees) << delimeter;
outstream << Auxiliary->Getbeta(inDegrees) << delimeter;
outstream << Propagate->GetLocation().GetLatitudeDeg() << delimeter;
}
if (SubSystems & ssPropagate) {
socket->Append("Altitude");
- socket->Append("Phi");
- socket->Append("Tht");
- socket->Append("Psi");
- socket->Append("Alpha");
- socket->Append("Beta");
- socket->Append("Latitude (Deg)");
- socket->Append("Longitude (Deg)");
+ socket->Append("Phi (deg)");
+ socket->Append("Tht (deg)");
+ socket->Append("Psi (deg)");
+ socket->Append("Alpha (deg)");
+ socket->Append("Beta (deg)");
+ socket->Append("Latitude (deg)");
+ socket->Append("Longitude (deg)");
}
if (SubSystems & ssCoefficients) {
scratch = Aerodynamics->GetCoefficientStrings(",");
}
if (OutputProperties.size() > 0) {
for (unsigned int i=0;i<OutputProperties.size();i++) {
- socket->Append(OutputProperties[i]->GetName());
+ socket->Append(OutputProperties[i]->GetPrintableName());
}
}
socket->Append(FCS->GetDfPos());
}
if (SubSystems & ssRates) {
- socket->Append(Propagate->GetPQR(eP));
- socket->Append(Propagate->GetPQR(eQ));
- socket->Append(Propagate->GetPQR(eR));
- socket->Append(Propagate->GetPQRdot(eP));
- socket->Append(Propagate->GetPQRdot(eQ));
- socket->Append(Propagate->GetPQRdot(eR));
+ socket->Append(radtodeg*Propagate->GetPQR(eP));
+ socket->Append(radtodeg*Propagate->GetPQR(eQ));
+ socket->Append(radtodeg*Propagate->GetPQR(eR));
+ socket->Append(radtodeg*Propagate->GetPQRdot(eP));
+ socket->Append(radtodeg*Propagate->GetPQRdot(eQ));
+ socket->Append(radtodeg*Propagate->GetPQRdot(eR));
}
if (SubSystems & ssVelocities) {
socket->Append(Auxiliary->Getqbar());
}
if (SubSystems & ssPropagate) {
socket->Append(Propagate->Geth());
- socket->Append(Propagate->GetEuler(ePhi));
- socket->Append(Propagate->GetEuler(eTht));
- socket->Append(Propagate->GetEuler(ePsi));
+ socket->Append(radtodeg*Propagate->GetEuler(ePhi));
+ socket->Append(radtodeg*Propagate->GetEuler(eTht));
+ socket->Append(radtodeg*Propagate->GetEuler(ePsi));
socket->Append(Auxiliary->Getalpha(inDegrees));
socket->Append(Auxiliary->Getbeta(inDegrees));
socket->Append(Propagate->GetLocation().GetLatitudeDeg());
int OutRate = 0;
string property;
unsigned int port;
- FGXMLParse output_file_parser;
- Element *document, *property_element;
- ifstream* output_file = new ifstream();
+ Element *property_element;
string separator = "/";
# ifdef macintosh
} else {
output_file_name = FDMExec->GetFullAircraftPath() + separator + fname + ".xml";
}
- output_file->open(output_file_name.c_str());
- if (output_file->is_open()) {
- readXML(*output_file, output_file_parser);
- delete output_file;
- } else {
- delete output_file;
- cerr << "Could not open directives file: " << output_file_name << endl;
- return false;
- }
- document = output_file_parser.GetDocument();
+ document = LoadXMLDocument(output_file_name);
} else {
document = element;
}
#endif
#include <input_output/FGfdmSocket.h>
-#include <input_output/FGXMLElement.h>
+#include <input_output/FGXMLFileRead.h>
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
-class FGOutput : public FGModel
+class FGOutput : public FGModel, public FGXMLFileRead
{
public:
FGOutput(FGFDMExec*);
Name = "FGPropagate";
// vQtrndot.zero();
+ last2_vPQRdot.InitMatrix();
+ last_vPQRdot.InitMatrix();
+ vPQRdot.InitMatrix();
+
+ last2_vUVWdot.InitMatrix();
+ last_vUVWdot.InitMatrix();
+ vUVWdot.InitMatrix();
+
+ last2_vLocationDot.InitMatrix();
+ last_vLocationDot.InitMatrix();
+ vLocationDot.InitMatrix();
+
bind();
Debug(0);
}
bool FGPropulsion::Load(Element* el)
{
string type, engine_filename;
- int Feed;
bool ThrottleAdded = false;
- Element* document;
- FGXMLParse engine_file_parser;
- ifstream* engine_file;
Debug(2);
}
engine_filename = FindEngineFullPathname(engine_filename);
- readXML(engine_filename, engine_file_parser);
- document = engine_file_parser.GetDocument(); // document holds the engine description
+ document = LoadXMLDocument(engine_filename);
document->SetParent(engine_element);
type = document->GetName();
numEngines++;
engine_element = el->FindNextElement("engine");
- engine_file_parser.reset();
+ ResetParser();
}
// Process tank definitions
void FGPropulsion::SetActiveEngine(int engine)
{
- if (engine >= Engines.size() || engine < 0)
+ if (engine >= (int)Engines.size() || engine < 0)
ActiveEngine = -1;
else
ActiveEngine = engine;
#include <models/propulsion/FGEngine.h>
#include <models/propulsion/FGTank.h>
#include <math/FGMatrix33.h>
-#include <input_output/FGXMLElement.h>
+#include <input_output/FGXMLFileRead.h>
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
-class FGPropulsion : public FGModel
+class FGPropulsion : public FGModel, public FGXMLFileRead
{
public:
/// Constructor
--- /dev/null
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ Module: FGActuator.cpp
+ Author: Jon Berndt
+ Date started: 21 February 2006
+
+ ------------- Copyright (C) 2007 Jon S. Berndt (jsb@hal-pc.org) -------------
+
+ This program is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2 of the License, or (at your option) any later
+ version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Further information about the GNU Lesser General Public License can also be found on
+ the world wide web at http://www.gnu.org.
+
+FUNCTIONAL DESCRIPTION
+--------------------------------------------------------------------------------
+
+HISTORY
+--------------------------------------------------------------------------------
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+COMMENTS, REFERENCES, and NOTES
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+INCLUDES
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+#include "FGActuator.h"
+
+namespace JSBSim {
+
+static const char *IdSrc = "$Id$";
+static const char *IdHdr = ID_ACTUATOR;
+
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+CLASS IMPLEMENTATION
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+
+FGActuator::FGActuator(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
+{
+ double denom;
+ dt = fcs->GetDt();
+
+ // inputs are read from the base class constructor
+
+ PreviousOutput = 0.0;
+ PreviousHystOutput = 0.0;
+ PreviousRateLimOutput = 0.0;
+ PreviousLagInput = PreviousLagOutput = 0.0;
+ bias = lag = hysteresis_width = deadband_width = 0.0;
+ rate_limit = 0.0; // no limit
+ fail_zero = fail_hardover = fail_stuck = false;
+ ca = cb = 0.0;
+
+ if ( element->FindElement("deadband_width") ) {
+ deadband_width = element->FindElementValueAsNumber("deadband_width");
+ }
+ if ( element->FindElement("hysteresis_width") ) {
+ hysteresis_width = element->FindElementValueAsNumber("hysteresis_width");
+ }
+ if ( element->FindElement("rate_limit") ) {
+ rate_limit = element->FindElementValueAsNumber("rate_limit");
+ }
+ if ( element->FindElement("bias") ) {
+ bias = element->FindElementValueAsNumber("bias");
+ }
+ if ( element->FindElement("lag") ) {
+ lag = element->FindElementValueAsNumber("lag");
+ denom = 2.00 + dt*lag;
+ ca = dt*lag / denom;
+ cb = (2.00 - dt*lag) / denom;
+ }
+
+ FGFCSComponent::bind();
+ bind();
+
+ Debug(0);
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+FGActuator::~FGActuator()
+{
+ Debug(1);
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+bool FGActuator::Run(void )
+{
+ dt = fcs->GetDt();
+
+ Input = InputNodes[0]->getDoubleValue() * InputSigns[0];
+ Output = Input; // perfect actuator
+
+ if (fail_zero) Input = 0;
+ if (fail_hardover) Input = clipmax*fabs(Input)/Input;
+
+ if (lag != 0.0) Lag(); // models actuator lag
+ if (rate_limit != 0) RateLimit(); // limit the actuator rate
+ if (deadband_width != 0.0) Deadband();
+ if (hysteresis_width != 0.0) Hysteresis();
+ if (bias != 0.0) Bias(); // models a finite bias
+
+ if (fail_stuck) Output = PreviousOutput;
+ PreviousOutput = Output; // previous value needed for "stuck" malfunction
+
+ Clip();
+ if (IsOutput) SetOutput();
+
+ return true;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGActuator::Bias(void)
+{
+ Output += bias;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGActuator::Lag(void)
+{
+ // "Output" on the right side of the "=" is the current frame input
+ // for this Lag filter
+ double input = Output;
+ Output = ca * (input + PreviousLagInput) + PreviousLagOutput * cb;
+ PreviousLagInput = input;
+ PreviousLagOutput = Output;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGActuator::Hysteresis(void)
+{
+ // Note: this function acts cumulatively on the "Output" parameter. So, "Output"
+ // is - for the purposes of this Hysteresis method - really the input to the
+ // method.
+ double input = Output;
+
+ if (input > PreviousHystOutput) {
+ Output = max(PreviousHystOutput, input-0.5*hysteresis_width);
+ } else if (input < PreviousHystOutput) {
+ Output = min(PreviousHystOutput, input+0.5*hysteresis_width);
+ }
+
+ PreviousHystOutput = Output;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGActuator::RateLimit(void)
+{
+ // Note: this function acts cumulatively on the "Output" parameter. So, "Output"
+ // is - for the purposes of this RateLimit method - really the input to the
+ // method.
+ double input = Output;
+ if (dt > 0.0) {
+ double rate = (input - PreviousRateLimOutput)/dt;
+ if (fabs(rate) > rate_limit) {
+ Output = PreviousRateLimOutput + (rate_limit*fabs(rate)/rate)*dt;
+ }
+ }
+ PreviousRateLimOutput = Output;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGActuator::Deadband(void)
+{
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGActuator::bind(void)
+{
+ string tmp = "fcs/" + PropertyManager->mkPropertyName(Name, true);
+ const string tmp_zero = tmp + "/malfunction/fail_zero";
+ const string tmp_hardover = tmp + "/malfunction/fail_hardover";
+ const string tmp_stuck = tmp + "/malfunction/fail_stuck";
+
+ PropertyManager->Tie( tmp_zero, this, &FGActuator::GetFailZero, &FGActuator::SetFailZero);
+ PropertyManager->Tie( tmp_hardover, this, &FGActuator::GetFailHardover, &FGActuator::SetFailHardover);
+ PropertyManager->Tie( tmp_stuck, this, &FGActuator::GetFailStuck, &FGActuator::SetFailStuck);
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// The bitmasked value choices are as follows:
+// unset: In this case (the default) JSBSim would only print
+// out the normally expected messages, essentially echoing
+// the config files as they are read. If the environment
+// variable is not set, debug_lvl is set to 1 internally
+// 0: This requests JSBSim not to output any messages
+// whatsoever.
+// 1: This value explicity requests the normal JSBSim
+// startup messages
+// 2: This value asks for a message to be printed out when
+// a class is instantiated
+// 4: When this value is set, a message is displayed when a
+// FGModel object executes its Run() method
+// 8: When this value is set, various runtime state variables
+// are printed out periodically
+// 16: When set various parameters are sanity checked and
+// a message is printed out when they go out of bounds
+
+void FGActuator::Debug(int from)
+{
+ if (debug_lvl <= 0) return;
+
+ if (debug_lvl & 1) { // Standard console startup message output
+ if (from == 0) { // Constructor
+ if (InputSigns[0] < 0)
+ cout << " INPUT: -" << InputNodes[0]->getName() << endl;
+ else
+ cout << " INPUT: " << InputNodes[0]->getName() << endl;
+
+ if (IsOutput) cout << " OUTPUT: " << OutputNode->getName() << endl;
+ if (bias != 0.0) cout << " Bias: " << bias << endl;
+ if (rate_limit != 0) cout << " Rate limit: " << rate_limit << endl;
+ if (lag != 0) cout << " Actuator lag: " << lag << endl;
+ if (hysteresis_width != 0) cout << " Hysteresis width: " << hysteresis_width << endl;
+ if (deadband_width != 0) cout << " Deadband width: " << deadband_width << endl;
+ }
+ }
+ if (debug_lvl & 2 ) { // Instantiation/Destruction notification
+ if (from == 0) cout << "Instantiated: FGActuator" << endl;
+ if (from == 1) cout << "Destroyed: FGActuator" << endl;
+ }
+ if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
+ }
+ if (debug_lvl & 8 ) { // Runtime state variables
+ }
+ if (debug_lvl & 16) { // Sanity checking
+ }
+ if (debug_lvl & 64) {
+ if (from == 0) { // Constructor
+ cout << IdSrc << endl;
+ cout << IdHdr << endl;
+ }
+ }
+}
+}
--- /dev/null
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ Header: FGActuator.h
+ Author: Jon Berndt
+ Date started: 21 February 2007
+
+ ------------- Copyright (C) 2006 Jon S. Berndt (jsb@hal-pc.org) -------------
+
+ This program is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2 of the License, or (at your option) any later
+ version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Further information about the GNU Lesser General Public License can also be found on
+ the world wide web at http://www.gnu.org.
+
+HISTORY
+--------------------------------------------------------------------------------
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+SENTRY
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+#ifndef FGACTUATOR_H
+#define FGACTUATOR_H
+
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+INCLUDES
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+#include "FGFCSComponent.h"
+#include <input_output/FGXMLElement.h>
+
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+DEFINITIONS
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+#define ID_ACTUATOR "$Id$"
+
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+FORWARD DECLARATIONS
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+namespace JSBSim {
+
+class FGFCS;
+
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+CLASS DOCUMENTATION
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+/** Encapsulates an Actuator component for the flight control system.
+ The actuator can be modeled as a "perfect actuator", with the Output
+ being set directly to the input. The actuator can be made more "real"
+ by specifying any/all of the following additional effects that can be
+ applied to the actuator. In order of application to the input signal,
+ these are:
+
+ -System lag (input lag, really)
+ -Rate limiting
+ -Deadband
+ -Hysteresis (mechanical hysteresis)
+ -Bias (mechanical bias)
+ -Position limiting ("hard stops")
+
+ There are also several malfunctions that can be applied to the actuator
+ by setting a property to true or false (or 1 or 0).
+
+Syntax:
+
+@code
+<actuator name=\94name\94>
+ <input> {[-]property} </input>
+ <lag> number </lag>
+ <rate_limit> number <rate_limit>
+ <bias> number </bias>
+ <deadband_width> number </deadband_width>
+ <hysteresis_width> number </hysteresis_width>
+ [<clipto>
+ <min> {property name | value} </min>
+ <max> {property name | value} </max>
+ </clipto>]
+ [<output> {property} </output>]
+</actuator>
+@endcode
+
+Example:
+
+@code
+<actuator name=\94fcs/gimbal_pitch_position\94>
+ <input> fcs/gimbal_pitch_command </input>
+ <lag> 60 </lag>
+ <rate_limit> 0.085 <rate_limit> <!-- 5 degrees/sec -->
+ <bias> 0.002 </bias>
+ <deadband_width> 0.002 </deadband_width>
+ <hysteresis_width> 0.05 </hysteresis_width>
+ <clipto> <!-- +/- 10 degrees -->
+ <min> -0.17 </min>
+ <max> 0.17 </max>
+ </clipto>
+</actuator>
+@endcode
+
+@author Jon S. Berndt
+@version $Revision$
+*/
+
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+CLASS DECLARATION
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+class FGActuator : public FGFCSComponent
+{
+public:
+ /// Constructor
+ FGActuator(FGFCS* fcs, Element* element);
+ /// Destructor
+ ~FGActuator();
+
+ /** This function processes the input.
+ It calls private functions if needed to perform the hysteresis, lag,
+ limiting, etc. functions. */
+ bool Run (void);
+
+ // these may need to have the bool argument replaced with a double
+ /** This function fails the actuator to zero. The motion to zero
+ will flow through the lag, hysteresis, and rate limiting
+ functions if those are activated. */
+ inline void SetFailZero(bool set) {fail_zero = set;}
+ inline void SetFailHardover(bool set) {fail_hardover = set;}
+ inline void SetFailStuck(bool set) {fail_stuck = set;}
+
+ inline bool GetFailZero(void) const {return fail_zero;}
+ inline bool GetFailHardover(void) const {return fail_hardover;}
+ inline bool GetFailStuck(void) const {return fail_stuck;}
+
+private:
+ double dt;
+ double span;
+ double bias;
+ double rate_limit;
+ double hysteresis_width;
+ double deadband_width;
+ double lag;
+ double ca; // lag filter coefficient "a"
+ double cb; // lag filter coefficient "b"
+ double PreviousOutput;
+ double PreviousHystOutput;
+ double PreviousRateLimOutput;
+ double PreviousLagInput;
+ double PreviousLagOutput;
+ bool fail_zero;
+ bool fail_hardover;
+ bool fail_stuck;
+
+ void Hysteresis(void);
+ void Lag(void);
+ void RateLimit(void);
+ void Deadband(void);
+ void Bias(void);
+
+ void bind(void);
+
+ void Debug(int from);
+};
+}
+#endif
Type = "PID";
} else if (element->GetName() == string("sensor")) {
Type = "SENSOR";
+ } else if (element->GetName() == string("actuator")) {
+ Type = "ACTUATOR";
} else { // illegal component in this channel
Type = "UNKNOWN";
}
{
Element *function_element = element->FindElement("function");
- function = new FGFunction(PropertyManager, function_element);
+ if (function_element)
+ function = new FGFunction(PropertyManager, function_element);
+ else {
+ cerr << "FCS Function should contain a \"function\" element" << endl;
+ exit(-1);
+ }
FGFCSComponent::bind();
Debug(0);
Input = InputNodes[0]->getDoubleValue() * InputSigns[0];
P_out = Kp * (Input - Input_prev);
- I_out = Ki * dt * Input;
D_out = (Kd / dt) * (Input - 2*Input_prev + Input_prev2);
+ // Do not continue to integrate the input to the integrator if a wind-up
+ // condition is sensed - that is, if the property pointed to by the trigger
+ // element is non-zero.
+
if (Trigger != 0) {
double test = Trigger->getDoubleValue();
- if (fabs(test) > 0.000001) {
- I_out = 0.0;
+ if (fabs(test) < 0.000001) {
+ I_out = Ki * dt * Input;
}
+ } else { // no anti-wind-up trigger defined
+ I_out = Ki * dt * Input;
}
Input_prev = Input;
FGSensor::FGSensor(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
{
double denom;
- dt = fcs->GetState()->Getdt();
+ dt = fcs->GetDt();
// inputs are read from the base class constructor
- dt = fcs->GetState()->Getdt();
-
bits = quantized = divisions = 0;
PreviousInput = PreviousOutput = 0.0;
min = max = bias = noise_variance = lag = drift_rate = drift = span = 0.0;
if ( quantization_element->FindElement("max") ) {
max = quantization_element->FindElementValueAsNumber("max");
}
+ quant_property = quantization_element->GetAttributeValue("name");
span = max - min;
granularity = span/divisions;
}
PropertyManager->Tie( tmp_low, this, &FGSensor::GetFailLow, &FGSensor::SetFailLow);
PropertyManager->Tie( tmp_high, this, &FGSensor::GetFailHigh, &FGSensor::SetFailHigh);
PropertyManager->Tie( tmp_stuck, this, &FGSensor::GetFailStuck, &FGSensor::SetFailStuck);
+
+ if (!quant_property.empty()) {
+ if (quant_property.find("/") == string::npos) { // not found
+ string qprop = "fcs/" + PropertyManager->mkPropertyName(quant_property, true);
+ PropertyManager->Tie(qprop, this, &FGSensor::GetQuantized);
+ }
+ }
+
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if (debug_lvl & 1) { // Standard console startup message output
if (from == 0) { // Constructor
-
+ if (InputSigns[0] < 0)
+ cout << " INPUT: -" << InputNodes[0]->getName() << endl;
+ else
+ cout << " INPUT: " << InputNodes[0]->getName() << endl;
+
+ if (IsOutput) cout << " OUTPUT: " << OutputNode->getName() << endl;
+ if (bits != 0) {
+ if (quant_property.empty())
+ cout << " Quantized output" << endl;
+ else
+ cout << " Quantized output (property: " << quant_property << ")" << endl;
+
+ cout << " Bits: " << bits << endl;
+ cout << " Min value: " << min << endl;
+ cout << " Max value: " << max << endl;
+ cout << " (span: " << span << ", granularity: " << granularity << ")" << endl;
+ }
+ if (bias != 0.0) cout << " Bias: " << bias << endl;
+ if (drift_rate != 0) cout << " Sensor drift rate: " << drift_rate << endl;
+ if (lag != 0) cout << " Sensor lag: " << lag << endl;
+ if (noise_variance != 0) {
+ if (NoiseType == eAbsolute) {
+ cout << " Noise variance (absolute): " << noise_variance << endl;
+ } else if (NoiseType == ePercent) {
+ cout << " Noise variance (percent): " << noise_variance << endl;
+ } else {
+ cout << " Noise variance type is invalid" << endl;
+ }
+ }
}
}
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
Syntax:
@code
-<sensor name=\94name\94 rate_group=\94name\94>
+<sensor name=\94name\94>
<input> property </input>
<lag> number </lag>
<noise variation=\94PERCENT|ABSOLUTE\94> number </noise>
Example:
@code
-<sensor name=\94aero/sensor/qbar\94 rate_group=\94HFCS\94>
+<sensor name=\94aero/sensor/qbar\94>
<input> aero/qbar </input>
<lag> 0.5 </lag>
<noise variation=\94PERCENT\94> 2 </noise>
inline double GetFailLow(void) const {if (fail_low) return 1.0; else return 0.0;}
inline double GetFailHigh(void) const {if (fail_high) return 1.0; else return 0.0;}
inline double GetFailStuck(void) const {if (fail_stuck) return 1.0; else return 0.0;}
+ inline int GetQuantized(void) const {return quantized;}
bool Run (void);
bool fail_low;
bool fail_high;
bool fail_stuck;
+ string quant_property;
void Noise(void);
void Bias(void);
else { // error
cerr << "Unrecognized LOGIC token " << logic << " in switch component: " << Name << endl;
}
- for (int i=0; i<test_element->GetNumDataLines(); i++)
+ for (unsigned int i=0; i<test_element->GetNumDataLines(); i++)
current_test->conditions.push_back(FGCondition(test_element->GetDataLine(i), PropertyManager));
condition_element = test_element->GetElement(); // retrieve condition groups
libFlightControl_a_SOURCES = FGPID.cpp FGDeadBand.cpp FGFCSComponent.cpp \
FGFilter.cpp FGGain.cpp FGGradient.cpp FGKinemat.cpp \
- FGSummer.cpp FGSwitch.cpp FGFCSFunction.cpp FGSensor.cpp
+ FGSummer.cpp FGSwitch.cpp FGFCSFunction.cpp FGSensor.cpp \
+ FGActuator.cpp
noinst_HEADERS = FGPID.h FGDeadBand.h FGFCSComponent.h FGFilter.h \
FGGain.h FGGradient.h FGKinemat.h FGSummer.h FGSwitch.h FGFCSFunction.h \
- FGSensor.h
+ FGSensor.h FGActuator.h
INCLUDES = -I$(top_srcdir)/src/FDM/JSBSim
else cerr << "No engine location found for this engine." << endl;
local_element = engine_element->GetParent()->FindElement("orient");
- if (local_element) orientation = local_element->FindElementTripletConvertTo("IN");
+ if (local_element) orientation = local_element->FindElementTripletConvertTo("DEG");
else cerr << "No engine orientation found for this engine." << endl;
SetPlacement(location, orientation);
{
string token, fullpath, localpath;
string thruster_filename, thruster_fullpathname, thrType;
- double xLoc, yLoc, zLoc, Pitch, Yaw;
double P_Factor = 0, Sense = 0.0;
string enginePath = FDMExec->GetEnginePath();
string aircraftPath = FDMExec->GetFullAircraftPath();
- FGXMLParse thruster_file_parser;
- Element *document, *element;
ifstream thruster_file;
FGColumnVector3 location, orientation;
string separator = "/";
return false;
}
- readXML(thruster_fullpathname, thruster_file_parser);
- document = thruster_file_parser.GetDocument(); // document holds the thruster description
+ document = LoadXMLDocument(thruster_fullpathname);
document->SetParent(thruster_element);
thrType = document->GetName();
cout << " X = " << Thruster->GetLocationX() << endl;
cout << " Y = " << Thruster->GetLocationY() << endl;
cout << " Z = " << Thruster->GetLocationZ() << endl;
- cout << " Pitch = " << Thruster->GetAnglesToBody(ePitch) << endl;
- cout << " Yaw = " << Thruster->GetAnglesToBody(eYaw) << endl;
+ cout << " Pitch = " << radtodeg*Thruster->GetAnglesToBody(ePitch) << " degrees" << endl;
+ cout << " Yaw = " << radtodeg*Thruster->GetAnglesToBody(eYaw) << " degrees" << endl;
}
}
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
#include <FGJSBBase.h>
#include "FGThruster.h"
#include <input_output/FGPropertyManager.h>
+#include <input_output/FGXMLFileRead.h>
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
-class FGEngine : public FGJSBBase
+class FGEngine : public FGJSBBase, public FGXMLFileRead
{
public:
FGEngine(FGFDMExec* exec, Element* el, int engine_number);
mT(1,1) = 1; //identity matrix
mT(2,2) = 1;
mT(3,3) = 1;
- vSense.InitMatrix(1);
Debug(0);
}
FGColumnVector3& FGForce::GetBodyForces(void)
{
- vFb = Transform()*(vFn.multElementWise(vSense));
+ vFb = Transform()*vFn;
// Find the distance from this vector's acting location to the cg; this
// needs to be done like this to convert from structural to body coords.
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+void FGForce::UpdateCustomTransformMatrix(void)
+{
+ double cp,sp,cr,sr,cy,sy;
+
+ cp=cos(vOrient(ePitch)); sp=sin(vOrient(ePitch));
+ cr=cos(vOrient(eRoll)); sr=sin(vOrient(eRoll));
+ cy=cos(vOrient(eYaw)); sy=sin(vOrient(eYaw));
+
+ mT(1,1) = cp*cy;
+ mT(1,2) = cp*sy;
+ mT(1,3) = -sp;
+
+ mT(2,1) = sr*sp*cy - cr*sy;
+ mT(2,2) = sr*sp*sy + cr*cy;
+ mT(2,3) = sr*cp;
+
+ mT(3,1) = cr*sp*cy + sr*sy;
+ mT(3,2) = cr*sp*sy - sr*cy;
+ mT(3,3) = cr*cp;
+ mT = mT.Inverse();
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
void FGForce::SetAnglesToBody(double broll, double bpitch, double byaw)
{
if (ttype == tCustom) {
- double cp,sp,cr,sr,cy,sy;
vOrient(ePitch) = bpitch;
vOrient(eRoll) = broll;
vOrient(eYaw) = byaw;
- cp=cos(bpitch); sp=sin(bpitch);
- cr=cos(broll); sr=sin(broll);
- cy=cos(byaw); sy=sin(byaw);
-
- mT(1,1)=cp*cy;
- mT(1,2)=cp*sy;
- mT(1,3)=-1*sp;
-
- mT(2,1)=sr*sp*cy-cr*sy;
- mT(2,2)=sr*sp*sy+cr*cy;
- mT(2,3)=sr*cp;
-
- mT(3,1)=cr*sp*cy+sr*sy;
- mT(3,2)=cr*sp*sy-sr*cy;
- mT(3,3)=cr*cp;
+ UpdateCustomTransformMatrix();
}
}
<p>This method is where the bulk of the work gets done so calling it more than
once for the same set of native forces and moments should probably be avoided.
-Note that the moment calculations are done here as well so they should not be
+Note that the moment calculations are done here as well so they should be
retrieved after calling the GetBodyForces() method:</p>
<p><tt>vM=fgf.GetMoments();</tt> </p>
/// Destructor
~FGForce();
- enum TransformType { tNone, tWindBody, tLocalBody, tCustom } ttype;
-
- inline void SetNativeForces(double Fnx, double Fny, double Fnz) {
- vFn(1)=Fnx;
- vFn(2)=Fny;
- vFn(3)=Fnz;
- }
- inline void SetNativeForces(FGColumnVector3 vv) { vFn = vv; };
-
- inline void SetNativeMoments(double Ln,double Mn, double Nn) {
- vMn(1)=Ln;
- vMn(2)=Mn;
- vMn(3)=Nn;
- }
- inline void SetNativeMoments(FGColumnVector3 vv) { vMn = vv; }
-
- inline FGColumnVector3& GetNativeForces(void) { return vFn; }
- inline FGColumnVector3& GetNativeMoments(void) { return vMn; }
+ enum TransformType { tNone, tWindBody, tLocalBody, tCustom };
FGColumnVector3& GetBodyForces(void);
SetAnglesToBody(vv(eRoll), vv(ePitch), vv(eYaw));
}
- void SetPitch(double pitch) {vOrient(ePitch) = pitch;}
- void SetYaw(double yaw) {vOrient(eYaw) = yaw;}
+ void UpdateCustomTransformMatrix(void);
+ void SetPitch(double pitch) {vOrient(ePitch) = pitch; UpdateCustomTransformMatrix();}
+ void SetYaw(double yaw) {vOrient(eYaw) = yaw; UpdateCustomTransformMatrix();}
double GetPitch(void) const {return vOrient(ePitch);}
double GetYaw(void) const {return vOrient(eYaw);}
- inline void SetSense(double x, double y, double z) { vSense(eX)=x, vSense(eY)=y, vSense(eZ)=z; }
- inline void SetSense(FGColumnVector3 vv) { vSense=vv; }
-
inline FGColumnVector3& GetAnglesToBody(void) {return vOrient;}
inline double GetAnglesToBody(int axis) {return vOrient(axis);}
- inline FGColumnVector3& GetSense(void) { return vSense; }
inline void SetTransformType(TransformType ii) { ttype=ii; }
inline TransformType GetTransformType(void) { return ttype; }
FGColumnVector3 vMn;
FGColumnVector3 vH;
FGColumnVector3 vOrient;
+ TransformType ttype;
+ FGColumnVector3 vXYZn;
+ FGColumnVector3 vActingXYZn;
private:
FGColumnVector3 vFb;
FGColumnVector3 vM;
- FGColumnVector3 vXYZn;
- FGColumnVector3 vActingXYZn;
FGColumnVector3 vDXYZ;
- FGColumnVector3 vSense;
FGMatrix33 mT;
Type = ttNozzle;
Area2 = (Diameter*Diameter/4.0)*M_PI;
AreaT = Area2/ExpR;
-
+
Debug(0);
}
double FGNozzle::Calculate(double CfPc)
{
double pAtm = fdmex->GetAtmosphere()->GetPressure();
- Thrust = max((double)0.0, (CfPc * AreaT + (PE - pAtm)*Area2) * nzlEff);
+ if (CfPc > 0)
+ Thrust = max((double)0.0, (CfPc * AreaT + (PE - pAtm)*Area2) * nzlEff);
+ else
+ Thrust = 0.0;
+
vFn(1) = Thrust * cos(ReverserAngle);
ThrustCoeff = max((double)0.0, CfPc / ((pAtm - PE) * Area2));
{
std::ostringstream buf;
- buf << Name << "_Thrust[" << id << ']';
+ buf << Name << " Thrust (engine " << id << " in lbs)";
return buf.str();
}
}
minMAP = MinManifoldPressure_inHg * 3386.38; // inHg to Pa
maxMAP = MaxManifoldPressure_inHg * 3386.38;
- StarterHP = sqrt(MaxHP) * 0.2;
+ StarterHP = sqrt(MaxHP) * 0.4;
// Set up and sanity-check the turbo/supercharging configuration based on the input values.
if (TakeoffBoost > RatedBoost[0]) bTakeoffBoost = true;
if (RPM < 10) {
HP = StarterHP;
} else if (RPM < 480) {
- HP = StarterHP + ((480 - RPM) / 10.0);
+ HP = StarterHP + ((480 - RPM) / 8.0);
// This is a guess - would be nice to find a proper starter moter torque curve
} else {
HP = StarterHP;
HP = 0.0;
}
}
- //cout << "Power = " << HP << '\n';
+// cout << "Power = " << HP << " RPM = " << RPM << " Running = " << Running << " Cranking = " << Cranking << endl;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
{
std::ostringstream buf;
- buf << Name << "_PwrAvail[" << EngineNumber << "]" << delimeter
- << Name << "_HP[" << EngineNumber << "]" << delimeter
- << Name << "_equiv_ratio[" << EngineNumber << "]" << delimeter
- << Name << "_MAP[" << EngineNumber << "]" << delimeter
+ buf << Name << " Power Available (engine " << EngineNumber << " in HP)" << delimeter
+ << Name << " HP (engine " << EngineNumber << ")" << delimeter
+ << Name << " equivalent ratio (engine " << EngineNumber << ")" << delimeter
+ << Name << " MAP (engine " << EngineNumber << ")" << delimeter
<< Thruster->GetThrusterLabels(EngineNumber, delimeter);
return buf.str();
: FGThruster(exec, prop_element, num)
{
string token;
- int rows, cols;
Element *table_element, *local_element;
string name="";
FGPropertyManager* PropertyManager = exec->GetPropertyManager();
{
std::ostringstream buf;
- buf << Name << "_Torque[" << id << "]" << delimeter
- << Name << "_PFactor_Pitch[" << id << "]" << delimeter
- << Name << "_PFactor_Yaw[" << id << "]" << delimeter
- << Name << "_Thrust[" << id << "]" << delimeter;
+ buf << Name << " Torque (engine " << id << ")" << delimeter
+ << Name << " PFactor Pitch (engine " << id << ")" << delimeter
+ << Name << " PFactor Yaw (engine " << id << ")" << delimeter
+ << Name << " Thrust (engine " << id << " in lbs)" << delimeter;
if (IsVPitch())
- buf << Name << "_Pitch[" << id << "]" << delimeter;
- buf << Name << "_RPM[" << id << "]";
+ buf << Name << " Pitch (engine " << id << ")" << delimeter;
+ buf << Name << " RPM (engine " << id << ")";
return buf.str();
}
FGRocket::FGRocket(FGFDMExec* exec, Element *el, int engine_number)
: FGEngine(exec, el, engine_number)
{
+ Element* thrust_table_element = 0;
+ ThrustTable = 0L;
+ BurnTime = 0.0;
+
+ // Defaults
+ Variance = 0.0;
+ MinThrottle = 0.0;
+ MaxThrottle = 1.0;
+
if (el->FindElement("shr"))
SHR = el->FindElementValueAsNumber("shr");
if (el->FindElement("max_pc"))
if (el->FindElement("variance"))
Variance = el->FindElementValueAsNumber("variance");
+ thrust_table_element = el->FindElement("thrust_table");
+ if (thrust_table_element) {
+ ThrustTable = new FGTable(PropertyManager, thrust_table_element);
+ }
+
Debug(0);
Type = etRocket;
Throttle = FCS->GetThrottlePos(EngineNumber);
+ // If there is a thrust table, it is a function of elapsed burn time. The engine
+ // is started when the throttle is advanced to 1.0. After that, it burns
+ // without regard to throttle setting. The table returns a value between zero
+ // and one, representing the percentage of maximum vacuum thrust being applied.
+
+ if (ThrustTable != 0L) {
+ if (Throttle == 1 || BurnTime > 0.0) {
+ BurnTime += State->Getdt();
+ }
+ Throttle = ThrustTable->GetValue(BurnTime);
+ }
+
if (Throttle < MinThrottle || Starved) {
PctPower = Thrust = 0.0; // desired thrust
Flameout = true;
{
std::ostringstream buf;
- buf << Name << "_ChamberPress[" << EngineNumber << "]" << delimeter
+ buf << Name << " Chamber Pressure (engine " << EngineNumber << " in psf)" << delimeter
<< Thruster->GetThrusterLabels(EngineNumber, delimeter);
return buf.str();
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGEngine.h"
+#include <math/FGTable.h>
#include <input_output/FGXMLElement.h>
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
coefficient is multiplied by the chamber pressure and then passed
to the nozzle Calculate() routine, where the thrust force is
determined.
+
+ One can model the thrust of a solid rocket by providing a normalized thrust table
+ as a function of time. For instance, the space shuttle solid rocket booster
+ normalized thrust value looks roughly like this:
+
+<pre>
+ <thrust_table name="propulsion/thrust_time" type="internal">
+ <tableData>
+ 0.0 0.00
+ 0.2 0.91
+ 8.0 0.97
+ 16.0 0.99
+ 20.0 1.00
+ 21.0 1.00
+ 24.0 0.95
+ 32.0 0.85
+ 40.0 0.78
+ 48.0 0.72
+ 50.0 0.71
+ 52.0 0.71
+ 56.0 0.73
+ 64.0 0.78
+ 72.0 0.82
+ 80.0 0.81
+ 88.0 0.73
+ 96.0 0.69
+ 104.0 0.59
+ 112.0 0.46
+ 120.0 0.09
+ 132.0 0.00
+ </tableData>
+ </thrust_table>
+</pre>
+
+The left column is time, the right column is normalized thrust. Inside the
+FGRocket class code, the maximum thrust is calculated, and multiplied by this
+table. The Rocket class also tracks burn time. All that needs to be done is
+for the rocket engine to be throttle up to 1. At that time, the solid rocket
+fuel begins burning and thrust is provided.
@author Jon S. Berndt
$Id$
double kFactor;
double Variance;
double PC;
+ double BurnTime;
bool Flameout;
+ FGTable* ThrustTable;
void Debug(int from);
};
Type = ttDirect;
SetTransformType(FGForce::tCustom);
- Name = el->GetName();
+ Name = el->GetAttributeValue("name");
GearRatio = 1.0;
ReverserAngle = 0.0;
else cerr << "No thruster location found." << endl;
element = thruster_element->FindElement("orient");
- if (element) orientation = element->FindElementTripletConvertTo("IN");
+ if (element) orientation = element->FindElementTripletConvertTo("RAD");
else cerr << "No thruster orientation found." << endl;
SetLocation(location);
{
std::ostringstream buf;
- buf << Name << "_Thrust[" << id << "]";
+ buf << Name << " Thrust (engine " << id << " in lbs)";
return buf.str();
}
double FGTurboProp::Run(void)
{
- double idlethrust, milthrust, thrust = 0.0, EngPower_HP, eff_coef;
+ double thrust = 0.0, EngPower_HP, eff_coef;
Running = true; Starter = false; EngStarting = false;
//---