]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/FGFDMExec.cpp
Merge branch 'next' of gitorious.org:fg/flightgear into next
[flightgear.git] / src / FDM / JSBSim / FGFDMExec.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3  Module:       FGFDMExec.cpp
4  Author:       Jon S. Berndt
5  Date started: 11/17/98
6  Purpose:      Schedules and runs the model routines.
7
8  ------------- Copyright (C) 1999  Jon S. Berndt (jon@jsbsim.org) -------------
9
10  This program is free software; you can redistribute it and/or modify it under
11  the terms of the GNU Lesser General Public License as published by the Free Software
12  Foundation; either version 2 of the License, or (at your option) any later
13  version.
14
15  This program is distributed in the hope that it will be useful, but WITHOUT
16  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
18  details.
19
20  You should have received a copy of the GNU Lesser General Public License along with
21  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22  Place - Suite 330, Boston, MA  02111-1307, USA.
23
24  Further information about the GNU Lesser General Public License can also be found on
25  the world wide web at http://www.gnu.org.
26
27 FUNCTIONAL DESCRIPTION
28 --------------------------------------------------------------------------------
29
30 This class wraps up the simulation scheduling routines.
31
32 HISTORY
33 --------------------------------------------------------------------------------
34 11/17/98   JSB   Created
35
36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37 COMMENTS, REFERENCES,  and NOTES
38 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
39
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41 INCLUDES
42 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
43
44 #include "FGFDMExec.h"
45 #include "models/FGAtmosphere.h"
46 #include "models/atmosphere/FGMSIS.h"
47 #include "models/atmosphere/FGMars.h"
48 #include "models/FGFCS.h"
49 #include "models/FGPropulsion.h"
50 #include "models/FGMassBalance.h"
51 #include "models/FGGroundReactions.h"
52 #include "models/FGExternalReactions.h"
53 #include "models/FGBuoyantForces.h"
54 #include "models/FGAerodynamics.h"
55 #include "models/FGInertial.h"
56 #include "models/FGAircraft.h"
57 #include "models/FGPropagate.h"
58 #include "models/FGAuxiliary.h"
59 #include "models/FGInput.h"
60 #include "models/FGOutput.h"
61 #include "initialization/FGInitialCondition.h"
62 //#include "initialization/FGTrimAnalysis.h" // Remove until later
63 #include "input_output/FGPropertyManager.h"
64 #include "input_output/FGScript.h"
65
66 #include <iostream>
67 #include <iterator>
68 #include <cstdlib>
69
70 using namespace std;
71
72 namespace JSBSim {
73
74 static const char *IdSrc = "$Id: FGFDMExec.cpp,v 1.82 2010/10/07 03:17:29 jberndt Exp $";
75 static const char *IdHdr = ID_FDMEXEC;
76
77 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78 CLASS IMPLEMENTATION
79 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
80
81 void checkTied ( FGPropertyManager *node )
82 {
83   int N = node->nChildren();
84   string name;
85
86   for (int i=0; i<N; i++) {
87     if (node->getChild(i)->nChildren() ) {
88       checkTied( (FGPropertyManager*)node->getChild(i) );
89     }
90     if ( node->getChild(i)->isTied() ) {
91       name = ((FGPropertyManager*)node->getChild(i))->GetFullyQualifiedName();
92       node->Untie(name);
93     }
94   }
95 }
96
97 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98 // Constructors
99 FGFDMExec::FGFDMExec(FGPropertyManager* root) : Root(root)
100 {
101   FDMctr = new unsigned int;
102   *FDMctr = 0;
103   Initialize();
104 }
105
106 FGFDMExec::FGFDMExec(FGPropertyManager* root, unsigned int* fdmctr) : Root(root), FDMctr(fdmctr)
107 {
108   Initialize();
109 }
110
111 void FGFDMExec::Initialize()
112 {
113   Frame           = 0;
114   Error           = 0;
115   GroundCallback  = 0;
116   Atmosphere      = 0;
117   FCS             = 0;
118   Propulsion      = 0;
119   MassBalance     = 0;
120   Aerodynamics    = 0;
121   Inertial        = 0;
122   GroundReactions = 0;
123   ExternalReactions = 0;
124   BuoyantForces   = 0;
125   Aircraft        = 0;
126   Propagate       = 0;
127   Auxiliary       = 0;
128   Input           = 0;
129   IC              = 0;
130   Trim            = 0;
131   Script          = 0;
132
133   RootDir = "";
134
135   modelLoaded = false;
136   IsChild = false;
137   holding = false;
138   Terminate = false;
139
140   sim_time = 0.0;
141   dT = 1.0/120.0; // a default timestep size. This is needed for when JSBSim is
142                   // run in standalone mode with no initialization file.
143
144   try {
145     char* num = getenv("JSBSIM_DEBUG");
146     if (num) debug_lvl = atoi(num); // set debug level
147   } catch (...) {                   // if error set to 1
148     debug_lvl = 1;
149   }
150
151   if (Root == 0) {                 // Then this is the root FDM
152     Root = new FGPropertyManager;  // Create the property manager
153     
154     FDMctr = new unsigned int;     // Create and initialize the child FDM counter
155     (*FDMctr) = 0;
156   }
157
158   // Store this FDM's ID
159   IdFDM = (*FDMctr); // The main (parent) JSBSim instance is always the "zeroth"
160                                                                       
161   // Prepare FDMctr for the next child FDM id
162   (*FDMctr)++;       // instance. "child" instances are loaded last.
163
164   instance = Root->GetNode("/fdm/jsbsim",IdFDM,true);
165   Debug(0);
166   // this is to catch errors in binding member functions to the property tree.
167   try {
168     Allocate();
169   } catch ( string msg ) {
170     cout << "Caught error: " << msg << endl;
171     exit(1);
172   }
173
174   trim_status = false;
175   ta_mode     = 99;
176
177   Constructing = true;
178   typedef int (FGFDMExec::*iPMF)(void) const;
179 //  instance->Tie("simulation/do_trim_analysis", this, (iPMF)0, &FGFDMExec::DoTrimAnalysis);
180   instance->Tie("simulation/do_simple_trim", this, (iPMF)0, &FGFDMExec::DoTrim);
181   instance->Tie("simulation/reset", this, (iPMF)0, &FGFDMExec::ResetToInitialConditions);
182   instance->Tie("simulation/terminate", (int *)&Terminate);
183   instance->Tie("simulation/sim-time-sec", this, &FGFDMExec::GetSimTime);
184   instance->Tie("simulation/jsbsim-debug", this, &FGFDMExec::GetDebugLevel, &FGFDMExec::SetDebugLevel);
185
186   Constructing = false;
187 }
188
189 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
190
191 FGFDMExec::~FGFDMExec()
192 {
193   try {
194     checkTied( instance );
195     DeAllocate();
196     
197     if (IdFDM == 0) { // Meaning this is no child FDM
198       if(Root != 0) {
199          delete Root;
200          Root = 0;
201       }
202       if(FDMctr != 0) {
203          delete FDMctr;
204          FDMctr = 0;
205       }
206     }
207   } catch ( string msg ) {
208     cout << "Caught error: " << msg << endl;
209   }
210
211   for (unsigned int i=1; i<ChildFDMList.size(); i++) delete ChildFDMList[i]->exec;
212   ChildFDMList.clear();
213
214   PropertyCatalog.clear();
215
216   FDMctr--;
217
218   Debug(1);
219 }
220
221 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
222
223 bool FGFDMExec::Allocate(void)
224 {
225   bool result=true;
226
227   Atmosphere      = new FGAtmosphere(this);
228   FCS             = new FGFCS(this);
229   Propulsion      = new FGPropulsion(this);
230   MassBalance     = new FGMassBalance(this);
231   Aerodynamics    = new FGAerodynamics (this);
232   Inertial        = new FGInertial(this);
233
234   GroundCallback  = new FGGroundCallback(Inertial->GetRefRadius());
235
236   GroundReactions = new FGGroundReactions(this);
237   ExternalReactions = new FGExternalReactions(this);
238   BuoyantForces   = new FGBuoyantForces(this);
239   Aircraft        = new FGAircraft(this);
240   Propagate       = new FGPropagate(this);
241   Auxiliary       = new FGAuxiliary(this);
242   Input           = new FGInput(this);
243
244   // Schedule a model. The second arg (the integer) is the pass number. For
245   // instance, the atmosphere model could get executed every fifth pass it is called.
246   
247   Schedule(Input,           1);
248   Schedule(Atmosphere,      1);
249   Schedule(FCS,             1);
250   Schedule(Propulsion,      1);
251   Schedule(MassBalance,     1);
252   Schedule(Aerodynamics,    1);
253   Schedule(Inertial,        1);
254   Schedule(GroundReactions, 1);
255   Schedule(ExternalReactions, 1);
256   Schedule(BuoyantForces,   1);
257   Schedule(Aircraft,        1);
258   Schedule(Propagate,       1);
259   Schedule(Auxiliary,       1);
260
261   // Initialize models so they can communicate with each other
262
263   vector <FGModel*>::iterator it;
264   for (it = Models.begin(); it != Models.end(); ++it) (*it)->InitModel();
265
266   IC = new FGInitialCondition(this);
267
268   modelLoaded = false;
269
270   return result;
271 }
272
273 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
274
275 bool FGFDMExec::DeAllocate(void)
276 {
277   delete Input;
278   delete Atmosphere;
279   delete FCS;
280   delete Propulsion;
281   delete MassBalance;
282   delete Aerodynamics;
283   delete Inertial;
284   delete GroundReactions;
285   delete ExternalReactions;
286   delete BuoyantForces;
287   delete Aircraft;
288   delete Propagate;
289   delete Auxiliary;
290   delete Script;
291
292   for (unsigned i=0; i<Outputs.size(); i++) delete Outputs[i];
293   Outputs.clear();
294
295   delete IC;
296   delete Trim;
297
298   delete GroundCallback;
299
300   Error       = 0;
301
302   Input           = 0;
303   Atmosphere      = 0;
304   FCS             = 0;
305   Propulsion      = 0;
306   MassBalance     = 0;
307   Aerodynamics    = 0;
308   Inertial        = 0;
309   GroundReactions = 0;
310   ExternalReactions = 0;
311   BuoyantForces   = 0;
312   Aircraft        = 0;
313   Propagate       = 0;
314   Auxiliary       = 0;
315   Script          = 0;
316
317   Models.clear();
318
319   modelLoaded = false;
320   return modelLoaded;
321 }
322
323 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
324
325 void FGFDMExec::Schedule(FGModel* model, int rate)
326 {
327   model->SetRate(rate);
328   Models.push_back(model);
329 }
330
331 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
332
333 bool FGFDMExec::Run(void)
334 {
335   bool success=true;
336
337   Debug(2);
338
339   for (unsigned int i=1; i<ChildFDMList.size(); i++) {
340     ChildFDMList[i]->AssignState(Propagate); // Transfer state to the child FDM
341     ChildFDMList[i]->Run();
342   }
343
344   // returns true if success, false if complete
345   if (Script != 0 && !IntegrationSuspended()) success = Script->RunScript();
346
347   vector <FGModel*>::iterator it;
348   for (it = Models.begin(); it != Models.end(); ++it) (*it)->Run();
349
350   Frame++;
351   if (!Holding()) IncrTime();
352   if (Terminate) success = false;
353
354   return (success);
355 }
356
357 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
358 // This call will cause the sim time to reset to 0.0
359
360 bool FGFDMExec::RunIC(void)
361 {
362   SuspendIntegration(); // saves the integration rate, dt, then sets it to 0.0.
363   Initialize(IC);
364   Run();
365   ResumeIntegration(); // Restores the integration rate to what it was.
366
367   return true;
368 }
369
370 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
371
372 void FGFDMExec::Initialize(FGInitialCondition *FGIC)
373 {
374   Propagate->SetInitialState( FGIC );
375
376   Atmosphere->Run();
377   Atmosphere->SetWindNED( FGIC->GetWindNFpsIC(),
378                           FGIC->GetWindEFpsIC(),
379                           FGIC->GetWindDFpsIC() );
380
381   FGColumnVector3 vAeroUVW;
382   vAeroUVW = Propagate->GetUVW() + Propagate->GetTl2b()*Atmosphere->GetTotalWindNED();
383
384   double alpha, beta;
385   if (vAeroUVW(eW) != 0.0)
386     alpha = vAeroUVW(eU)*vAeroUVW(eU) > 0.0 ? atan2(vAeroUVW(eW), vAeroUVW(eU)) : 0.0;
387   else
388     alpha = 0.0;
389   if (vAeroUVW(eV) != 0.0)
390     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;
391   else
392     beta = 0.0;
393
394   Auxiliary->SetAB(alpha, beta);
395
396   double Vt = vAeroUVW.Magnitude();
397   Auxiliary->SetVt(Vt);
398
399   Auxiliary->SetMach(Vt/Atmosphere->GetSoundSpeed());
400
401   double qbar = 0.5*Vt*Vt*Atmosphere->GetDensity();
402   Auxiliary->Setqbar(qbar);
403 }
404
405 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
406 //
407 // A private, internal function call for Tie-ing to a property, so it needs an
408 // argument.
409
410 void FGFDMExec::ResetToInitialConditions(int mode)
411 {
412   if (mode == 1) {
413     for (unsigned int i=0; i<Outputs.size(); i++) {
414       Outputs[i]->SetStartNewFile(true); 
415     }
416   }
417   
418   ResetToInitialConditions();
419 }
420
421 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
422
423 void FGFDMExec::ResetToInitialConditions(void)
424 {
425   if (Constructing) return;
426
427   vector <FGModel*>::iterator it;
428   for (it = Models.begin(); it != Models.end(); ++it) (*it)->InitModel();
429
430   RunIC();
431   if (Script) Script->ResetEvents();
432 }
433
434 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
435
436 void FGFDMExec::SetGroundCallback(FGGroundCallback* p)
437 {
438   delete GroundCallback;
439   GroundCallback = p;
440 }
441
442 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
443
444 vector <string> FGFDMExec::EnumerateFDMs(void)
445 {
446   vector <string> FDMList;
447
448   FDMList.push_back(Aircraft->GetAircraftName());
449
450   for (unsigned int i=1; i<ChildFDMList.size(); i++) {
451     FDMList.push_back(ChildFDMList[i]->exec->GetAircraft()->GetAircraftName());
452   }
453
454   return FDMList;
455 }
456
457 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
458
459 bool FGFDMExec::LoadScript(const string& script, double deltaT)
460 {
461   bool result;
462
463   Script = new FGScript(this);
464   result = Script->LoadScript(RootDir + script, deltaT);
465
466   return result;
467 }
468
469 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
470
471 bool FGFDMExec::LoadModel(const string& AircraftPath, const string& EnginePath, const string& SystemsPath,
472                 const string& model, bool addModelToPath)
473 {
474   FGFDMExec::AircraftPath = RootDir + AircraftPath;
475   FGFDMExec::EnginePath = RootDir + EnginePath;
476   FGFDMExec::SystemsPath = RootDir + SystemsPath;
477
478   return LoadModel(model, addModelToPath);
479 }
480
481 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482
483 bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
484 {
485   string token;
486   string aircraftCfgFileName;
487   Element* element = 0L;
488   bool result = false; // initialize result to false, indicating input file not yet read
489
490   modelName = model; // Set the class modelName attribute
491
492   if( AircraftPath.empty() || EnginePath.empty() || SystemsPath.empty()) {
493     cerr << "Error: attempted to load aircraft with undefined ";
494     cerr << "aircraft, engine, and system paths" << endl;
495     return false;
496   }
497
498   FullAircraftPath = AircraftPath;
499   if (addModelToPath) FullAircraftPath += "/" + model;
500   aircraftCfgFileName = FullAircraftPath + "/" + model + ".xml";
501
502   if (modelLoaded) {
503     DeAllocate();
504     Allocate();
505   }
506
507   int saved_debug_lvl = debug_lvl;
508
509   document = LoadXMLDocument(aircraftCfgFileName); // "document" is a class member
510   if (document) {
511     if (IsChild) debug_lvl = 0;
512
513     ReadPrologue(document);
514
515     if (IsChild) debug_lvl = saved_debug_lvl;
516
517     // Process the fileheader element in the aircraft config file. This element is OPTIONAL.
518     element = document->FindElement("fileheader");
519     if (element) {
520       result = ReadFileHeader(element);
521       if (!result) {
522         cerr << endl << "Aircraft fileheader element has problems in file " << aircraftCfgFileName << endl;
523         return result;
524       }
525     }
526
527     if (IsChild) debug_lvl = 0;
528
529     // Process the metrics element. This element is REQUIRED.
530     element = document->FindElement("metrics");
531     if (element) {
532       result = Aircraft->Load(element);
533       if (!result) {
534         cerr << endl << "Aircraft metrics element has problems in file " << aircraftCfgFileName << endl;
535         return result;
536       }
537     } else {
538       cerr << endl << "No metrics element was found in the aircraft config file." << endl;
539       return false;
540     }
541
542     // Process the mass_balance element. This element is REQUIRED.
543     element = document->FindElement("mass_balance");
544     if (element) {
545       result = MassBalance->Load(element);
546       if (!result) {
547         cerr << endl << "Aircraft mass_balance element has problems in file " << aircraftCfgFileName << endl;
548         return result;
549       }
550     } else {
551       cerr << endl << "No mass_balance element was found in the aircraft config file." << endl;
552       return false;
553     }
554
555     // Process the ground_reactions element. This element is REQUIRED.
556     element = document->FindElement("ground_reactions");
557     if (element) {
558       result = GroundReactions->Load(element);
559       if (!result) {
560         cerr << endl << "Aircraft ground_reactions element has problems in file " << aircraftCfgFileName << endl;
561         return result;
562       }
563     } else {
564       cerr << endl << "No ground_reactions element was found in the aircraft config file." << endl;
565       return false;
566     }
567
568     // Process the external_reactions element. This element is OPTIONAL.
569     element = document->FindElement("external_reactions");
570     if (element) {
571       result = ExternalReactions->Load(element);
572       if (!result) {
573         cerr << endl << "Aircraft external_reactions element has problems in file " << aircraftCfgFileName << endl;
574         return result;
575       }
576     }
577
578     // Process the buoyant_forces element. This element is OPTIONAL.
579     element = document->FindElement("buoyant_forces");
580     if (element) {
581       result = BuoyantForces->Load(element);
582       if (!result) {
583         cerr << endl << "Aircraft buoyant_forces element has problems in file " << aircraftCfgFileName << endl;
584         return result;
585       }
586     }
587
588     // Process the propulsion element. This element is OPTIONAL.
589     element = document->FindElement("propulsion");
590     if (element) {
591       result = Propulsion->Load(element);
592       if (!result) {
593         cerr << endl << "Aircraft propulsion element has problems in file " << aircraftCfgFileName << endl;
594         return result;
595       }
596     }
597
598     // Process the system element[s]. This element is OPTIONAL, and there may be more than one.
599     element = document->FindElement("system");
600     while (element) {
601       result = FCS->Load(element, FGFCS::stSystem);
602       if (!result) {
603         cerr << endl << "Aircraft system element has problems in file " << aircraftCfgFileName << endl;
604         return result;
605       }
606       element = document->FindNextElement("system");
607     }
608
609     // Process the autopilot element. This element is OPTIONAL.
610     element = document->FindElement("autopilot");
611     if (element) {
612       result = FCS->Load(element, FGFCS::stAutoPilot);
613       if (!result) {
614         cerr << endl << "Aircraft autopilot element has problems in file " << aircraftCfgFileName << endl;
615         return result;
616       }
617     }
618
619     // Process the flight_control element. This element is OPTIONAL.
620     element = document->FindElement("flight_control");
621     if (element) {
622       result = FCS->Load(element, FGFCS::stFCS);
623       if (!result) {
624         cerr << endl << "Aircraft flight_control element has problems in file " << aircraftCfgFileName << endl;
625         return result;
626       }
627     }
628
629     // Process the aerodynamics element. This element is OPTIONAL, but almost always expected.
630     element = document->FindElement("aerodynamics");
631     if (element) {
632       result = Aerodynamics->Load(element);
633       if (!result) {
634         cerr << endl << "Aircraft aerodynamics element has problems in file " << aircraftCfgFileName << endl;
635         return result;
636       }
637     } else {
638       cerr << endl << "No expected aerodynamics element was found in the aircraft config file." << endl;
639     }
640
641     // Process the input element. This element is OPTIONAL.
642     element = document->FindElement("input");
643     if (element) {
644       result = Input->Load(element);
645       if (!result) {
646         cerr << endl << "Aircraft input element has problems in file " << aircraftCfgFileName << endl;
647         return result;
648       }
649     }
650
651     // Process the output element[s]. This element is OPTIONAL, and there may be more than one.
652     unsigned int idx=0;
653     typedef int (FGOutput::*iOPMF)(void) const;
654     element = document->FindElement("output");
655     while (element) {
656       if (debug_lvl > 0) cout << endl << "  Output data set: " << idx << "  ";
657       FGOutput* Output = new FGOutput(this);
658       Output->InitModel();
659       Schedule(Output, 1);
660       result = Output->Load(element);
661       if (!result) {
662         cerr << endl << "Aircraft output element has problems in file " << aircraftCfgFileName << endl;
663         return result;
664       } else {
665         Outputs.push_back(Output);
666         string outputProp = CreateIndexedPropertyName("simulation/output",idx);
667         instance->Tie(outputProp+"/log_rate_hz", Output, (iOPMF)0, &FGOutput::SetRate);
668         idx++;
669       }
670       element = document->FindNextElement("output");
671     }
672
673     // Lastly, process the child element. This element is OPTIONAL - and NOT YET SUPPORTED.
674     element = document->FindElement("child");
675     if (element) {
676       result = ReadChild(element);
677       if (!result) {
678         cerr << endl << "Aircraft child element has problems in file " << aircraftCfgFileName << endl;
679         return result;
680       }
681     }
682
683     modelLoaded = true;
684
685     if (debug_lvl > 0) {
686       MassBalance->Run(); // Update all mass properties for the report.
687       MassBalance->GetMassPropertiesReport();
688
689       cout << endl << fgblue << highint
690            << "End of vehicle configuration loading." << endl
691            << "-------------------------------------------------------------------------------"
692            << reset << endl;
693     }
694     
695     if (IsChild) debug_lvl = saved_debug_lvl;
696
697   } else {
698     cerr << fgred
699          << "  JSBSim failed to open the configuration file: " << aircraftCfgFileName
700          << fgdef << endl;
701   }
702
703   // Late bind previously undefined FCS inputs.
704   try {
705     FCS->LateBind();
706   } catch (string prop) {
707     cerr << endl << fgred << "  Could not late bind property " << prop 
708          << ". Aborting." << endl;
709     result = false;
710   }
711
712   if (result) {
713     struct PropertyCatalogStructure masterPCS;
714     masterPCS.base_string = "";
715     masterPCS.node = (FGPropertyManager*)Root;
716     BuildPropertyCatalog(&masterPCS);
717   }
718
719   return result;
720 }
721
722 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
723
724 void FGFDMExec::BuildPropertyCatalog(struct PropertyCatalogStructure* pcs)
725 {
726   struct PropertyCatalogStructure* pcsNew = new struct PropertyCatalogStructure;
727   int node_idx = 0;
728
729   for (int i=0; i<pcs->node->nChildren(); i++) {
730     pcsNew->base_string = pcs->base_string + "/" + pcs->node->getChild(i)->getName();
731     node_idx = pcs->node->getChild(i)->getIndex();
732     if (node_idx != 0) {
733       pcsNew->base_string = CreateIndexedPropertyName(pcsNew->base_string, node_idx);
734     }
735     if (pcs->node->getChild(i)->nChildren() == 0) {
736       if (pcsNew->base_string.substr(0,11) == string("/fdm/jsbsim")) {
737         pcsNew->base_string = pcsNew->base_string.erase(0,12);
738       }
739       PropertyCatalog.push_back(pcsNew->base_string);
740     } else {
741       pcsNew->node = (FGPropertyManager*)pcs->node->getChild(i);
742       BuildPropertyCatalog(pcsNew);
743     }
744   }
745   delete pcsNew;
746 }
747
748 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
749
750 string FGFDMExec::QueryPropertyCatalog(const string& in)
751 {
752   string results="";
753   for (unsigned i=0; i<PropertyCatalog.size(); i++) {
754     if (PropertyCatalog[i].find(in) != string::npos) results += PropertyCatalog[i] + "\n";
755   }
756   if (results.empty()) return "No matches found\n";
757   return results;
758 }
759
760 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
761
762 void FGFDMExec::PrintPropertyCatalog(void)
763 {
764   cout << endl;
765   cout << "  " << fgblue << highint << underon << "Property Catalog for "
766        << modelName << reset << endl << endl;
767   for (unsigned i=0; i<PropertyCatalog.size(); i++) {
768     cout << "    " << PropertyCatalog[i] << endl;
769   }
770 }
771
772 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
773
774 bool FGFDMExec::ReadFileHeader(Element* el)
775 {
776   bool result = true; // true for success
777
778   if (debug_lvl == 0) return result;
779
780   if (IsChild) {
781     cout << endl <<highint << fgblue << "Reading child model: " << IdFDM << reset << endl << endl;
782   }
783
784   if (el->FindElement("description"))
785     cout << "  Description:   " << el->FindElement("description")->GetDataLine() << endl;
786   if (el->FindElement("author"))
787     cout << "  Model Author:  " << el->FindElement("author")->GetDataLine() << endl;
788   if (el->FindElement("filecreationdate"))
789     cout << "  Creation Date: " << el->FindElement("filecreationdate")->GetDataLine() << endl;
790   if (el->FindElement("version"))
791     cout << "  Version:       " << el->FindElement("version")->GetDataLine() << endl;
792
793   return result;
794 }
795
796 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
797
798 bool FGFDMExec::ReadPrologue(Element* el) // el for ReadPrologue is the document element
799 {
800   bool result = true; // true for success
801
802   if (!el) return false;
803
804   string AircraftName = el->GetAttributeValue("name");
805   Aircraft->SetAircraftName(AircraftName);
806
807   if (debug_lvl & 1) cout << underon << "Reading Aircraft Configuration File"
808             << underoff << ": " << highint << AircraftName << normint << endl;
809
810   CFGVersion = el->GetAttributeValue("version");
811   Release    = el->GetAttributeValue("release");
812
813   if (debug_lvl & 1)
814     cout << "                            Version: " << highint << CFGVersion
815                                                     << normint << endl;
816   if (CFGVersion != needed_cfg_version) {
817     cerr << endl << fgred << "YOU HAVE AN INCOMPATIBLE CFG FILE FOR THIS AIRCRAFT."
818             " RESULTS WILL BE UNPREDICTABLE !!" << endl;
819     cerr << "Current version needed is: " << needed_cfg_version << endl;
820     cerr << "         You have version: " << CFGVersion << endl << fgdef << endl;
821     return false;
822   }
823
824   if (Release == "ALPHA" && (debug_lvl & 1)) {
825     cout << endl << endl
826          << highint << "This aircraft model is an " << fgred << Release
827          << reset << highint << " release!!!" << endl << endl << reset
828          << "This aircraft model may not even properly load, and probably"
829          << " will not fly as expected." << endl << endl
830          << fgred << highint << "Use this model for development purposes ONLY!!!"
831          << normint << reset << endl << endl;
832   } else if (Release == "BETA" && (debug_lvl & 1)) {
833     cout << endl << endl
834          << highint << "This aircraft model is a " << fgred << Release
835          << reset << highint << " release!!!" << endl << endl << reset
836          << "This aircraft model probably will not fly as expected." << endl << endl
837          << fgblue << highint << "Use this model for development purposes ONLY!!!"
838          << normint << reset << endl << endl;
839   } else if (Release == "PRODUCTION" && (debug_lvl & 1)) {
840     cout << endl << endl
841          << highint << "This aircraft model is a " << fgblue << Release
842          << reset << highint << " release." << endl << endl << reset;
843   } else if (debug_lvl & 1) {
844     cout << endl << endl
845          << highint << "This aircraft model is an " << fgred << Release
846          << reset << highint << " release!!!" << endl << endl << reset
847          << "This aircraft model may not even properly load, and probably"
848          << " will not fly as expected." << endl << endl
849          << fgred << highint << "Use this model for development purposes ONLY!!!"
850          << normint << reset << endl << endl;
851   }
852
853   return result;
854 }
855
856 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
857
858 bool FGFDMExec::ReadChild(Element* el)
859 {
860   // Add a new childData object to the child FDM list
861   // Populate that childData element with a new FDMExec object
862   // Set the IsChild flag for that FDMExec object
863   // Get the aircraft name
864   // set debug level to print out no additional data for child objects
865   // Load the model given the aircraft name
866   // reset debug level to prior setting
867
868   string token;
869
870   struct childData* child = new childData;
871
872   child->exec = new FGFDMExec(Root, FDMctr);
873   child->exec->SetChild(true);
874
875   string childAircraft = el->GetAttributeValue("name");
876   string sMated = el->GetAttributeValue("mated");
877   if (sMated == "false") child->mated = false; // child objects are mated by default.
878   string sInternal = el->GetAttributeValue("internal");
879   if (sInternal == "true") child->internal = true; // child objects are external by default.
880
881   child->exec->SetAircraftPath( AircraftPath );
882   child->exec->SetEnginePath( EnginePath );
883   child->exec->SetSystemsPath( SystemsPath );
884   child->exec->LoadModel(childAircraft);
885
886   Element* location = el->FindElement("location");
887   if (location) {
888     child->Loc = location->FindElementTripletConvertTo("IN");
889   } else {
890     cerr << endl << highint << fgred << "  No location was found for this child object!" << reset << endl;
891     exit(-1);
892   }
893   
894   Element* orientation = el->FindElement("orient");
895   if (orientation) {
896     child->Orient = orientation->FindElementTripletConvertTo("RAD");
897   } else if (debug_lvl > 0) {
898     cerr << endl << highint << "  No orientation was found for this child object! Assuming 0,0,0." << reset << endl;
899   }
900
901   ChildFDMList.push_back(child);
902
903   return true;
904 }
905
906 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
907
908 FGPropertyManager* FGFDMExec::GetPropertyManager(void)
909 {
910   return instance;
911 }
912
913 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
914
915 FGTrim* FGFDMExec::GetTrim(void)
916 {
917   delete Trim;
918   Trim = new FGTrim(this,tNone);
919   return Trim;
920 }
921
922 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
923
924 void FGFDMExec::DisableOutput(void)
925 {
926   for (unsigned i=0; i<Outputs.size(); i++) {
927     Outputs[i]->Disable();
928   }
929 }
930
931 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
932
933 void FGFDMExec::EnableOutput(void)
934 {
935   for (unsigned i=0; i<Outputs.size(); i++) {
936     Outputs[i]->Enable();
937   }
938 }
939
940 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
941
942 bool FGFDMExec::SetOutputDirectives(const string& fname)
943 {
944   bool result;
945
946   FGOutput* Output = new FGOutput(this);
947   Output->SetDirectivesFile(RootDir + fname);
948   Output->InitModel();
949   Schedule(Output, 1);
950   result = Output->Load(0);
951
952   if (result) {
953     Outputs.push_back(Output);
954     typedef int (FGOutput::*iOPMF)(void) const;
955     string outputProp = CreateIndexedPropertyName("simulation/output",Outputs.size()-1);
956     instance->Tie(outputProp+"/log_rate_hz", Output, (iOPMF)0, &FGOutput::SetRate);
957   }
958
959   return result;
960 }
961
962 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
963
964 void FGFDMExec::DoTrim(int mode)
965 {
966   double saved_time;
967
968   if (Constructing) return;
969
970   if (mode < 0 || mode > JSBSim::tNone) {
971     cerr << endl << "Illegal trimming mode!" << endl << endl;
972     return;
973   }
974   saved_time = sim_time;
975   FGTrim trim(this, (JSBSim::TrimMode)mode);
976   if ( !trim.DoTrim() ) cerr << endl << "Trim Failed" << endl << endl;
977   trim.Report();
978   sim_time = saved_time;
979 }
980
981 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
982 /*
983 void FGFDMExec::DoTrimAnalysis(int mode)
984 {
985   double saved_time;
986   if (Constructing) return;
987
988   if (mode < 0 || mode > JSBSim::taNone) {
989     cerr << endl << "Illegal trimming mode!" << endl << endl;
990     return;
991   }
992   saved_time = sim_time;
993
994   FGTrimAnalysis trimAnalysis(this, (JSBSim::TrimAnalysisMode)mode);
995
996   if ( !trimAnalysis.Load(IC->GetInitFile(), false) ) {
997     cerr << "A problem occurred with trim configuration file " << trimAnalysis.Load(IC->GetInitFile()) << endl;
998     exit(-1);
999   }
1000
1001   bool result = trimAnalysis.DoTrim();
1002
1003   if ( !result ) cerr << endl << "Trim Failed" << endl << endl;
1004
1005   trimAnalysis.Report();
1006   Setsim_time(saved_time);
1007
1008   EnableOutput();
1009   cout << "\nOutput: " << GetOutputFileName() << endl;
1010
1011 }
1012 */
1013 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1014
1015 void FGFDMExec::UseAtmosphereMSIS(void)
1016 {
1017   FGAtmosphere *oldAtmosphere = Atmosphere;
1018   Atmosphere = new MSIS(this);
1019   if (!Atmosphere->InitModel()) {
1020     cerr << fgred << "MSIS Atmosphere model init failed" << fgdef << endl;
1021     Error+=1;
1022   }
1023   delete oldAtmosphere;
1024 }
1025
1026 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1027
1028 void FGFDMExec::UseAtmosphereMars(void)
1029 {
1030 /*
1031   FGAtmosphere *oldAtmosphere = Atmosphere;
1032   Atmosphere = new FGMars(this);
1033   if (!Atmosphere->InitModel()) {
1034     cerr << fgred << "Mars Atmosphere model init failed" << fgdef << endl;
1035     Error+=1;
1036   }
1037   delete oldAtmosphere;
1038 */
1039 }
1040
1041 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1042 //    The bitmasked value choices are as follows:
1043 //    unset: In this case (the default) JSBSim would only print
1044 //       out the normally expected messages, essentially echoing
1045 //       the config files as they are read. If the environment
1046 //       variable is not set, debug_lvl is set to 1 internally
1047 //    0: This requests JSBSim not to output any messages
1048 //       whatsoever.
1049 //    1: This value explicity requests the normal JSBSim
1050 //       startup messages
1051 //    2: This value asks for a message to be printed out when
1052 //       a class is instantiated
1053 //    4: When this value is set, a message is displayed when a
1054 //       FGModel object executes its Run() method
1055 //    8: When this value is set, various runtime state variables
1056 //       are printed out periodically
1057 //    16: When set various parameters are sanity checked and
1058 //       a message is printed out when they go out of bounds
1059
1060 void FGFDMExec::Debug(int from)
1061 {
1062   if (debug_lvl <= 0) return;
1063
1064   if (debug_lvl & 1 && IdFDM == 0) { // Standard console startup message output
1065     if (from == 0) { // Constructor
1066       cout << "\n\n     " << highint << underon << "JSBSim Flight Dynamics Model v"
1067                                      << JSBSim_version << underoff << normint << endl;
1068       cout << halfint << "            [JSBSim-ML v" << needed_cfg_version << "]\n\n";
1069       cout << normint << "JSBSim startup beginning ...\n\n";
1070     } else if (from == 3) {
1071       cout << "\n\nJSBSim startup complete\n\n";
1072     }
1073   }
1074   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
1075     if (from == 0) cout << "Instantiated: FGFDMExec" << endl;
1076     if (from == 1) cout << "Destroyed:    FGFDMExec" << endl;
1077   }
1078   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
1079     if (from == 2) {
1080       cout << "================== Frame: " << Frame << "  Time: "
1081            << sim_time << " dt: " << dT << endl;
1082     }
1083   }
1084   if (debug_lvl & 8 ) { // Runtime state variables
1085   }
1086   if (debug_lvl & 16) { // Sanity checking
1087   }
1088   if (debug_lvl & 64) {
1089     if (from == 0) { // Constructor
1090       cout << IdSrc << endl;
1091       cout << IdHdr << endl;
1092     }
1093   }
1094 }
1095 }
1096
1097