X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FFDM%2FJSBSim%2FFGFDMExec.cpp;h=a3b08a49ec5deaf6afdf01e392f4cfdcf5ac3f75;hb=de1564d83e32cf077a5367cc838f28271dd79381;hp=3002ec56bdc2f44175b5eeba46acd7ccbaef58ac;hpb=8e1482f746088ce9cf607e2cb49d532926e36a49;p=flightgear.git diff --git a/src/FDM/JSBSim/FGFDMExec.cpp b/src/FDM/JSBSim/FGFDMExec.cpp index 3002ec56b..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,77 +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 "FGAtmosphere.h" -#include "FGFCS.h" -#include "FGPropulsion.h" -#include "FGMassBalance.h" -#include "FGGroundReactions.h" -#include "FGAerodynamics.h" -#include "FGInertial.h" -#include "FGAircraft.h" -#include "FGPropagate.h" -#include "FGAuxiliary.h" -#include "FGOutput.h" -#include "FGConfigFile.h" -#include "FGInitialCondition.h" -#include "FGPropertyManager.h" +#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) +FGFDMExec::FGFDMExec(FGPropertyManager* root, unsigned int* fdmctr) : Root(root), FDMctr(fdmctr) { Frame = 0; - FirstModel = 0; Error = 0; - State = 0; + GroundCallback = 0; Atmosphere = 0; FCS = 0; Propulsion = 0; @@ -119,44 +94,79 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root) Aerodynamics = 0; Inertial = 0; GroundReactions = 0; + ExternalReactions = 0; + BuoyantForces = 0; Aircraft = 0; Propagate = 0; Auxiliary = 0; - Output = 0; + Input = 0; IC = 0; Trim = 0; + Script = 0; + + RootDir = ""; - terminate = false; - frozen = false; modelLoaded = false; - IsSlave = false; + IsChild = false; + holding = false; + Terminate = false; + StandAlone = false; - IdFDM = FDMctr; - FDMctr++; + 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. + + 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; + } - instance = master->GetNode("/fdm/jsbsim",IdFDM,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 = Root->GetNode("/fdm/jsbsim",IdFDM,true); Debug(0); - - // this is here to catch errors in binding member functions - // to the property tree. + // this is to catch errors in binding member functions to the property tree. try { Allocate(); } catch ( string msg ) { cout << "Caught error: " << msg << endl; exit(1); } + + trim_status = false; + ta_mode = 99; + + Constructing = true; + typedef int (FGFDMExec::*iPMF)(void) const; +// 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; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -164,14 +174,30 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root) FGFDMExec::~FGFDMExec() { try { + Unbind(); DeAllocate(); - checkTied( instance ); + + 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); } @@ -188,61 +214,21 @@ 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); - Output = new FGOutput(this); - - 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+=512;} - if (!Auxiliary->InitModel()) { - cerr << fgred << "Auxiliary model init failed" << fgdef << endl; - Error+=2058;} - if (!Output->InitModel()) { - cerr << fgred << "Output model init failed" << fgdef << endl; - Error+=4096;} - - if (Error > 0) result = false; - - IC = new FGInitialCondition(this); + Input = new FGInput(this); // Schedule a model. The second arg (the integer) is the pass number. For - // instance, the atmosphere model gets executed every fifth pass it is called - // by the executive. Everything else here gets executed each pass. - // 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); Schedule(Propulsion, 1); @@ -250,10 +236,18 @@ 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); - Schedule(Output, 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; @@ -264,6 +258,7 @@ bool FGFDMExec::Allocate(void) bool FGFDMExec::DeAllocate(void) { + delete Input; delete Atmosphere; delete FCS; delete Propulsion; @@ -271,19 +266,24 @@ bool FGFDMExec::DeAllocate(void) delete Aerodynamics; delete Inertial; delete GroundReactions; + delete ExternalReactions; + delete BuoyantForces; delete Aircraft; delete Propagate; delete Auxiliary; - delete Output; - delete State; + delete Script; + + for (unsigned i=0; iSetRate(rate); + Models.push_back(model); +} - model_iterator = FirstModel; +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - if (model_iterator == 0L) { // this is the first model +bool FGFDMExec::Run(void) +{ + bool success=true; - FirstModel = model; - FirstModel->NextModel = 0L; - FirstModel->SetRate(rate); + Debug(2); - } else { // subsequent model + for (unsigned int i=1; iAssignState(Propagate); // Transfer state to the child FDM + ChildFDMList[i]->Run(); + } - while (model_iterator->NextModel != 0L) { - model_iterator = model_iterator->NextModel; - } - model_iterator->NextModel = model; - model_iterator->NextModel->SetRate(rate); + // returns true if success, false if complete + if (Script != 0 && !IntegrationSuspended()) success = Script->RunScript(); - } + 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. - if (frozen) return true; + return true; +} - model_iterator = FirstModel; - if (model_iterator == 0L) return false; +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - Debug(2); +void FGFDMExec::Initialize(FGInitialCondition *FGIC) +{ + Setsim_time(0.0); - for (unsigned int i=1; iexec->State->Initialize(); // Transfer state to the slave FDM -// SlaveFDMList[i]->exec->Run(); - } + Propagate->SetInitialState( FGIC ); - while (model_iterator != 0L) { - model_iterator->Run(); - model_iterator = model_iterator->NextModel; - } + Atmosphere->Run(); + Atmosphere->SetWindNED( FGIC->GetWindNFpsIC(), + FGIC->GetWindEFpsIC(), + FGIC->GetWindDFpsIC() ); - frame = Frame++; - State->IncrTime(); - return true; + 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); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// 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->Suspend(); - State->Initialize(IC); - Run(); - State->Resume(); + 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) +{ + delete GroundCallback; + GroundCallback = p; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -375,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; @@ -384,107 +445,355 @@ vector FGFDMExec::EnumerateFDMs(void) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::LoadModel(string AircraftPath, string EnginePath, string model) +bool FGFDMExec::LoadScript(const string& script, double deltaT) +{ + 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 = AircraftPath; - FGFDMExec::EnginePath = EnginePath; + FGFDMExec::AircraftPath = RootDir + AircraftPath; + FGFDMExec::EnginePath = RootDir + EnginePath; + FGFDMExec::SystemsPath = RootDir + SystemsPath; - return LoadModel(model); + return LoadModel(model, addModelToPath); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::LoadModel(string model) +bool FGFDMExec::LoadModel(const string& model, bool addModelToPath) { - bool result = true; string token; string aircraftCfgFileName; + Element* element = 0L; + bool result = false; // initialize result to false, indicating input file not yet read + + 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; } -# ifndef macintosh - aircraftCfgFileName = AircraftPath + "/" + model + ".xml"; -# else - aircraftCfgFileName = AircraftPath + ";" + model + ".xml"; -# endif - - FGConfigFile AC_cfg(aircraftCfgFileName); - if (!AC_cfg.IsOpen()) return false; - - modelName = model; + FullAircraftPath = AircraftPath; + if (addModelToPath) FullAircraftPath += "/" + model; + aircraftCfgFileName = FullAircraftPath + "/" + model + ".xml"; if (modelLoaded) { DeAllocate(); Allocate(); } - if (!ReadPrologue(&AC_cfg)) return false; - - while ((AC_cfg.GetNextConfigLine() != string("EOF")) && - (token = AC_cfg.GetValue()) != string("/FDM_CONFIG")) { - if (token == "METRICS") { - if (debug_lvl > 0) cout << fgcyan << "\n Reading Metrics" << fgdef << endl; - if (!ReadMetrics(&AC_cfg)) result = false; - } else if (token == "SLAVE") { - if (debug_lvl > 0) cout << fgcyan << "\n Reading Slave flight vehicle: " << fgdef - << AC_cfg.GetValue("NAME") << endl; - if (!ReadSlave(&AC_cfg)) result = false; - } else if (token == "AERODYNAMICS") { - if (debug_lvl > 0) cout << fgcyan << "\n Reading Aerodynamics" << fgdef << endl; - if (!ReadAerodynamics(&AC_cfg)) result = false; - } else if (token == "UNDERCARRIAGE") { - if (debug_lvl > 0) cout << fgcyan << "\n Reading Landing Gear" << fgdef << endl; - if (!ReadUndercarriage(&AC_cfg)) result = false; - } else if (token == "PROPULSION") { - if (debug_lvl > 0) cout << fgcyan << "\n Reading Propulsion" << fgdef << endl; - if (!ReadPropulsion(&AC_cfg)) result = false; - } else if (token == "FLIGHT_CONTROL") { - if (debug_lvl > 0) cout << fgcyan << "\n Reading Flight Control" << fgdef << endl; - if (!ReadFlightControls(&AC_cfg)) result = false; - } else if (token == "AUTOPILOT") { - if (debug_lvl > 0) cout << fgcyan << "\n Reading Autopilot" << fgdef << endl; - if (!ReadFlightControls(&AC_cfg)) result = false; - } else if (token == "OUTPUT") { - if (debug_lvl > 0) cout << fgcyan << "\n Reading Output directives" << fgdef << endl; - if (!ReadOutput(&AC_cfg)) result = false; + int saved_debug_lvl = debug_lvl; + + document = LoadXMLDocument(aircraftCfgFileName); // "document" is a class member + if (document) { + if (IsChild) debug_lvl = 0; + + ReadPrologue(document); + + if (IsChild) debug_lvl = saved_debug_lvl; + + // 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; + } + } + + // 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); + 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"); + } + + // 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; + } } - } - 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 - << " FGFDMExec: Failed to load aircraft and/or engine model" + << " JSBSim failed to open the configuration file: " << aircraftCfgFileName << fgdef << endl; } + if (result) { + struct PropertyCatalogStructure masterPCS; + masterPCS.base_string = ""; + masterPCS.node = (FGPropertyManager*)Root; + BuildPropertyCatalog(&masterPCS); + } + + return result; +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +void FGFDMExec::BuildPropertyCatalog(struct PropertyCatalogStructure* pcs) +{ + struct PropertyCatalogStructure* pcsNew = new struct PropertyCatalogStructure; + int node_idx = 0; + + 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(); + 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); + BuildPropertyCatalog(pcsNew); + } + } + delete pcsNew; +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +string FGFDMExec::QueryPropertyCatalog(const string& in) +{ + string results=""; + for (unsigned 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; + return result; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::ReadPrologue(FGConfigFile* AC_cfg) +bool FGFDMExec::ReadPrologue(Element* el) // el for ReadPrologue is the document element { - string token = AC_cfg->GetValue(); - string scratch; - string AircraftName; + bool result = true; // true for success - AircraftName = AC_cfg->GetValue("NAME"); + if (!el) return false; + + string AircraftName = el->GetAttributeValue("name"); Aircraft->SetAircraftName(AircraftName); - if (debug_lvl > 0) cout << underon << "Reading Aircraft Configuration File" + if (debug_lvl & 1) cout << underon << "Reading Aircraft Configuration File" << underoff << ": " << highint << AircraftName << normint << endl; - scratch = AC_cfg->GetValue("VERSION").c_str(); - CFGVersion = AC_cfg->GetValue("VERSION"); - Release = AC_cfg->GetValue("RELEASE"); + CFGVersion = el->GetAttributeValue("version"); + Release = el->GetAttributeValue("release"); - if (debug_lvl > 0) + if (debug_lvl & 1) cout << " Version: " << highint << CFGVersion << normint << endl; if (CFGVersion != needed_cfg_version) { @@ -495,8 +804,7 @@ bool FGFDMExec::ReadPrologue(FGConfigFile* AC_cfg) return false; } - if (Release == "ALPHA") { - system("banner ALPHA"); + if (Release == "ALPHA" && (debug_lvl & 1)) { cout << endl << endl << highint << "This aircraft model is an " << fgred << Release << reset << highint << " release!!!" << endl << endl << reset @@ -504,149 +812,220 @@ bool FGFDMExec::ReadPrologue(FGConfigFile* AC_cfg) << " will not fly as expected." << endl << endl << fgred << highint << "Use this model for development purposes ONLY!!!" << normint << reset << endl << endl; - } else if (Release == "BETA") { - system("banner BETA"); + } else if (Release == "BETA" && (debug_lvl & 1)) { cout << endl << endl << highint << "This aircraft model is a " << fgred << Release << reset << highint << " release!!!" << endl << endl << reset << "This aircraft model probably will not fly as expected." << endl << endl << fgblue << highint << "Use this model for development purposes ONLY!!!" << normint << reset << endl << endl; + } else if (Release == "PRODUCTION" && (debug_lvl & 1)) { + cout << endl << endl + << highint << "This aircraft model is a " << fgblue << Release + << reset << highint << " release." << endl << endl << reset; + } else if (debug_lvl & 1) { + cout << endl << endl + << highint << "This aircraft model is an " << fgred << Release + << reset << highint << " release!!!" << endl << endl << reset + << "This aircraft model may not even properly load, and probably" + << " will not fly as expected." << endl << endl + << fgred << highint << "Use this model for development purposes ONLY!!!" + << normint << reset << endl << endl; } - return true; + return result; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::ReadSlave(FGConfigFile* AC_cfg) +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(); + struct childData* child = new childData; - string AircraftName = AC_cfg->GetValue("FILE"); + child->exec = new FGFDMExec(Root, FDMctr); + child->exec->SetChild(true); - debug_lvl = 0; // turn off debug output for slave vehicle + 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. - 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 + child->exec->SetAircraftPath( AircraftPath ); + child->exec->SetEnginePath( EnginePath ); + child->exec->SetSystemsPath( SystemsPath ); + child->exec->LoadModel(childAircraft); - 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; + 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; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::ReadPropulsion(FGConfigFile* AC_cfg) +FGPropertyManager* FGFDMExec::GetPropertyManager(void) { - if (!Propulsion->Load(AC_cfg)) { - cerr << " Propulsion not successfully loaded" << endl; - return false; - } - return true; + return instance; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::ReadFlightControls(FGConfigFile* AC_cfg) +FGTrim* FGFDMExec::GetTrim(void) { - if (!FCS->Load(AC_cfg)) { - cerr << " Flight Controls not successfully loaded" << endl; - return false; - } - return true; + delete Trim; + Trim = new FGTrim(this,tNone); + return Trim; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::ReadAerodynamics(FGConfigFile* AC_cfg) +void FGFDMExec::DisableOutput(void) { - if (!Aerodynamics->Load(AC_cfg)) { - cerr << " Aerodynamics not successfully loaded" << endl; - return false; + for (unsigned i=0; iDisable(); } - return true; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::ReadUndercarriage(FGConfigFile* AC_cfg) +void FGFDMExec::EnableOutput(void) { - if (!GroundReactions->Load(AC_cfg)) { - cerr << " Ground Reactions not successfully loaded" << endl; - return false; + for (unsigned i=0; iEnable(); } - return true; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::ReadMetrics(FGConfigFile* AC_cfg) +void FGFDMExec::ForceOutput(int idx) { - if (!Aircraft->Load(AC_cfg)) { - cerr << " Aircraft metrics not successfully loaded" << endl; - return false; + 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 true; + + return result; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bool FGFDMExec::ReadOutput(FGConfigFile* AC_cfg) +void FGFDMExec::DoTrim(int mode) { - if (!Output->Load(AC_cfg)) { - cerr << " Output not successfully loaded" << endl; - return false; + double saved_time; + + if (Constructing) return; + + if (mode < 0 || mode > JSBSim::tNone) { + cerr << endl << "Illegal trimming mode!" << endl << endl; + return; } - return true; + saved_time = sim_time; + FGTrim trim(this, (JSBSim::TrimMode)mode); + if ( !trim.DoTrim() ) cerr << endl << "Trim Failed" << endl << endl; + trim.Report(); + sim_time = saved_time; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +/* +void FGFDMExec::DoTrimAnalysis(int mode) +{ + double saved_time; + if (Constructing) return; -FGPropertyManager* FGFDMExec::GetPropertyManager(void) { - return instance; + 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) +{ + FGAtmosphere *oldAtmosphere = Atmosphere; + Atmosphere = new MSIS(this); + if (!Atmosphere->InitModel()) { + cerr << fgred << "MSIS Atmosphere model init failed" << fgdef << endl; + Error+=1; + } + delete oldAtmosphere; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -FGTrim* FGFDMExec::GetTrim(void) { - delete Trim; - Trim = new FGTrim(this,tNone); - return Trim; +void FGFDMExec::UseAtmosphereMars(void) +{ +/* + FGAtmosphere *oldAtmosphere = Atmosphere; + Atmosphere = new FGMars(this); + if (!Atmosphere->InitModel()) { + cerr << fgred << "Mars Atmosphere model init failed" << fgdef << endl; + Error+=1; + } + delete oldAtmosphere; +*/ } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -676,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"; @@ -689,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() << endl; + << sim_time << " dt: " << dT << endl; } } if (debug_lvl & 8 ) { // Runtime state variables @@ -705,3 +1084,4 @@ void FGFDMExec::Debug(int from) } } +