X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FFDM%2FJSBSim%2FFGFDMExec.cpp;h=a3b08a49ec5deaf6afdf01e392f4cfdcf5ac3f75;hb=de1564d83e32cf077a5367cc838f28271dd79381;hp=12735d24b9db28b4cfb517ffd1bce4b870e0905e;hpb=31e3caa4f6ba19dd05f582870e719fc5dbe0950a;p=flightgear.git diff --git a/src/FDM/JSBSim/FGFDMExec.cpp b/src/FDM/JSBSim/FGFDMExec.cpp index 12735d24b..a3b08a49e 100644 --- a/src/FDM/JSBSim/FGFDMExec.cpp +++ b/src/FDM/JSBSim/FGFDMExec.cpp @@ -5,23 +5,23 @@ Date started: 11/17/98 Purpose: Schedules and runs the model routines. - ------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) ------------- + ------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) ------------- This program is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free Software + 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 General Public License for more + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - You should have received a copy of the GNU General Public License along with + 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 General Public License can also be found on + 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 @@ -41,80 +41,52 @@ COMMENTS, REFERENCES, and NOTES INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#ifdef FGFS -# include -# include STL_IOSTREAM -# include STL_ITERATOR -#else -# if defined(sgi) && !defined(__GNUC__) && (_COMPILER_VERSION < 740) -# include -# else -# include -# endif -# include -#endif - #include "FGFDMExec.h" -#include "FGState.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "models/FGAtmosphere.h" +#include "models/atmosphere/FGMSIS.h" +#include "models/atmosphere/FGMars.h" +#include "models/FGFCS.h" +#include "models/FGPropulsion.h" +#include "models/FGMassBalance.h" +#include "models/FGGroundReactions.h" +#include "models/FGExternalReactions.h" +#include "models/FGBuoyantForces.h" +#include "models/FGAerodynamics.h" +#include "models/FGInertial.h" +#include "models/FGAircraft.h" +#include "models/FGPropagate.h" +#include "models/FGAuxiliary.h" +#include "models/FGInput.h" +#include "models/FGOutput.h" +#include "initialization/FGInitialCondition.h" +//#include "initialization/FGTrimAnalysis.h" // Remove until later +#include "input_output/FGPropertyManager.h" +#include "input_output/FGScript.h" + +#include +#include +#include + +using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id$"; +static const char *IdSrc = "$Id: FGFDMExec.cpp,v 1.91 2011/04/05 20:20:21 andgi Exp $"; static const char *IdHdr = ID_FDMEXEC; -/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -GLOBAL DECLARATIONS -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ - -unsigned int FGFDMExec::FDMctr = 0; -FGPropertyManager* FGFDMExec::master=0; - /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CLASS IMPLEMENTATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -void checkTied ( FGPropertyManager *node ) -{ - int N = node->nChildren(); - string name; - - for (int i=0; igetChild(i)->nChildren() ) { - checkTied( (FGPropertyManager*)node->getChild(i) ); - } else if ( node->getChild(i)->isTied() ) { - name = ((FGPropertyManager*)node->getChild(i))->GetFullyQualifiedName(); - cerr << name << " is tied" << endl; - } - } -} - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // Constructor -FGFDMExec::FGFDMExec(FGPropertyManager* root) : Root(root) +FGFDMExec::FGFDMExec(FGPropertyManager* root, unsigned int* fdmctr) : Root(root), FDMctr(fdmctr) { Frame = 0; - FirstModel = 0; Error = 0; GroundCallback = 0; - State = 0; Atmosphere = 0; FCS = 0; Propulsion = 0; @@ -122,40 +94,56 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root) : Root(root) Aerodynamics = 0; Inertial = 0; GroundReactions = 0; + ExternalReactions = 0; + BuoyantForces = 0; Aircraft = 0; Propagate = 0; Auxiliary = 0; Input = 0; IC = 0; Trim = 0; + Script = 0; + + RootDir = ""; - terminate = false; modelLoaded = false; - IsSlave = false; + IsChild = false; holding = false; + Terminate = false; + StandAlone = false; + sim_time = 0.0; + dT = 1.0/120.0; // a default timestep size. This is needed for when JSBSim is + // run in standalone mode with no initialization file. - // Multiple FDM's are stopped for now. We need to ensure that - // the "user" instance always gets the zeroeth instance number, - // because there may be instruments or scripts tied to properties - // in the jsbsim[0] node. - // ToDo: it could be that when JSBSim is reset and a new FDM is wanted, that - // process might try setting FDMctr = 0. Then the line below would not need - // to be commented out. - IdFDM = FDMctr; - //FDMctr++; + AircraftPath = "aircraft"; + EnginePath = "engine"; + SystemsPath = "systems"; try { char* num = getenv("JSBSIM_DEBUG"); if (num) debug_lvl = atoi(num); // set debug level - } catch (...) { // if error set to 1 + } catch (...) { // if error set to 1 debug_lvl = 1; } - if (Root == 0) master= new FGPropertyManager; - else master = Root; + if (Root == 0) { // Then this is the root FDM + Root = new FGPropertyManager; // Create the property manager + StandAlone = true; + } + + if (FDMctr == 0) { + FDMctr = new unsigned int; // Create and initialize the child FDM counter + (*FDMctr) = 0; + } + + // Store this FDM's ID + IdFDM = (*FDMctr); // The main (parent) JSBSim instance is always the "zeroth" + + // Prepare FDMctr for the next child FDM id + (*FDMctr)++; // instance. "child" instances are loaded last. - instance = master->GetNode("/fdm/jsbsim",IdFDM,true); + instance = Root->GetNode("/fdm/jsbsim",IdFDM,true); Debug(0); // this is to catch errors in binding member functions to the property tree. try { @@ -165,9 +153,19 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root) : Root(root) exit(1); } + trim_status = false; + ta_mode = 99; + Constructing = true; typedef int (FGFDMExec::*iPMF)(void) const; - instance->Tie("simulation/do_trim", this, (iPMF)0, &FGFDMExec::DoTrim); +// instance->Tie("simulation/do_trim_analysis", this, (iPMF)0, &FGFDMExec::DoTrimAnalysis, false); + instance->Tie("simulation/do_simple_trim", this, (iPMF)0, &FGFDMExec::DoTrim, false); + instance->Tie("simulation/reset", this, (iPMF)0, &FGFDMExec::ResetToInitialConditions, false); + instance->Tie("simulation/terminate", (int *)&Terminate); + instance->Tie("simulation/sim-time-sec", this, &FGFDMExec::GetSimTime); + instance->Tie("simulation/jsbsim-debug", this, &FGFDMExec::GetDebugLevel, &FGFDMExec::SetDebugLevel); + instance->Tie("simulation/frame", (int *)&Frame, false); + Constructing = false; } @@ -175,18 +173,31 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root) : Root(root) FGFDMExec::~FGFDMExec() { - instance->Untie("simulation/do_trim"); - try { + Unbind(); DeAllocate(); - checkTied( instance ); - if (Root == 0) delete master; + + if (IdFDM == 0) { // Meaning this is no child FDM + if(Root != 0) { + if(StandAlone) + delete Root; + Root = 0; + } + if(FDMctr != 0) { + delete FDMctr; + FDMctr = 0; + } + } } catch ( string msg ) { cout << "Caught error: " << msg << endl; } - for (unsigned int i=1; iexec; - SlaveFDMList.clear(); + for (unsigned int i=1; iexec; + ChildFDMList.clear(); + + PropertyCatalog.clear(); + + FDMctr--; Debug(1); } @@ -203,61 +214,20 @@ bool FGFDMExec::Allocate(void) MassBalance = new FGMassBalance(this); Aerodynamics = new FGAerodynamics (this); Inertial = new FGInertial(this); + + GroundCallback = new FGGroundCallback(Inertial->GetRefRadius()); + GroundReactions = new FGGroundReactions(this); + ExternalReactions = new FGExternalReactions(this); + BuoyantForces = new FGBuoyantForces(this); Aircraft = new FGAircraft(this); Propagate = new FGPropagate(this); Auxiliary = new FGAuxiliary(this); Input = new FGInput(this); - GroundCallback = new FGGroundCallback(); - State = new FGState(this); // This must be done here, as the FGState - // class needs valid pointers to the above - // model classes - - // Initialize models so they can communicate with each other - - if (!Atmosphere->InitModel()) { - cerr << fgred << "Atmosphere model init failed" << fgdef << endl; - Error+=1;} - if (!FCS->InitModel()) { - cerr << fgred << "FCS model init failed" << fgdef << endl; - Error+=2;} - if (!Propulsion->InitModel()) { - cerr << fgred << "FGPropulsion model init failed" << fgdef << endl; - Error+=4;} - if (!MassBalance->InitModel()) { - cerr << fgred << "FGMassBalance model init failed" << fgdef << endl; - Error+=8;} - if (!Aerodynamics->InitModel()) { - cerr << fgred << "FGAerodynamics model init failed" << fgdef << endl; - Error+=16;} - if (!Inertial->InitModel()) { - cerr << fgred << "FGInertial model init failed" << fgdef << endl; - Error+=32;} - if (!GroundReactions->InitModel()) { - cerr << fgred << "Ground Reactions model init failed" << fgdef << endl; - Error+=64;} - if (!Aircraft->InitModel()) { - cerr << fgred << "Aircraft model init failed" << fgdef << endl; - Error+=128;} - if (!Propagate->InitModel()) { - cerr << fgred << "Propagate model init failed" << fgdef << endl; - Error+=256;} - if (!Auxiliary->InitModel()) { - cerr << fgred << "Auxiliary model init failed" << fgdef << endl; - Error+=512;} - if (!Input->InitModel()) { - cerr << fgred << "Input model init failed" << fgdef << endl; - Error+=1024;} - - if (Error > 0) result = false; - - 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(FCS, 1); @@ -266,10 +236,19 @@ bool FGFDMExec::Allocate(void) Schedule(Aerodynamics, 1); Schedule(Inertial, 1); Schedule(GroundReactions, 1); + Schedule(ExternalReactions, 1); + Schedule(BuoyantForces, 1); Schedule(Aircraft, 1); Schedule(Propagate, 1); Schedule(Auxiliary, 1); + // Initialize models so they can communicate with each other + + vector ::iterator it; + for (it = Models.begin(); it != Models.end(); ++it) (*it)->InitModel(); + + IC = new FGInitialCondition(this); + modelLoaded = false; return result; @@ -287,15 +266,14 @@ bool FGFDMExec::DeAllocate(void) delete Aerodynamics; delete Inertial; delete GroundReactions; + delete ExternalReactions; + delete BuoyantForces; delete Aircraft; delete Propagate; delete Auxiliary; - delete State; - - for (int i=0; iSetRate(rate); + Models.push_back(model); +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - model_iterator = FirstModel; +bool FGFDMExec::Run(void) +{ + bool success=true; - if (model_iterator == 0L) { // this is the first model + Debug(2); - FirstModel = model; - FirstModel->NextModel = 0L; - FirstModel->SetRate(rate); + for (unsigned int i=1; iAssignState(Propagate); // Transfer state to the child FDM + ChildFDMList[i]->Run(); + } - } else { // subsequent model + // returns true if success, false if complete + if (Script != 0 && !IntegrationSuspended()) success = Script->RunScript(); - while (model_iterator->NextModel != 0L) { - model_iterator = model_iterator->NextModel; - } - model_iterator->NextModel = model; - model_iterator->NextModel->SetRate(rate); + vector ::iterator it; + for (it = Models.begin(); it != Models.end(); ++it) (*it)->Run(); - } + Frame++; + if (!Holding()) IncrTime(); + if (Terminate) success = false; - return 0; + return (success); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// This call will cause the sim time to reset to 0.0 -bool FGFDMExec::Run(void) +bool FGFDMExec::RunIC(void) { - FGModel* model_iterator; + SuspendIntegration(); // saves the integration rate, dt, then sets it to 0.0. + Initialize(IC); + Run(); + ResumeIntegration(); // Restores the integration rate to what it was. - model_iterator = FirstModel; - if (model_iterator == 0L) return false; + return true; +} - Debug(2); +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - for (unsigned int i=1; iexec->State->Initialize(); // Transfer state to the slave FDM -// SlaveFDMList[i]->exec->Run(); - } +void FGFDMExec::Initialize(FGInitialCondition *FGIC) +{ + Setsim_time(0.0); - while (model_iterator != 0L) { - model_iterator->Run(); - model_iterator = model_iterator->NextModel; - } + Propagate->SetInitialState( FGIC ); - frame = Frame++; - if (!Holding()) State->IncrTime(); - return true; + Atmosphere->Run(); + Atmosphere->SetWindNED( FGIC->GetWindNFpsIC(), + FGIC->GetWindEFpsIC(), + FGIC->GetWindDFpsIC() ); + + FGColumnVector3 vAeroUVW; + + //ToDo: move this to the Auxiliary class !? + + vAeroUVW = Propagate->GetUVW() + Propagate->GetTl2b()*Atmosphere->GetTotalWindNED(); + + double alpha, beta; + if (vAeroUVW(eW) != 0.0) + alpha = vAeroUVW(eU)*vAeroUVW(eU) > 0.0 ? atan2(vAeroUVW(eW), vAeroUVW(eU)) : 0.0; + else + alpha = 0.0; + if (vAeroUVW(eV) != 0.0) + beta = vAeroUVW(eU)*vAeroUVW(eU)+vAeroUVW(eW)*vAeroUVW(eW) > 0.0 ? atan2(vAeroUVW(eV), (fabs(vAeroUVW(eU))/vAeroUVW(eU))*sqrt(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW))) : 0.0; + else + beta = 0.0; + + Auxiliary->SetAB(alpha, beta); + + double Vt = vAeroUVW.Magnitude(); + Auxiliary->SetVt(Vt); + + Auxiliary->SetMach(Vt/Atmosphere->GetSoundSpeed()); + + double qbar = 0.5*Vt*Vt*Atmosphere->GetDensity(); + Auxiliary->Setqbar(qbar); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -// This call will cause the sim time to reset to 0.0 +// +// A private, internal function call for Tie-ing to a property, so it needs an +// argument. -bool FGFDMExec::RunIC(void) +void FGFDMExec::ResetToInitialConditions(int mode) { - State->SuspendIntegration(); - State->Initialize(IC); - Run(); - State->ResumeIntegration(); + if (mode == 1) { + for (unsigned int i=0; iSetStartNewFile(true); + } + } + + ResetToInitialConditions(); +} - return true; +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +void FGFDMExec::ResetToInitialConditions(void) +{ + if (Constructing) return; + + vector ::iterator it; + for (it = Models.begin(); it != Models.end(); ++it) (*it)->InitModel(); + + RunIC(); + if (Script) Script->ResetEvents(); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void FGFDMExec::SetGroundCallback(FGGroundCallback* p) { - if (GroundCallback) delete GroundCallback; - + delete GroundCallback; GroundCallback = p; } @@ -406,8 +436,8 @@ vector FGFDMExec::EnumerateFDMs(void) FDMList.push_back(Aircraft->GetAircraftName()); - for (unsigned int i=1; iexec->GetAircraft()->GetAircraftName()); + for (unsigned int i=1; iexec->GetAircraft()->GetAircraftName()); } return FDMList; @@ -415,104 +445,259 @@ vector FGFDMExec::EnumerateFDMs(void) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::LoadModel(string AircraftPath, string EnginePath, string model, - bool addModelToPath) +bool FGFDMExec::LoadScript(const string& script, double deltaT) { - FGFDMExec::AircraftPath = AircraftPath; - FGFDMExec::EnginePath = EnginePath; + bool result; + + Script = new FGScript(this); + result = Script->LoadScript(RootDir + script, deltaT); + + return result; +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +bool FGFDMExec::LoadModel(const string& AircraftPath, const string& EnginePath, const string& SystemsPath, + const string& model, bool addModelToPath) +{ + FGFDMExec::AircraftPath = RootDir + AircraftPath; + FGFDMExec::EnginePath = RootDir + EnginePath; + FGFDMExec::SystemsPath = RootDir + SystemsPath; return LoadModel(model, addModelToPath); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::LoadModel(string model, bool addModelToPath) +bool FGFDMExec::LoadModel(const string& model, bool addModelToPath) { string token; string aircraftCfgFileName; - string separator = "/"; + Element* element = 0L; + bool result = false; // initialize result to false, indicating input file not yet read -# ifdef macintosh - separator = ";"; -# endif + modelName = model; // Set the class modelName attribute - if( AircraftPath.empty() || EnginePath.empty() ) { + if( AircraftPath.empty() || EnginePath.empty() || SystemsPath.empty()) { cerr << "Error: attempted to load aircraft with undefined "; - cerr << "aircraft and engine paths" << endl; + cerr << "aircraft, engine, and system paths" << endl; return false; } - aircraftCfgFileName = AircraftPath; - if (addModelToPath) aircraftCfgFileName += separator + model; - aircraftCfgFileName += separator + model + ".xml"; + FullAircraftPath = AircraftPath; + if (addModelToPath) FullAircraftPath += "/" + model; + aircraftCfgFileName = FullAircraftPath + "/" + model + ".xml"; - FGXMLParse *XMLParse = new FGXMLParse(); - Element* element = 0L; - Element* document; + if (modelLoaded) { + DeAllocate(); + Allocate(); + } - ifstream input_file(aircraftCfgFileName.c_str()); + int saved_debug_lvl = debug_lvl; - if (!input_file.is_open()) { // file open failed - cerr << "Could not open file " << aircraftCfgFileName.c_str() << endl; - return false; - } + document = LoadXMLDocument(aircraftCfgFileName); // "document" is a class member + if (document) { + if (IsChild) debug_lvl = 0; - readXML(input_file, *XMLParse); - document = XMLParse->GetDocument(); + ReadPrologue(document); - modelName = model; + if (IsChild) debug_lvl = saved_debug_lvl; - if (modelLoaded) { - DeAllocate(); - Allocate(); - } + // Process the fileheader element in the aircraft config file. This element is OPTIONAL. + element = document->FindElement("fileheader"); + if (element) { + result = ReadFileHeader(element); + if (!result) { + cerr << endl << "Aircraft fileheader element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } + + if (IsChild) debug_lvl = 0; + + // Process the metrics element. This element is REQUIRED. + element = document->FindElement("metrics"); + if (element) { + result = Aircraft->Load(element); + if (!result) { + cerr << endl << "Aircraft metrics element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } else { + cerr << endl << "No metrics element was found in the aircraft config file." << endl; + return false; + } + + // Process the mass_balance element. This element is REQUIRED. + element = document->FindElement("mass_balance"); + if (element) { + result = MassBalance->Load(element); + if (!result) { + cerr << endl << "Aircraft mass_balance element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } else { + cerr << endl << "No mass_balance element was found in the aircraft config file." << endl; + return false; + } + + // Process the ground_reactions element. This element is REQUIRED. + element = document->FindElement("ground_reactions"); + if (element) { + result = GroundReactions->Load(element); + if (!result) { + cerr << endl << "Aircraft ground_reactions element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } else { + cerr << endl << "No ground_reactions element was found in the aircraft config file." << endl; + return false; + } + + // Process the external_reactions element. This element is OPTIONAL. + element = document->FindElement("external_reactions"); + if (element) { + result = ExternalReactions->Load(element); + if (!result) { + cerr << endl << "Aircraft external_reactions element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } + + // Process the buoyant_forces element. This element is OPTIONAL. + element = document->FindElement("buoyant_forces"); + if (element) { + result = BuoyantForces->Load(element); + if (!result) { + cerr << endl << "Aircraft buoyant_forces element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } + + // Process the propulsion element. This element is OPTIONAL. + element = document->FindElement("propulsion"); + if (element) { + result = Propulsion->Load(element); + if (!result) { + cerr << endl << "Aircraft propulsion element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } + + // Process the system element[s]. This element is OPTIONAL, and there may be more than one. + element = document->FindElement("system"); + while (element) { + result = FCS->Load(element, FGFCS::stSystem); + if (!result) { + cerr << endl << "Aircraft system element has problems in file " << aircraftCfgFileName << endl; + return result; + } + element = document->FindNextElement("system"); + } + + // Process the autopilot element. This element is OPTIONAL. + element = document->FindElement("autopilot"); + if (element) { + result = FCS->Load(element, FGFCS::stAutoPilot); + if (!result) { + cerr << endl << "Aircraft autopilot element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } - ReadPrologue(document); - element = document->GetElement(); - - bool result = true; - while (element && result) { - string element_name = element->GetName(); - if (element_name == "fileheader" ) result = ReadFileHeader(element); - else if (element_name == "slave") result = ReadSlave(element); - else if (element_name == "metrics") result = Aircraft->Load(element); - else if (element_name == "mass_balance") result = MassBalance->Load(element); - else if (element_name == "ground_reactions") result = GroundReactions->Load(element); - else if (element_name == "propulsion") result = Propulsion->Load(element); - else if (element_name == "autopilot") result = FCS->Load(element); - else if (element_name == "flight_control") result = FCS->Load(element); - else if (element_name == "aerodynamics") result = Aerodynamics->Load(element); - else if (element_name == "input") result = Input->Load(element); - else if (element_name == "output") { - FGOutput* Output = new FGOutput(this); - Output->InitModel(); - Schedule(Output, 1); + // Process the flight_control element. This element is OPTIONAL. + element = document->FindElement("flight_control"); + if (element) { + result = FCS->Load(element, FGFCS::stFCS); + if (!result) { + cerr << endl << "Aircraft flight_control element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } + + // Process the aerodynamics element. This element is OPTIONAL, but almost always expected. + element = document->FindElement("aerodynamics"); + if (element) { + result = Aerodynamics->Load(element); + if (!result) { + cerr << endl << "Aircraft aerodynamics element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } else { + cerr << endl << "No expected aerodynamics element was found in the aircraft config file." << endl; + } + + // Process the input element. This element is OPTIONAL. + element = document->FindElement("input"); + if (element) { + result = Input->Load(element); + if (!result) { + cerr << endl << "Aircraft input element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } + + // Process the output element[s]. This element is OPTIONAL, and there may be more than one. + unsigned int idx=0; + typedef double (FGOutput::*iOPMF)(void) const; + typedef int (FGFDMExec::*iOPV)(void) const; + typedef void (FGFDMExec::*vOPI)(int) const; + element = document->FindElement("output"); + while (element) { + if (debug_lvl > 0) cout << endl << " Output data set: " << idx << " "; + FGOutput* Output = new FGOutput(this); + Output->InitModel(); + Schedule(Output, 1); + result = Output->Load(element); + if (!result) { + cerr << endl << "Aircraft output element has problems in file " << aircraftCfgFileName << endl; + return result; + } else { Outputs.push_back(Output); - result = Output->Load(element); + string outputProp = CreateIndexedPropertyName("simulation/output",idx); + instance->Tie(outputProp+"/log_rate_hz", Output, (iOPMF)0, &FGOutput::SetRate, false); + instance->Tie("simulation/force-output", this, (iOPV)0, &FGFDMExec::ForceOutput, false); + idx++; + } + element = document->FindNextElement("output"); } - else { - cerr << "Found unexpected subsystem: " << element_name << ", exiting." << endl; - result = false; - break; + + // Lastly, process the child element. This element is OPTIONAL - and NOT YET SUPPORTED. + element = document->FindElement("child"); + if (element) { + result = ReadChild(element); + if (!result) { + cerr << endl << "Aircraft child element has problems in file " << aircraftCfgFileName << endl; + return result; + } } - element = document->GetNextElement(); - } - if (result) { modelLoaded = true; - Debug(3); + + if (debug_lvl > 0) { + MassBalance->Run(); // Update all mass properties for the report. + MassBalance->GetMassPropertiesReport(); + + cout << endl << fgblue << highint + << "End of vehicle configuration loading." << endl + << "-------------------------------------------------------------------------------" + << reset << endl; + } + + if (IsChild) debug_lvl = saved_debug_lvl; + } else { cerr << fgred - << " JSBSim failed to load aircraft and/or engine model" + << " JSBSim failed to open the configuration file: " << aircraftCfgFileName << fgdef << endl; - return false; } - struct PropertyCatalogStructure masterPCS; - masterPCS.base_string = ""; - masterPCS.node = (FGPropertyManager*)master; - - BuildPropertyCatalog(&masterPCS); + if (result) { + struct PropertyCatalogStructure masterPCS; + masterPCS.base_string = ""; + masterPCS.node = (FGPropertyManager*)Root; + BuildPropertyCatalog(&masterPCS); + } return result; } @@ -523,14 +708,17 @@ void FGFDMExec::BuildPropertyCatalog(struct PropertyCatalogStructure* pcs) { struct PropertyCatalogStructure* pcsNew = new struct PropertyCatalogStructure; int node_idx = 0; - char int_buf[10]; for (int i=0; inode->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); - if (node_idx != 0) pcsNew->base_string += string(int_buf); + if (node_idx != 0) { + pcsNew->base_string = CreateIndexedPropertyName(pcsNew->base_string, node_idx); + } if (pcs->node->getChild(i)->nChildren() == 0) { + if (pcsNew->base_string.substr(0,11) == string("/fdm/jsbsim")) { + pcsNew->base_string = pcsNew->base_string.erase(0,12); + } PropertyCatalog.push_back(pcsNew->base_string); } else { pcsNew->node = (FGPropertyManager*)pcs->node->getChild(i); @@ -542,10 +730,10 @@ void FGFDMExec::BuildPropertyCatalog(struct PropertyCatalogStructure* pcs) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -string FGFDMExec::QueryPropertyCatalog(string in) +string FGFDMExec::QueryPropertyCatalog(const string& in) { string results=""; - for (int i=0; iFindElement("description")) + cout << " Description: " << el->FindElement("description")->GetDataLine() << endl; if (el->FindElement("author")) cout << " Model Author: " << el->FindElement("author")->GetDataLine() << endl; if (el->FindElement("filecreationdate")) cout << " Creation Date: " << el->FindElement("filecreationdate")->GetDataLine() << endl; if (el->FindElement("version")) cout << " Version: " << el->FindElement("version")->GetDataLine() << endl; - if (el->FindElement("description")) - cout << " Description: " << el->FindElement("description")->GetDataLine() << endl; return result; } @@ -634,53 +838,51 @@ bool FGFDMExec::ReadPrologue(Element* el) // el for ReadPrologue is the document //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::ReadSlave(Element* el) +bool FGFDMExec::ReadChild(Element* el) { - // Add a new slaveData object to the slave FDM list - // Populate that slaveData element with a new FDMExec object - // Set the IsSlave flag for that FDMExec object + // Add a new childData object to the child FDM list + // Populate that childData element with a new FDMExec object + // Set the IsChild flag for that FDMExec object // Get the aircraft name - // set debug level to print out no additional data for slave objects + // set debug level to print out no additional data for child objects // Load the model given the aircraft name // reset debug level to prior setting - int saved_debug_lvl = debug_lvl; string token; - SlaveFDMList.push_back(new slaveData); - SlaveFDMList.back()->exec = new FGFDMExec(); - SlaveFDMList.back()->exec->SetSlave(); -/* - string AircraftName = AC_cfg->GetValue("file"); - - debug_lvl = 0; // turn off debug output for slave vehicle - - SlaveFDMList.back()->exec->SetAircraftPath( AircraftPath ); - SlaveFDMList.back()->exec->SetEnginePath( EnginePath ); - SlaveFDMList.back()->exec->LoadModel(AircraftName); - debug_lvl = saved_debug_lvl; // turn debug output back on for master vehicle - - AC_cfg->GetNextConfigLine(); - while ((token = AC_cfg->GetValue()) != string("/SLAVE")) { - *AC_cfg >> token; - if (token == "xloc") { *AC_cfg >> SlaveFDMList.back()->x; } - else if (token == "yloc") { *AC_cfg >> SlaveFDMList.back()->y; } - else if (token == "zloc") { *AC_cfg >> SlaveFDMList.back()->z; } - else if (token == "pitch") { *AC_cfg >> SlaveFDMList.back()->pitch;} - else if (token == "yaw") { *AC_cfg >> SlaveFDMList.back()->yaw; } - else if (token == "roll") { *AC_cfg >> SlaveFDMList.back()->roll; } - else cerr << "Unknown identifier: " << token << " in slave vehicle definition" << endl; + struct childData* child = new childData; + + child->exec = new FGFDMExec(Root, FDMctr); + child->exec->SetChild(true); + + string childAircraft = el->GetAttributeValue("name"); + string sMated = el->GetAttributeValue("mated"); + if (sMated == "false") child->mated = false; // child objects are mated by default. + string sInternal = el->GetAttributeValue("internal"); + if (sInternal == "true") child->internal = true; // child objects are external by default. + + child->exec->SetAircraftPath( AircraftPath ); + child->exec->SetEnginePath( EnginePath ); + child->exec->SetSystemsPath( SystemsPath ); + child->exec->LoadModel(childAircraft); + + Element* location = el->FindElement("location"); + if (location) { + child->Loc = location->FindElementTripletConvertTo("IN"); + } else { + cerr << endl << highint << fgred << " No location was found for this child object!" << reset << endl; + exit(-1); } -*/ - if (debug_lvl > 0) { - cout << " X = " << SlaveFDMList.back()->x << endl; - cout << " Y = " << SlaveFDMList.back()->y << endl; - cout << " Z = " << SlaveFDMList.back()->z << endl; - cout << " Pitch = " << SlaveFDMList.back()->pitch << endl; - cout << " Yaw = " << SlaveFDMList.back()->yaw << endl; - cout << " Roll = " << SlaveFDMList.back()->roll << endl; + + Element* orientation = el->FindElement("orient"); + if (orientation) { + child->Orient = orientation->FindElementTripletConvertTo("RAD"); + } else if (debug_lvl > 0) { + cerr << endl << highint << " No orientation was found for this child object! Assuming 0,0,0." << reset << endl; } + ChildFDMList.push_back(child); + return true; } @@ -704,7 +906,7 @@ FGTrim* FGFDMExec::GetTrim(void) void FGFDMExec::DisableOutput(void) { - for (int i=0; iDisable(); } } @@ -713,32 +915,91 @@ void FGFDMExec::DisableOutput(void) void FGFDMExec::EnableOutput(void) { - for (int i=0; iEnable(); } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +void FGFDMExec::ForceOutput(int idx) +{ + if (idx >= 0 && idx < Outputs.size()) Outputs[idx]->Print(); +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +bool FGFDMExec::SetOutputDirectives(const string& fname) +{ + bool result; + + FGOutput* Output = new FGOutput(this); + Output->SetDirectivesFile(RootDir + fname); + Output->InitModel(); + Schedule(Output, 1); + result = Output->Load(0); + + if (result) { + Outputs.push_back(Output); + typedef double (FGOutput::*iOPMF)(void) const; + string outputProp = CreateIndexedPropertyName("simulation/output",Outputs.size()-1); + instance->Tie(outputProp+"/log_rate_hz", Output, (iOPMF)0, &FGOutput::SetRate, false); + } + + return result; +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + void FGFDMExec::DoTrim(int mode) { double saved_time; -cout << "DoTrim called" << endl; - if (Constructing) return; if (mode < 0 || mode > JSBSim::tNone) { cerr << endl << "Illegal trimming mode!" << endl << endl; return; } - saved_time = State->Getsim_time(); + saved_time = sim_time; FGTrim trim(this, (JSBSim::TrimMode)mode); if ( !trim.DoTrim() ) cerr << endl << "Trim Failed" << endl << endl; trim.Report(); - State->Setsim_time(saved_time); + sim_time = saved_time; } +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +/* +void FGFDMExec::DoTrimAnalysis(int mode) +{ + double saved_time; + if (Constructing) return; + + if (mode < 0 || mode > JSBSim::taNone) { + cerr << endl << "Illegal trimming mode!" << endl << endl; + return; + } + saved_time = sim_time; + + FGTrimAnalysis trimAnalysis(this, (JSBSim::TrimAnalysisMode)mode); + + if ( !trimAnalysis.Load(IC->GetInitFile(), false) ) { + cerr << "A problem occurred with trim configuration file " << trimAnalysis.Load(IC->GetInitFile()) << endl; + exit(-1); + } + + bool result = trimAnalysis.DoTrim(); + + if ( !result ) cerr << endl << "Trim Failed" << endl << endl; + + trimAnalysis.Report(); + Setsim_time(saved_time); + + EnableOutput(); + cout << "\nOutput: " << GetOutputFileName() << endl; + +} +*/ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void FGFDMExec::UseAtmosphereMSIS(void) @@ -794,7 +1055,7 @@ void FGFDMExec::Debug(int from) if (from == 0) { // Constructor cout << "\n\n " << highint << underon << "JSBSim Flight Dynamics Model v" << JSBSim_version << underoff << normint << endl; - cout << halfint << " [cfg file spec v" << needed_cfg_version << "]\n\n"; + cout << halfint << " [JSBSim-ML v" << needed_cfg_version << "]\n\n"; cout << normint << "JSBSim startup beginning ...\n\n"; } else if (from == 3) { cout << "\n\nJSBSim startup complete\n\n"; @@ -807,7 +1068,7 @@ void FGFDMExec::Debug(int from) if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects if (from == 2) { cout << "================== Frame: " << Frame << " Time: " - << State->Getsim_time() << " dt: " << State->Getdt() << endl; + << sim_time << " dt: " << dT << endl; } } if (debug_lvl & 8 ) { // Runtime state variables