]> git.mxchange.org Git - flightgear.git/blob - src/Main/fg_init.cxx
More route-manager functionality moved to Nasal.
[flightgear.git] / src / Main / fg_init.cxx
1 // fg_init.cxx -- Flight Gear top level initialization routines
2 //
3 // Written by Curtis Olson, started August 1997.
4 //
5 // Copyright (C) 1997  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>             // strcmp()
31
32 #if defined( unix ) || defined( __CYGWIN__ )
33 #  include <unistd.h>           // for gethostname()
34 #endif
35 #ifdef _WIN32
36 #  include <direct.h>           // for getcwd()
37 #  define getcwd _getcwd
38 #  include <io.h>               // isatty()
39 #  define isatty _isatty
40 #  include "winsock2.h"         // for gethostname()
41 #endif
42
43 // work around a stdc++ lib bug in some versions of linux, but doesn't
44 // seem to hurt to have this here for all versions of Linux.
45 #ifdef linux
46 #  define _G_NO_EXTERN_TEMPLATES
47 #endif
48
49 #include <simgear/compiler.h>
50
51 #include <string>
52 #include <boost/algorithm/string/compare.hpp>
53 #include <boost/algorithm/string/predicate.hpp>
54
55 #include <simgear/constants.h>
56 #include <simgear/debug/logstream.hxx>
57 #include <simgear/structure/exception.hxx>
58 #include <simgear/structure/event_mgr.hxx>
59 #include <simgear/structure/SGPerfMon.hxx>
60 #include <simgear/misc/sg_path.hxx>
61 #include <simgear/misc/sg_dir.hxx>
62 #include <simgear/misc/sgstream.hxx>
63 #include <simgear/misc/strutils.hxx>
64 #include <simgear/props/props_io.hxx>
65
66 #include <simgear/misc/interpolator.hxx>
67 #include <simgear/scene/material/matlib.hxx>
68 #include <simgear/scene/model/particles.hxx>
69 #include <simgear/sound/soundmgr_openal.hxx>
70
71 #include <Aircraft/controls.hxx>
72 #include <Aircraft/replay.hxx>
73 #include <Airports/apt_loader.hxx>
74 #include <Airports/runways.hxx>
75 #include <Airports/simple.hxx>
76 #include <Airports/dynamics.hxx>
77
78 #include <AIModel/AIManager.hxx>
79
80 #include <ATCDCL/ATISmgr.hxx>
81 #include <ATC/atc_mgr.hxx>
82
83 #include <Autopilot/route_mgr.hxx>
84 #include <Autopilot/autopilotgroup.hxx>
85
86 #include <Cockpit/panel.hxx>
87 #include <Cockpit/panel_io.hxx>
88
89 #include <GUI/new_gui.hxx>
90 #include <Input/input.hxx>
91 #include <Instrumentation/instrument_mgr.hxx>
92 #include <Model/acmodel.hxx>
93 #include <Model/modelmgr.hxx>
94 #include <AIModel/submodel.hxx>
95 #include <AIModel/AIManager.hxx>
96 #include <Navaids/navdb.hxx>
97 #include <Navaids/navlist.hxx>
98 #include <Navaids/fix.hxx>
99 #include <Navaids/fixlist.hxx>
100 #include <Navaids/airways.hxx>
101 #include <Scenery/scenery.hxx>
102 #include <Scenery/tilemgr.hxx>
103 #include <Scripting/NasalSys.hxx>
104 #include <Sound/voice.hxx>
105 #include <Systems/system_mgr.hxx>
106 #include <Time/light.hxx>
107 #include <Traffic/TrafficMgr.hxx>
108 #include <MultiPlayer/multiplaymgr.hxx>
109 #include <FDM/fdm_shell.hxx>
110 #include <Environment/ephemeris.hxx>
111 #include <Environment/environment_mgr.hxx>
112 #include <Viewer/renderer.hxx>
113 #include <Viewer/viewmgr.hxx>
114
115 #include "fg_init.hxx"
116 #include "fg_io.hxx"
117 #include "fg_commands.hxx"
118 #include "fg_props.hxx"
119 #include "options.hxx"
120 #include "globals.hxx"
121 #include "logger.hxx"
122 #include "main.hxx"
123
124
125 using std::string;
126 using namespace boost::algorithm;
127
128
129 // Return the current base package version
130 string fgBasePackageVersion() {
131     SGPath base_path( globals->get_fg_root() );
132     base_path.append("version");
133
134     sg_gzifstream in( base_path.str() );
135     if ( !in.is_open() ) {
136         SGPath old_path( globals->get_fg_root() );
137         old_path.append( "Thanks" );
138         sg_gzifstream old( old_path.str() );
139         if ( !old.is_open() ) {
140             return "[none]";
141         } else {
142             return "[old version]";
143         }
144     }
145
146     string version;
147     in >> version;
148
149     return version;
150 }
151
152
153 template <class T>
154 bool fgFindAircraftInDir(const SGPath& dirPath, T* obj, bool (T::*pred)(const SGPath& p))
155 {
156   if (!dirPath.exists()) {
157     SG_LOG(SG_GENERAL, SG_WARN, "fgFindAircraftInDir: no such path:" << dirPath.str());
158     return false;
159   }
160     
161   bool recurse = true;
162   simgear::Dir dir(dirPath);
163   simgear::PathList setFiles(dir.children(simgear::Dir::TYPE_FILE, "-set.xml"));
164   simgear::PathList::iterator p;
165   for (p = setFiles.begin(); p != setFiles.end(); ++p) {
166     // check file name ends with -set.xml
167     
168     // if we found a -set.xml at this level, don't recurse any deeper
169     recurse = false;
170     
171     bool done = (obj->*pred)(*p);
172     if (done) {
173       return true;
174     }
175   } // of -set.xml iteration
176   
177   if (!recurse) {
178     return false;
179   }
180   
181   simgear::PathList subdirs(dir.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT));
182   for (p = subdirs.begin(); p != subdirs.end(); ++p) {
183     if (p->file() == "CVS") {
184       continue;
185     }
186     
187     if (fgFindAircraftInDir(*p, obj, pred)) {
188       return true;
189     }
190   } // of subdirs iteration
191   
192   return false;
193 }
194
195 template <class T>
196 void fgFindAircraft(T* obj, bool (T::*pred)(const SGPath& p))
197 {
198   const string_list& paths(globals->get_aircraft_paths());
199   string_list::const_iterator it = paths.begin();
200   for (; it != paths.end(); ++it) {
201     bool done = fgFindAircraftInDir(SGPath(*it), obj, pred);
202     if (done) {
203       return;
204     }
205   } // of aircraft paths iteration
206   
207   // if we reach this point, search the default location (always last)
208   SGPath rootAircraft(globals->get_fg_root());
209   rootAircraft.append("Aircraft");
210   fgFindAircraftInDir(rootAircraft, obj, pred);
211 }
212
213 class FindAndCacheAircraft
214 {
215 public:
216   FindAndCacheAircraft(SGPropertyNode* autoSave)
217   {
218     _cache = autoSave->getNode("sim/startup/path-cache", true);
219   }
220   
221   bool loadAircraft()
222   {
223     std::string aircraft = fgGetString( "/sim/aircraft", "");
224     if (aircraft.empty()) {
225       SG_LOG(SG_GENERAL, SG_ALERT, "no aircraft specified");
226       return false;
227     }
228     
229     _searchAircraft = aircraft + "-set.xml";
230     std::string aircraftDir = fgGetString("/sim/aircraft-dir", "");
231     if (!aircraftDir.empty()) {
232       // aircraft-dir was set, skip any searching at all, if it's valid
233       simgear::Dir acPath(aircraftDir);
234       SGPath setFile = acPath.file(_searchAircraft);
235       if (setFile.exists()) {
236         SG_LOG(SG_GENERAL, SG_INFO, "found aircraft in dir: " << aircraftDir );
237         
238         try {
239           readProperties(setFile.str(), globals->get_props());
240         } catch ( const sg_exception &e ) {
241           SG_LOG(SG_INPUT, SG_ALERT, "Error reading aircraft: " << e.getFormattedMessage());
242           return false;
243         }
244         
245         return true;
246       } else {
247         SG_LOG(SG_GENERAL, SG_ALERT, "aircraft '" << _searchAircraft << 
248                "' not found in specified dir:" << aircraftDir);
249         return false;
250       }
251     }
252     
253     if (!checkCache()) {
254       // prepare cache for re-scan
255       SGPropertyNode *n = _cache->getNode("fg-root", true);
256       n->setStringValue(globals->get_fg_root().c_str());
257       n->setAttribute(SGPropertyNode::USERARCHIVE, true);
258       n = _cache->getNode("fg-aircraft", true);
259       n->setStringValue(getAircraftPaths().c_str());
260       n->setAttribute(SGPropertyNode::USERARCHIVE, true);
261       _cache->removeChildren("aircraft");
262   
263       fgFindAircraft(this, &FindAndCacheAircraft::checkAircraft);
264     }
265     
266     if (_foundPath.str().empty()) {
267       SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find specified aircraft: " << aircraft );
268       return false;
269     }
270     
271     SG_LOG(SG_GENERAL, SG_INFO, "Loading aircraft -set file from:" << _foundPath.str());
272     fgSetString( "/sim/aircraft-dir", _foundPath.dir().c_str());
273     if (!_foundPath.exists()) {
274       SG_LOG(SG_GENERAL, SG_ALERT, "Unable to find -set file:" << _foundPath.str());
275       return false;
276     }
277     
278     try {
279       readProperties(_foundPath.str(), globals->get_props());
280     } catch ( const sg_exception &e ) {
281       SG_LOG(SG_INPUT, SG_ALERT, "Error reading aircraft: " << e.getFormattedMessage());
282       return false;
283     }
284     
285     return true;
286   }
287   
288 private:
289   SGPath getAircraftPaths() {
290     string_list pathList = globals->get_aircraft_paths();
291     SGPath aircraftPaths;
292     string_list::const_iterator it = pathList.begin();
293     if (it != pathList.end()) {
294         aircraftPaths.set(*it);
295         it++;
296     }
297     for (; it != pathList.end(); ++it) {
298         aircraftPaths.add(*it);
299     }
300     return aircraftPaths;
301   }
302   
303   bool checkCache()
304   {
305     if (globals->get_fg_root() != _cache->getStringValue("fg-root", "")) {
306       return false; // cache mismatch
307     }
308
309     if (getAircraftPaths().str() != _cache->getStringValue("fg-aircraft", "")) {
310       return false; // cache mismatch
311     }
312     
313     vector<SGPropertyNode_ptr> cache = _cache->getChildren("aircraft");
314     for (unsigned int i = 0; i < cache.size(); i++) {
315       const char *name = cache[i]->getStringValue("file", "");
316       if (!boost::equals(_searchAircraft, name, is_iequal())) {
317         continue;
318       }
319       
320       SGPath xml(cache[i]->getStringValue("path", ""));
321       xml.append(name);
322       if (xml.exists()) {
323         _foundPath = xml;
324         return true;
325       } 
326       
327       return false;
328     } // of aircraft in cache iteration
329     
330     return false;
331   }
332   
333   bool checkAircraft(const SGPath& p)
334   {
335     // create cache node
336     int i = 0;
337     while (1) {
338         if (!_cache->getChild("aircraft", i++, false))
339             break;
340     }
341     
342     SGPropertyNode *n, *entry = _cache->getChild("aircraft", --i, true);
343
344     std::string fileName(p.file());
345     n = entry->getNode("file", true);
346     n->setStringValue(fileName);
347     n->setAttribute(SGPropertyNode::USERARCHIVE, true);
348
349     n = entry->getNode("path", true);
350     n->setStringValue(p.dir());
351     n->setAttribute(SGPropertyNode::USERARCHIVE, true);
352
353     if ( boost::equals(fileName, _searchAircraft.c_str(), is_iequal()) ) {
354         _foundPath = p;
355         return true;
356     }
357
358     return false;
359   }
360   
361   std::string _searchAircraft;
362   SGPath _foundPath;
363   SGPropertyNode* _cache;
364 };
365
366 #ifdef _WIN32
367 static SGPath platformDefaultDataPath()
368 {
369   char *envp = ::getenv( "APPDATA" );
370   SGPath config( envp );
371   config.append( "flightgear.org" );
372   return config;
373 }
374 #elif __APPLE__
375
376 #include <CoreServices/CoreServices.h>
377
378 static SGPath platformDefaultDataPath()
379 {
380   FSRef ref;
381   OSErr err = FSFindFolder(kUserDomain, kApplicationSupportFolderType, false, &ref);
382   if (err) {
383     return SGPath();
384   }
385   
386   unsigned char path[1024];
387   if (FSRefMakePath(&ref, path, 1024) != noErr) {
388     return SGPath();
389   }
390   
391   SGPath appData;
392   appData.set((const char*) path);
393   appData.append("FlightGear");
394   return appData;
395 }
396 #else
397 static SGPath platformDefaultDataPath()
398 {
399   SGPath config( homedir );
400   config.append( ".fgfs" );
401   return config;
402 }
403 #endif
404
405 // Read in configuration (file and command line)
406 bool fgInitConfig ( int argc, char **argv )
407 {
408     SGPath dataPath = platformDefaultDataPath();
409     
410     const char *fg_home = getenv("FG_HOME");
411     if (fg_home)
412       dataPath = fg_home;
413     
414     simgear::Dir exportDir(simgear::Dir(dataPath).file("Export"));
415     if (!exportDir.exists()) {
416       exportDir.create(0777);
417     }
418     
419     // Set /sim/fg-home and don't allow malign code to override it until
420     // Nasal security is set up.  Use FG_HOME if necessary.
421     SGPropertyNode *home = fgGetNode("/sim", true);
422     home->removeChild("fg-home", 0, false);
423     home = home->getChild("fg-home", 0, true);
424     home->setStringValue(dataPath.c_str());
425     home->setAttribute(SGPropertyNode::WRITE, false);
426   
427     flightgear::Options::sharedInstance()->init(argc, argv, dataPath);
428   
429     // Read global preferences from $FG_ROOT/preferences.xml
430     SG_LOG(SG_INPUT, SG_INFO, "Reading global preferences");
431     fgLoadProps("preferences.xml", globals->get_props());
432     SG_LOG(SG_INPUT, SG_INFO, "Finished Reading global preferences");
433
434     globals->loadUserSettings(dataPath);
435
436     // Scan user config files and command line for a specified aircraft.
437     flightgear::Options::sharedInstance()->initAircraft();
438
439     FindAndCacheAircraft f(globals->get_props());
440     if (!f.loadAircraft()) {
441       return false;
442     }
443
444     // parse options after loading aircraft to ensure any user
445     // overrides of defaults are honored.
446     flightgear::Options::sharedInstance()->processOptions();
447       
448     return true;
449 }
450
451 // Set current tower position lon/lat given an airport id
452 static bool fgSetTowerPosFromAirportID( const string& id) {
453     const FGAirport *a = fgFindAirportID( id);
454     if (a) {
455         SGGeod tower = a->getTowerLocation();
456         fgSetDouble("/sim/tower/longitude-deg",  tower.getLongitudeDeg());
457         fgSetDouble("/sim/tower/latitude-deg",  tower.getLatitudeDeg());
458         fgSetDouble("/sim/tower/altitude-ft", tower.getElevationFt());
459         return true;
460     } else {
461         return false;
462     }
463
464 }
465
466 struct FGTowerLocationListener : SGPropertyChangeListener {
467     void valueChanged(SGPropertyNode* node) {
468         string id(node->getStringValue());
469         if (fgGetBool("/sim/tower/auto-position",true))
470         {
471             // enforce using closest airport when auto-positioning is enabled 
472             const char* closest_airport = fgGetString("/sim/airport/closest-airport-id", "");
473             if (closest_airport && (id != closest_airport))
474             {
475                 id = closest_airport;
476                 node->setStringValue(id);
477             }
478         }
479         fgSetTowerPosFromAirportID(id);
480     }
481 };
482
483 struct FGClosestTowerLocationListener : SGPropertyChangeListener
484 {
485     void valueChanged(SGPropertyNode* )
486     {
487         // closest airport has changed
488         if (fgGetBool("/sim/tower/auto-position",true))
489         {
490             // update tower position
491             const char* id = fgGetString("/sim/airport/closest-airport-id", "");
492             if (id && *id!=0)
493                 fgSetString("/sim/tower/airport-id", id);
494         }
495     }
496 };
497
498 void fgInitTowerLocationListener() {
499     fgGetNode("/sim/tower/airport-id",  true)
500         ->addChangeListener( new FGTowerLocationListener(), true );
501     FGClosestTowerLocationListener* ntcl = new FGClosestTowerLocationListener();
502     fgGetNode("/sim/airport/closest-airport-id", true)
503         ->addChangeListener(ntcl , true );
504     fgGetNode("/sim/tower/auto-position", true)
505            ->addChangeListener(ntcl, true );
506 }
507
508 static void fgApplyStartOffset(const SGGeod& aStartPos, double aHeading, double aTargetHeading = HUGE_VAL)
509 {
510   SGGeod startPos(aStartPos);
511   if (aTargetHeading == HUGE_VAL) {
512     aTargetHeading = aHeading;
513   }
514   
515   if ( fabs( fgGetDouble("/sim/presets/offset-distance-nm") ) > SG_EPSILON ) {
516     double offsetDistance = fgGetDouble("/sim/presets/offset-distance-nm");
517     offsetDistance *= SG_NM_TO_METER;
518     double offsetAzimuth = aHeading;
519     if ( fabs(fgGetDouble("/sim/presets/offset-azimuth-deg")) > SG_EPSILON ) {
520       offsetAzimuth = fgGetDouble("/sim/presets/offset-azimuth-deg");
521       aHeading = aTargetHeading;
522     }
523
524     SGGeod offset;
525     double az2; // dummy
526     SGGeodesy::direct(startPos, offsetAzimuth + 180, offsetDistance, offset, az2);
527     startPos = offset;
528   }
529
530   // presets
531   fgSetDouble("/sim/presets/longitude-deg", startPos.getLongitudeDeg() );
532   fgSetDouble("/sim/presets/latitude-deg", startPos.getLatitudeDeg() );
533   fgSetDouble("/sim/presets/heading-deg", aHeading );
534
535   // other code depends on the actual values being set ...
536   fgSetDouble("/position/longitude-deg",  startPos.getLongitudeDeg() );
537   fgSetDouble("/position/latitude-deg",  startPos.getLatitudeDeg() );
538   fgSetDouble("/orientation/heading-deg", aHeading );
539 }
540
541 // Set current_options lon/lat given an airport id and heading (degrees)
542 bool fgSetPosFromAirportIDandHdg( const string& id, double tgt_hdg ) {
543     if ( id.empty() )
544         return false;
545
546     // set initial position from runway and heading
547     SG_LOG( SG_GENERAL, SG_INFO,
548             "Attempting to set starting position from airport code "
549             << id << " heading " << tgt_hdg );
550
551     const FGAirport* apt = fgFindAirportID(id);
552     if (!apt) return false;
553     FGRunway* r = apt->findBestRunwayForHeading(tgt_hdg);
554     fgSetString("/sim/atc/runway", r->ident().c_str());
555
556     SGGeod startPos = r->pointOnCenterline(fgGetDouble("/sim/airport/runways/start-offset-m", 5.0));
557           fgApplyStartOffset(startPos, r->headingDeg(), tgt_hdg);
558     return true;
559 }
560
561 // Set current_options lon/lat given an airport id and parkig position name
562 static bool fgSetPosFromAirportIDandParkpos( const string& id, const string& parkpos ) {
563     if ( id.empty() )
564         return false;
565
566     // can't see an easy way around this const_cast at the moment
567     FGAirport* apt = const_cast<FGAirport*>(fgFindAirportID(id));
568     if (!apt) {
569         SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find airport " << id );
570         return false;
571     }
572     FGAirportDynamics* dcs = apt->getDynamics();
573     if (!dcs) {
574         SG_LOG( SG_GENERAL, SG_ALERT,
575                 "Airport " << id << "does not appear to have parking information available");
576         return false;
577     }
578     
579     int park_index = dcs->getNrOfParkings() - 1;
580     bool succes;
581     double radius = fgGetDouble("/sim/dimensions/radius-m");
582     if ((parkpos == string("AVAILABLE")) && (radius > 0)) {
583         double lat, lon, heading;
584         string fltType;
585         string acOperator;
586         SGPath acData;
587         try {
588             acData = fgGetString("/sim/fg-home");
589             acData.append("aircraft-data");
590             string acfile = fgGetString("/sim/aircraft") + string(".xml");
591             acData.append(acfile);
592             SGPropertyNode root;
593             readProperties(acData.str(), &root);
594             SGPropertyNode * node = root.getNode("sim");
595             fltType    = node->getStringValue("aircraft-class", "NONE"     );
596             acOperator = node->getStringValue("aircraft-operator", "NONE"     );
597         } catch (const sg_exception &) {
598             SG_LOG(SG_GENERAL, SG_INFO,
599                 "Could not load aircraft aircrat type and operator information from: " << acData.str() << ". Using defaults");
600
601        // cout << path.str() << endl;
602         }
603         if (fltType.empty() || fltType == "NONE") {
604             SG_LOG(SG_GENERAL, SG_INFO,
605                 "Aircraft type information not found in: " << acData.str() << ". Using default value");
606                 fltType = fgGetString("/sim/aircraft-class"   );
607         }
608         if (acOperator.empty() || fltType == "NONE") {
609             SG_LOG(SG_GENERAL, SG_INFO,
610                 "Aircraft operator information not found in: " << acData.str() << ". Using default value");
611                 acOperator = fgGetString("/sim/aircraft-operator"   );
612         }
613
614         cerr << "Running aircraft " << fltType << " of livery " << acOperator << endl;
615         string acType; // Currently not used by findAvailable parking, so safe to leave empty. 
616         succes = dcs->getAvailableParking(&lat, &lon, &heading, &park_index, radius, fltType, acType, acOperator);
617         if (succes) {
618             fgGetString("/sim/presets/parkpos");
619             fgSetString("/sim/presets/parkpos", dcs->getParking(park_index)->getName());
620         } else {
621             SG_LOG( SG_GENERAL, SG_ALERT,
622                     "Failed to find a suitable parking at airport " << id );
623             return false;
624         }
625     } else {
626         //cerr << "We shouldn't get here when AVAILABLE" << endl;
627         while (park_index >= 0 && dcs->getParkingName(park_index) != parkpos) park_index--;
628         if (park_index < 0) {
629             SG_LOG( SG_GENERAL, SG_ALERT,
630                     "Failed to find parking position " << parkpos <<
631                     " at airport " << id );
632             return false;
633         }
634     }
635     FGParking* parking = dcs->getParking(park_index);
636     parking->setAvailable(false);
637     fgApplyStartOffset(
638       SGGeod::fromDeg(parking->getLongitude(), parking->getLatitude()),
639       parking->getHeading());
640     return true;
641 }
642
643
644 // Set current_options lon/lat given an airport id and runway number
645 static bool fgSetPosFromAirportIDandRwy( const string& id, const string& rwy, bool rwy_req ) {
646     if ( id.empty() )
647         return false;
648
649     // set initial position from airport and runway number
650     SG_LOG( SG_GENERAL, SG_INFO,
651             "Attempting to set starting position for "
652             << id << ":" << rwy );
653
654     const FGAirport* apt = fgFindAirportID(id);
655     if (!apt) {
656       SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find airport:" << id);
657       return false;
658     }
659     
660     if (!apt->hasRunwayWithIdent(rwy)) {
661       SG_LOG( SG_GENERAL, rwy_req ? SG_ALERT : SG_INFO,
662                 "Failed to find runway " << rwy <<
663                 " at airport " << id << ". Using default runway." );
664       return false;
665     }
666     
667     FGRunway* r(apt->getRunwayByIdent(rwy));
668     fgSetString("/sim/atc/runway", r->ident().c_str());
669     SGGeod startPos = r->pointOnCenterline( fgGetDouble("/sim/airport/runways/start-offset-m", 5.0));
670           fgApplyStartOffset(startPos, r->headingDeg());
671     return true;
672 }
673
674
675 static void fgSetDistOrAltFromGlideSlope() {
676     // cout << "fgSetDistOrAltFromGlideSlope()" << endl;
677     string apt_id = fgGetString("/sim/presets/airport-id");
678     double gs = fgGetDouble("/sim/presets/glideslope-deg")
679         * SG_DEGREES_TO_RADIANS ;
680     double od = fgGetDouble("/sim/presets/offset-distance-nm");
681     double alt = fgGetDouble("/sim/presets/altitude-ft");
682
683     double apt_elev = 0.0;
684     if ( ! apt_id.empty() ) {
685         apt_elev = fgGetAirportElev( apt_id );
686         if ( apt_elev < -9990.0 ) {
687             apt_elev = 0.0;
688         }
689     } else {
690         apt_elev = 0.0;
691     }
692
693     if( fabs(gs) > 0.01 && fabs(od) > 0.1 && alt < -9990 ) {
694         // set altitude from glideslope and offset-distance
695         od *= SG_NM_TO_METER * SG_METER_TO_FEET;
696         alt = fabs(od*tan(gs)) + apt_elev;
697         fgSetDouble("/sim/presets/altitude-ft", alt);
698         fgSetBool("/sim/presets/onground", false);
699         SG_LOG( SG_GENERAL, SG_INFO, "Calculated altitude as: "
700                 << alt  << " ft" );
701     } else if( fabs(gs) > 0.01 && alt > 0 && fabs(od) < 0.1) {
702         // set offset-distance from glideslope and altitude
703         od  = (alt - apt_elev) / tan(gs);
704         od *= -1*SG_FEET_TO_METER * SG_METER_TO_NM;
705         fgSetDouble("/sim/presets/offset-distance-nm", od);
706         fgSetBool("/sim/presets/onground", false);
707         SG_LOG( SG_GENERAL, SG_INFO, "Calculated offset distance as: " 
708                 << od  << " nm" );
709     } else if( fabs(gs) > 0.01 ) {
710         SG_LOG( SG_GENERAL, SG_ALERT,
711                 "Glideslope given but not altitude or offset-distance." );
712         SG_LOG( SG_GENERAL, SG_ALERT, "Resetting glideslope to zero" );
713         fgSetDouble("/sim/presets/glideslope-deg", 0);
714         fgSetBool("/sim/presets/onground", true);
715     }
716 }
717
718
719 // Set current_options lon/lat given an airport id and heading (degrees)
720 static bool fgSetPosFromNAV( const string& id, const double& freq, FGPositioned::Type type ) {
721
722     const nav_list_type navlist
723         = globals->get_navlist()->findByIdentAndFreq( id.c_str(), freq, type );
724
725     if (navlist.size() == 0 ) {
726         SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate NAV = "
727             << id << ":" << freq );
728         return false;
729     }
730
731     if( navlist.size() > 1 ) {
732         ostringstream buf;
733         buf << "Ambigous NAV-ID: '" << id << "'. Specify id and frequency. Available stations:" << endl;
734         for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
735             // NDB stored in kHz, VOR stored in MHz * 100 :-P
736             double factor = (*it)->type() == FGPositioned::NDB ? 1.0 : 1/100.0;
737             string unit = (*it)->type() == FGPositioned::NDB ? "kHz" : "MHz";
738             buf << (*it)->ident() << " "
739                 << setprecision(5) << (double)((*it)->get_freq() * factor) << " "
740                 << (*it)->get_lat() << "/" << (*it)->get_lon()
741                 << endl;
742         }
743
744         SG_LOG( SG_GENERAL, SG_ALERT, buf.str() );
745         return false;
746     }
747
748     FGNavRecord *nav = navlist[0];
749     fgApplyStartOffset(nav->geod(), fgGetDouble("/sim/presets/heading-deg"));
750     return true;
751 }
752
753 // Set current_options lon/lat given an aircraft carrier id
754 static bool fgSetPosFromCarrier( const string& carrier, const string& posid ) {
755
756     // set initial position from runway and heading
757     SGGeod geodPos;
758     double heading;
759     SGVec3d uvw;
760     if (FGAIManager::getStartPosition(carrier, posid, geodPos, heading, uvw)) {
761         double lon = geodPos.getLongitudeDeg();
762         double lat = geodPos.getLatitudeDeg();
763         double alt = geodPos.getElevationFt();
764
765         SG_LOG( SG_GENERAL, SG_INFO, "Attempting to set starting position for "
766                 << carrier << " at lat = " << lat << ", lon = " << lon
767                 << ", alt = " << alt << ", heading = " << heading);
768
769         fgSetDouble("/sim/presets/longitude-deg",  lon);
770         fgSetDouble("/sim/presets/latitude-deg",  lat);
771         fgSetDouble("/sim/presets/altitude-ft", alt);
772         fgSetDouble("/sim/presets/heading-deg", heading);
773         fgSetDouble("/position/longitude-deg",  lon);
774         fgSetDouble("/position/latitude-deg",  lat);
775         fgSetDouble("/position/altitude-ft", alt);
776         fgSetDouble("/orientation/heading-deg", heading);
777
778         fgSetString("/sim/presets/speed-set", "UVW");
779         fgSetDouble("/velocities/uBody-fps", uvw(0));
780         fgSetDouble("/velocities/vBody-fps", uvw(1));
781         fgSetDouble("/velocities/wBody-fps", uvw(2));
782         fgSetDouble("/sim/presets/uBody-fps", uvw(0));
783         fgSetDouble("/sim/presets/vBody-fps", uvw(1));
784         fgSetDouble("/sim/presets/wBody-fps", uvw(2));
785
786         fgSetBool("/sim/presets/onground", true);
787
788         return true;
789     } else {
790         SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate aircraft carrier = "
791                 << carrier );
792         return false;
793     }
794 }
795  
796 // Set current_options lon/lat given an airport id and heading (degrees)
797 static bool fgSetPosFromFix( const string& id )
798 {
799   FGPositioned::TypeFilter fixFilter(FGPositioned::FIX);
800   FGPositioned* fix = FGPositioned::findNextWithPartialId(NULL, id, &fixFilter);
801   if (!fix) {
802     SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate fix = " << id );
803     return false;
804   }
805   
806   fgApplyStartOffset(fix->geod(), fgGetDouble("/sim/presets/heading-deg"));
807   return true;
808 }
809
810 /**
811  * Initialize vor/ndb/ils/fix list management and query systems (as
812  * well as simple airport db list)
813  */
814 bool
815 fgInitNav ()
816 {
817     SG_LOG(SG_GENERAL, SG_INFO, "Loading Airport Database ...");
818
819     SGPath aptdb( globals->get_fg_root() );
820     aptdb.append( "Airports/apt.dat" );
821
822     SGPath p_metar( globals->get_fg_root() );
823     p_metar.append( "Airports/metar.dat" );
824
825     fgAirportDBLoad( aptdb.str(), p_metar.str() );
826     
827     FGNavList *navlist = new FGNavList;
828     FGNavList *loclist = new FGNavList;
829     FGNavList *gslist = new FGNavList;
830     FGNavList *dmelist = new FGNavList;
831     FGNavList *tacanlist = new FGNavList;
832     FGNavList *carrierlist = new FGNavList;
833     FGTACANList *channellist = new FGTACANList;
834
835     globals->set_navlist( navlist );
836     globals->set_loclist( loclist );
837     globals->set_gslist( gslist );
838     globals->set_dmelist( dmelist );
839     globals->set_tacanlist( tacanlist );
840     globals->set_carrierlist( carrierlist );
841     globals->set_channellist( channellist );
842
843     if ( !fgNavDBInit(navlist, loclist, gslist, dmelist, tacanlist, carrierlist, channellist) ) {
844         SG_LOG( SG_GENERAL, SG_ALERT,
845                 "Problems loading one or more navigational database" );
846     }
847     
848     SG_LOG(SG_GENERAL, SG_INFO, "  Fixes");
849     SGPath p_fix( globals->get_fg_root() );
850     p_fix.append( "Navaids/fix.dat" );
851     FGFixList fixlist;
852     fixlist.init( p_fix );  // adds fixes to the DB in positioned.cxx
853
854     SG_LOG(SG_GENERAL, SG_INFO, "  Airways");
855     flightgear::Airway::load();
856     
857     return true;
858 }
859
860
861 // Set the initial position based on presets (or defaults)
862 bool fgInitPosition() {
863     // cout << "fgInitPosition()" << endl;
864     double gs = fgGetDouble("/sim/presets/glideslope-deg")
865         * SG_DEGREES_TO_RADIANS ;
866     double od = fgGetDouble("/sim/presets/offset-distance-nm");
867     double alt = fgGetDouble("/sim/presets/altitude-ft");
868
869     bool set_pos = false;
870
871     // If glideslope is specified, then calculate offset-distance or
872     // altitude relative to glide slope if either of those was not
873     // specified.
874     if ( fabs( gs ) > 0.01 ) {
875         fgSetDistOrAltFromGlideSlope();
876     }
877
878
879     // If we have an explicit, in-range lon/lat, don't change it, just use it.
880     // If not, check for an airport-id and use that.
881     // If not, default to the middle of the KSFO field.
882     // The default values for lon/lat are deliberately out of range
883     // so that the airport-id can take effect; valid lon/lat will
884     // override airport-id, however.
885     double lon_deg = fgGetDouble("/sim/presets/longitude-deg");
886     double lat_deg = fgGetDouble("/sim/presets/latitude-deg");
887     if ( lon_deg >= -180.0 && lon_deg <= 180.0
888          && lat_deg >= -90.0 && lat_deg <= 90.0 )
889     {
890         set_pos = true;
891     }
892
893     string apt = fgGetString("/sim/presets/airport-id");
894     string rwy_no = fgGetString("/sim/presets/runway");
895     bool rwy_req = fgGetBool("/sim/presets/runway-requested");
896     string vor = fgGetString("/sim/presets/vor-id");
897     double vor_freq = fgGetDouble("/sim/presets/vor-freq");
898     string ndb = fgGetString("/sim/presets/ndb-id");
899     double ndb_freq = fgGetDouble("/sim/presets/ndb-freq");
900     string carrier = fgGetString("/sim/presets/carrier");
901     string parkpos = fgGetString("/sim/presets/parkpos");
902     string fix = fgGetString("/sim/presets/fix");
903     SGPropertyNode *hdg_preset = fgGetNode("/sim/presets/heading-deg", true);
904     double hdg = hdg_preset->getDoubleValue();
905
906     // save some start parameters, so that we can later say what the
907     // user really requested. TODO generalize that and move it to options.cxx
908     static bool start_options_saved = false;
909     if (!start_options_saved) {
910         start_options_saved = true;
911         SGPropertyNode *opt = fgGetNode("/sim/startup/options", true);
912
913         opt->setDoubleValue("latitude-deg", lat_deg);
914         opt->setDoubleValue("longitude-deg", lon_deg);
915         opt->setDoubleValue("heading-deg", hdg);
916         opt->setStringValue("airport", apt.c_str());
917         opt->setStringValue("runway", rwy_no.c_str());
918     }
919
920     if (hdg > 9990.0)
921         hdg = fgGetDouble("/environment/config/boundary/entry/wind-from-heading-deg", 270);
922
923     if ( !set_pos && !apt.empty() && !parkpos.empty() ) {
924         // An airport + parking position is requested
925         if ( fgSetPosFromAirportIDandParkpos( apt, parkpos ) ) {
926             // set tower position
927             fgSetString("/sim/airport/closest-airport-id",  apt.c_str());
928             fgSetString("/sim/tower/airport-id",  apt.c_str());
929             set_pos = true;
930         }
931     }
932
933     if ( !set_pos && !apt.empty() && !rwy_no.empty() ) {
934         // An airport + runway is requested
935         if ( fgSetPosFromAirportIDandRwy( apt, rwy_no, rwy_req ) ) {
936             // set tower position (a little off the heading for single
937             // runway airports)
938             fgSetString("/sim/airport/closest-airport-id",  apt.c_str());
939             fgSetString("/sim/tower/airport-id",  apt.c_str());
940             set_pos = true;
941         }
942     }
943
944     if ( !set_pos && !apt.empty() ) {
945         // An airport is requested (find runway closest to hdg)
946         if ( fgSetPosFromAirportIDandHdg( apt, hdg ) ) {
947             // set tower position (a little off the heading for single
948             // runway airports)
949             fgSetString("/sim/airport/closest-airport-id",  apt.c_str());
950             fgSetString("/sim/tower/airport-id",  apt.c_str());
951             set_pos = true;
952         }
953     }
954
955     if (hdg_preset->getDoubleValue() > 9990.0)
956         hdg_preset->setDoubleValue(hdg);
957
958     if ( !set_pos && !vor.empty() ) {
959         // a VOR is requested
960         if ( fgSetPosFromNAV( vor, vor_freq, FGPositioned::VOR ) ) {
961             set_pos = true;
962         }
963     }
964
965     if ( !set_pos && !ndb.empty() ) {
966         // an NDB is requested
967         if ( fgSetPosFromNAV( ndb, ndb_freq, FGPositioned::NDB ) ) {
968             set_pos = true;
969         }
970     }
971
972     if ( !set_pos && !carrier.empty() ) {
973         // an aircraft carrier is requested
974         if ( fgSetPosFromCarrier( carrier, parkpos ) ) {
975             set_pos = true;
976         }
977     }
978
979     if ( !set_pos && !fix.empty() ) {
980         // a Fix is requested
981         if ( fgSetPosFromFix( fix ) ) {
982             set_pos = true;
983         }
984     }
985
986     if ( !set_pos ) {
987         // No lon/lat specified, no airport specified, default to
988         // middle of KSFO field.
989         fgSetDouble("/sim/presets/longitude-deg", -122.374843);
990         fgSetDouble("/sim/presets/latitude-deg", 37.619002);
991     }
992
993     fgSetDouble( "/position/longitude-deg",
994                  fgGetDouble("/sim/presets/longitude-deg") );
995     fgSetDouble( "/position/latitude-deg",
996                  fgGetDouble("/sim/presets/latitude-deg") );
997     fgSetDouble( "/orientation/heading-deg", hdg_preset->getDoubleValue());
998
999     // determine if this should be an on-ground or in-air start
1000     if ((fabs(gs) > 0.01 || fabs(od) > 0.1 || alt > 0.1) && carrier.empty()) {
1001         fgSetBool("/sim/presets/onground", false);
1002     } else {
1003         fgSetBool("/sim/presets/onground", true);
1004     }
1005
1006     return true;
1007 }
1008
1009
1010 // General house keeping initializations
1011 bool fgInitGeneral() {
1012     string root;
1013
1014     SG_LOG( SG_GENERAL, SG_INFO, "General Initialization" );
1015     SG_LOG( SG_GENERAL, SG_INFO, "======= ==============" );
1016
1017     root = globals->get_fg_root();
1018     if ( ! root.length() ) {
1019         // No root path set? Then bail ...
1020         SG_LOG( SG_GENERAL, SG_ALERT,
1021                 "Cannot continue without a path to the base package "
1022                 << "being defined." );
1023         exit(-1);
1024     }
1025     SG_LOG( SG_GENERAL, SG_INFO, "FG_ROOT = " << '"' << root << '"' << endl );
1026
1027     globals->set_browser(fgGetString("/sim/startup/browser-app", "firefox %u"));
1028
1029     char buf[512], *cwd = getcwd(buf, 511);
1030     buf[511] = '\0';
1031     SGPropertyNode *curr = fgGetNode("/sim", true);
1032     curr->removeChild("fg-current", 0, false);
1033     curr = curr->getChild("fg-current", 0, true);
1034     curr->setStringValue(cwd ? cwd : "");
1035     curr->setAttribute(SGPropertyNode::WRITE, false);
1036
1037     fgSetBool("/sim/startup/stdout-to-terminal", isatty(1) != 0 );
1038     fgSetBool("/sim/startup/stderr-to-terminal", isatty(2) != 0 );
1039     return true;
1040 }
1041
1042 // This is the top level init routine which calls all the other
1043 // initialization routines.  If you are adding a subsystem to flight
1044 // gear, its initialization call should located in this routine.
1045 // Returns non-zero if a problem encountered.
1046 bool fgInitSubsystems() {
1047
1048     SG_LOG( SG_GENERAL, SG_INFO, "Initialize Subsystems");
1049     SG_LOG( SG_GENERAL, SG_INFO, "========== ==========");
1050
1051     ////////////////////////////////////////////////////////////////////
1052     // Initialize the sound subsystem.
1053     ////////////////////////////////////////////////////////////////////
1054     // Sound manager uses an own subsystem group "SOUND" which is the last
1055     // to be updated in every loop.
1056     // Sound manager is updated last so it can use the CPU while the GPU
1057     // is processing the scenery (doubled the frame-rate for me) -EMH-
1058     globals->add_subsystem("sound", new SGSoundMgr, SGSubsystemMgr::SOUND);
1059
1060     ////////////////////////////////////////////////////////////////////
1061     // Initialize the event manager subsystem.
1062     ////////////////////////////////////////////////////////////////////
1063
1064     globals->get_event_mgr()->init();
1065     globals->get_event_mgr()->setRealtimeProperty(fgGetNode("/sim/time/delta-realtime-sec", true));
1066
1067     ////////////////////////////////////////////////////////////////////
1068     // Initialize the property interpolator subsystem. Put into the INIT
1069     // group because the "nasal" subsystem may need it at GENERAL take-down.
1070     ////////////////////////////////////////////////////////////////////
1071     globals->add_subsystem("interpolator", new SGInterpolator, SGSubsystemMgr::INIT);
1072
1073
1074     ////////////////////////////////////////////////////////////////////
1075     // Add the FlightGear property utilities.
1076     ////////////////////////////////////////////////////////////////////
1077     globals->add_subsystem("properties", new FGProperties);
1078
1079
1080     ////////////////////////////////////////////////////////////////////
1081     // Add the performance monitoring system.
1082     ////////////////////////////////////////////////////////////////////
1083     globals->add_subsystem("performance-mon",
1084             new SGPerformanceMonitor(globals->get_subsystem_mgr(),
1085                                      fgGetNode("/sim/performance-monitor", true)));
1086
1087     ////////////////////////////////////////////////////////////////////
1088     // Initialize the material property subsystem.
1089     ////////////////////////////////////////////////////////////////////
1090
1091     SGPath mpath( globals->get_fg_root() );
1092     mpath.append( fgGetString("/sim/rendering/materials-file") );
1093     if ( ! globals->get_matlib()->load(globals->get_fg_root(), mpath.str(),
1094             globals->get_props()) ) {
1095         SG_LOG( SG_GENERAL, SG_ALERT,
1096                 "Error loading materials file " << mpath.str() );
1097         exit(-1);
1098     }
1099
1100
1101     ////////////////////////////////////////////////////////////////////
1102     // Initialize the scenery management subsystem.
1103     ////////////////////////////////////////////////////////////////////
1104
1105     globals->get_scenery()->get_scene_graph()
1106         ->addChild(simgear::Particles::getCommonRoot());
1107     simgear::GlobalParticleCallback::setSwitch(fgGetNode("/sim/rendering/particles", true));
1108
1109     ////////////////////////////////////////////////////////////////////
1110     // Initialize the flight model subsystem.
1111     ////////////////////////////////////////////////////////////////////
1112
1113     globals->add_subsystem("flight", new FDMShell, SGSubsystemMgr::FDM);
1114
1115     ////////////////////////////////////////////////////////////////////
1116     // Initialize the weather subsystem.
1117     ////////////////////////////////////////////////////////////////////
1118
1119     // Initialize the weather modeling subsystem
1120     globals->add_subsystem("environment", new FGEnvironmentMgr);
1121     globals->add_subsystem("ephemeris", new Ephemeris);
1122     
1123     ////////////////////////////////////////////////////////////////////
1124     // Initialize the aircraft systems and instrumentation (before the
1125     // autopilot.)
1126     ////////////////////////////////////////////////////////////////////
1127
1128     globals->add_subsystem("instrumentation", new FGInstrumentMgr, SGSubsystemMgr::FDM);
1129     globals->add_subsystem("systems", new FGSystemMgr, SGSubsystemMgr::FDM);
1130
1131     ////////////////////////////////////////////////////////////////////
1132     // Initialize the XML Autopilot subsystem.
1133     ////////////////////////////////////////////////////////////////////
1134
1135     globals->add_subsystem( "xml-autopilot", FGXMLAutopilotGroup::createInstance("autopilot"), SGSubsystemMgr::FDM );
1136     globals->add_subsystem( "xml-proprules", FGXMLAutopilotGroup::createInstance("property-rule"), SGSubsystemMgr::GENERAL );
1137     globals->add_subsystem( "route-manager", new FGRouteMgr );
1138
1139     ////////////////////////////////////////////////////////////////////
1140     // Initialize the Input-Output subsystem
1141     ////////////////////////////////////////////////////////////////////
1142     globals->add_subsystem( "io", new FGIO );
1143
1144     ////////////////////////////////////////////////////////////////////
1145     // Create and register the logger.
1146     ////////////////////////////////////////////////////////////////////
1147     
1148     globals->add_subsystem("logger", new FGLogger);
1149
1150     ////////////////////////////////////////////////////////////////////
1151     // Create and register the XML GUI.
1152     ////////////////////////////////////////////////////////////////////
1153
1154     globals->add_subsystem("gui", new NewGUI, SGSubsystemMgr::INIT);
1155
1156     //////////////////////////////////////////////////////////////////////
1157     // Initialize the 2D cloud subsystem.
1158     ////////////////////////////////////////////////////////////////////
1159     fgGetBool("/sim/rendering/bump-mapping", false);
1160
1161     ////////////////////////////////////////////////////////////////////
1162     // Initialise the ATIS Manager
1163     // Note that this is old stuff, but is necessary for the
1164     // current ATIS implementation. Therefore, leave it in here
1165     // until the ATIS system is ported over to make use of the ATIS 
1166     // sub system infrastructure.
1167     ////////////////////////////////////////////////////////////////////
1168
1169     globals->add_subsystem("ATIS", new FGATISMgr, SGSubsystemMgr::INIT, 0.4);
1170
1171     ////////////////////////////////////////////////////////////////////
1172    // Initialize the ATC subsystem
1173     ////////////////////////////////////////////////////////////////////
1174     globals->add_subsystem("ATC", new FGATCManager, SGSubsystemMgr::POST_FDM);
1175
1176     ////////////////////////////////////////////////////////////////////
1177     // Initialize multiplayer subsystem
1178     ////////////////////////////////////////////////////////////////////
1179
1180     globals->add_subsystem("mp", new FGMultiplayMgr, SGSubsystemMgr::POST_FDM);
1181
1182     ////////////////////////////////////////////////////////////////////
1183     // Initialise the AI Model Manager
1184     ////////////////////////////////////////////////////////////////////
1185     SG_LOG(SG_GENERAL, SG_INFO, "  AI Model Manager");
1186     globals->add_subsystem("ai-model", new FGAIManager, SGSubsystemMgr::POST_FDM);
1187     globals->add_subsystem("submodel-mgr", new FGSubmodelMgr, SGSubsystemMgr::POST_FDM);
1188
1189
1190     // It's probably a good idea to initialize the top level traffic manager
1191     // After the AI and ATC systems have been initialized properly.
1192     // AI Traffic manager
1193     globals->add_subsystem("traffic-manager", new FGTrafficManager, SGSubsystemMgr::POST_FDM);
1194
1195     ////////////////////////////////////////////////////////////////////
1196     // Add a new 2D panel.
1197     ////////////////////////////////////////////////////////////////////
1198
1199     string panel_path(fgGetString("/sim/panel/path"));
1200     if (!panel_path.empty()) {
1201       FGPanel* p = fgReadPanel(panel_path);
1202       if (p) {
1203         globals->set_current_panel(p);
1204         p->init();
1205         p->bind();
1206         SG_LOG( SG_INPUT, SG_INFO, "Loaded new panel from " << panel_path );
1207       } else {
1208         SG_LOG( SG_INPUT, SG_ALERT,
1209                 "Error reading new panel from " << panel_path );
1210       }
1211     }
1212
1213     ////////////////////////////////////////////////////////////////////
1214     // Initialize the controls subsystem.
1215     ////////////////////////////////////////////////////////////////////
1216
1217     globals->get_controls()->init();
1218     globals->get_controls()->bind();
1219
1220
1221     ////////////////////////////////////////////////////////////////////
1222     // Initialize the input subsystem.
1223     ////////////////////////////////////////////////////////////////////
1224
1225     globals->add_subsystem("input", new FGInput);
1226
1227
1228     ////////////////////////////////////////////////////////////////////
1229     // Initialize the replay subsystem
1230     ////////////////////////////////////////////////////////////////////
1231     globals->add_subsystem("replay", new FGReplay);
1232
1233 #ifdef ENABLE_AUDIO_SUPPORT
1234     ////////////////////////////////////////////////////////////////////
1235     // Initialize the sound-effects subsystem.
1236     ////////////////////////////////////////////////////////////////////
1237     globals->add_subsystem("voice", new FGVoiceMgr, SGSubsystemMgr::DISPLAY);
1238 #endif
1239
1240     ////////////////////////////////////////////////////////////////////
1241     // Initialize the lighting subsystem.
1242     ////////////////////////////////////////////////////////////////////
1243
1244     globals->add_subsystem("lighting", new FGLight, SGSubsystemMgr::DISPLAY);
1245     
1246     // ordering here is important : Nasal (via events), then models, then views
1247     globals->add_subsystem("events", globals->get_event_mgr(), SGSubsystemMgr::DISPLAY);
1248     
1249     FGAircraftModel* acm = new FGAircraftModel;
1250     globals->set_aircraft_model(acm);
1251     globals->add_subsystem("aircraft-model", acm, SGSubsystemMgr::DISPLAY);
1252
1253     FGModelMgr* mm = new FGModelMgr;
1254     globals->set_model_mgr(mm);
1255     globals->add_subsystem("model-manager", mm, SGSubsystemMgr::DISPLAY);
1256
1257     FGViewMgr *viewmgr = new FGViewMgr;
1258     globals->set_viewmgr( viewmgr );
1259     globals->add_subsystem("view-manager", viewmgr, SGSubsystemMgr::DISPLAY);
1260
1261     globals->add_subsystem("tile-manager", globals->get_tile_mgr(), 
1262       SGSubsystemMgr::DISPLAY);
1263       
1264     ////////////////////////////////////////////////////////////////////
1265     // Bind and initialize subsystems.
1266     ////////////////////////////////////////////////////////////////////
1267
1268     globals->get_subsystem_mgr()->bind();
1269     globals->get_subsystem_mgr()->init();
1270
1271     ////////////////////////////////////////////////////////////////////////
1272     // Initialize the Nasal interpreter.
1273     // Do this last, so that the loaded scripts see initialized state
1274     ////////////////////////////////////////////////////////////////////////
1275     FGNasalSys* nasal = new FGNasalSys();
1276     globals->add_subsystem("nasal", nasal, SGSubsystemMgr::INIT);
1277     nasal->init();
1278
1279     // initialize methods that depend on other subsystems.
1280     globals->get_subsystem_mgr()->postinit();
1281
1282     ////////////////////////////////////////////////////////////////////////
1283     // End of subsystem initialization.
1284     ////////////////////////////////////////////////////////////////////
1285
1286     fgSetBool("/sim/crashed", false);
1287     fgSetBool("/sim/initialized", true);
1288
1289     SG_LOG( SG_GENERAL, SG_INFO, endl);
1290
1291                                 // Save the initial state for future
1292                                 // reference.
1293     globals->saveInitialState();
1294     
1295     return true;
1296 }
1297
1298 // Reset: this is what the 'reset' command (and hence, GUI) is attached to
1299 void fgReInitSubsystems()
1300 {
1301     static const SGPropertyNode *master_freeze
1302         = fgGetNode("/sim/freeze/master");
1303
1304     SG_LOG( SG_GENERAL, SG_INFO, "fgReInitSubsystems()");
1305
1306 // setup state to begin re-init
1307     bool freeze = master_freeze->getBoolValue();
1308     if ( !freeze ) {
1309         fgSetBool("/sim/freeze/master", true);
1310     }
1311     
1312     fgSetBool("/sim/signals/reinit", true);
1313     fgSetBool("/sim/crashed", false);
1314
1315 // do actual re-init steps
1316     globals->get_subsystem("flight")->unbind();
1317     
1318   // reset control state, before restoring initial state; -set or config files
1319   // may specify values for flaps, trim tabs, magnetos, etc
1320     globals->get_controls()->reset_all();
1321         
1322     globals->restoreInitialState();
1323
1324     // update our position based on current presets
1325     fgInitPosition();
1326     
1327     // Force reupdating the positions of the ai 3d models. They are used for
1328     // initializing ground level for the FDM.
1329     globals->get_subsystem("ai-model")->reinit();
1330
1331     // Initialize the FDM
1332     globals->get_subsystem("flight")->reinit();
1333
1334     // reset replay buffers
1335     globals->get_subsystem("replay")->reinit();
1336     
1337     // reload offsets from config defaults
1338     globals->get_viewmgr()->reinit();
1339
1340     globals->get_subsystem("time")->reinit();
1341
1342     // need to bind FDMshell again, since we manually unbound it above...
1343     globals->get_subsystem("flight")->bind();
1344
1345 // setup state to end re-init
1346     fgSetBool("/sim/signals/reinit", false);
1347     if ( !freeze ) {
1348         fgSetBool("/sim/freeze/master", false);
1349     }
1350     fgSetBool("/sim/sceneryloaded",false);
1351 }
1352
1353
1354 ///////////////////////////////////////////////////////////////////////////////
1355 // helper object to implement the --show-aircraft command.
1356 // resides here so we can share the fgFindAircraftInDir template above,
1357 // and hence ensure this command lists exectly the same aircraft as the normal
1358 // loading path.
1359 class ShowAircraft 
1360 {
1361 public:
1362   ShowAircraft()
1363   {
1364     _minStatus = getNumMaturity(fgGetString("/sim/aircraft-min-status", "all"));
1365   }
1366   
1367   
1368   void show(const SGPath& path)
1369   {
1370     fgFindAircraftInDir(path, this, &ShowAircraft::processAircraft);
1371   
1372     std::sort(_aircraft.begin(), _aircraft.end(), ciLessLibC());
1373     SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on Windows
1374     cout << "Available aircraft:" << endl;
1375     for ( unsigned int i = 0; i < _aircraft.size(); i++ ) {
1376         cout << _aircraft[i] << endl;
1377     }
1378   }
1379   
1380 private:
1381   bool processAircraft(const SGPath& path)
1382   {
1383     SGPropertyNode root;
1384     try {
1385        readProperties(path.str(), &root);
1386     } catch (sg_exception& ) {
1387        return false;
1388     }
1389   
1390     int maturity = 0;
1391     string descStr("   ");
1392     descStr += path.file();
1393   // trim common suffix from file names
1394     int nPos = descStr.rfind("-set.xml");
1395     if (nPos == (int)(descStr.size() - 8)) {
1396       descStr.resize(nPos);
1397     }
1398     
1399     SGPropertyNode *node = root.getNode("sim");
1400     if (node) {
1401       SGPropertyNode* desc = node->getNode("description");
1402       // if a status tag is found, read it in
1403       if (node->hasValue("status")) {
1404         maturity = getNumMaturity(node->getStringValue("status"));
1405       }
1406       
1407       if (desc) {
1408         if (descStr.size() <= 27+3) {
1409           descStr.append(29+3-descStr.size(), ' ');
1410         } else {
1411           descStr += '\n';
1412           descStr.append( 32, ' ');
1413         }
1414         descStr += desc->getStringValue();
1415       }
1416     } // of have 'sim' node
1417     
1418     if (maturity < _minStatus) {
1419       return false;
1420     }
1421
1422     _aircraft.push_back(descStr);
1423     return false;
1424   }
1425
1426
1427   int getNumMaturity(const char * str) 
1428   {
1429     // changes should also be reflected in $FG_ROOT/data/options.xml & 
1430     // $FG_ROOT/data/Translations/string-default.xml
1431     const char* levels[] = {"alpha","beta","early-production","production"}; 
1432
1433     if (!strcmp(str, "all")) {
1434       return 0;
1435     }
1436
1437     for (size_t i=0; i<(sizeof(levels)/sizeof(levels[0]));i++) 
1438       if (strcmp(str,levels[i])==0)
1439         return i;
1440
1441     return 0;
1442   }
1443
1444   // recommended in Meyers, Effective STL when internationalization and embedded
1445   // NULLs aren't an issue.  Much faster than the STL or Boost lex versions.
1446   struct ciLessLibC : public std::binary_function<string, string, bool>
1447   {
1448     bool operator()(const std::string &lhs, const std::string &rhs) const
1449     {
1450       return strcasecmp(lhs.c_str(), rhs.c_str()) < 0 ? 1 : 0;
1451     }
1452   };
1453
1454   int _minStatus;
1455   string_list _aircraft;
1456 };
1457
1458 void fgShowAircraft(const SGPath &path)
1459 {
1460     ShowAircraft s;
1461     s.show(path);
1462         
1463 #ifdef _MSC_VER
1464     cout << "Hit a key to continue..." << endl;
1465     cin.get();
1466 #endif
1467 }
1468
1469