]> git.mxchange.org Git - flightgear.git/blobdiff - src/Airports/simple.cxx
Merge branch 'next' into durk-atc
[flightgear.git] / src / Airports / simple.cxx
index 6a1334cc10f40f53c30f4e31f01655f436a4446c..9df1cb254723f3ffd530f9430b6f2ead28caded5 100644 (file)
 
 #include "simple.hxx"
 
+#include <cassert>
+
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/props/props.hxx>
+#include <simgear/props/props_io.hxx>
 #include <simgear/debug/logstream.hxx>
 #include <simgear/sg_inlines.h>
 
 #include <Environment/environment.hxx>
 #include <Main/fg_props.hxx>
 #include <Airports/runways.hxx>
+#include <Airports/pavement.hxx>
 #include <Airports/dynamics.hxx>
 #include <Airports/xmlloader.hxx>
+#include <Navaids/procedure.hxx>
+#include <Navaids/waypoint.hxx>
+#include <Navaids/PositionedBinding.hxx>
+#include <ATC/CommStation.hxx>
+
+using std::vector;
+using namespace flightgear;
 
 // magic import of a helper which uses FGPositioned internals
 extern char** searchAirportNamesAndIdents(const std::string& aFilter);
@@ -55,8 +66,11 @@ FGAirport::FGAirport(const string &id, const SGGeod& location, const SGGeod& tow
     _tower_location(tower_location),
     _name(name),
     _has_metar(has_metar),
-    _dynamics(0)
+    _dynamics(0),
+    mRunwaysLoaded(false),
+    mTaxiwaysLoaded(true)
 {
+  init(true); // init FGPositioned
 }
 
 
@@ -82,30 +96,31 @@ bool FGAirport::isHeliport() const
 
 FGAirportDynamics * FGAirport::getDynamics()
 {
-    if (_dynamics != 0) {
+    if (_dynamics) {
         return _dynamics;
-    } else {
-        //cerr << "Trying to load dynamics for " << _id << endl;
-        _dynamics = new FGAirportDynamics(this);
-        XMLLoader::load(_dynamics);
-
-        FGRunwayPreference rwyPrefs(this);
-        XMLLoader::load(&rwyPrefs);
-        _dynamics->setRwyUse(rwyPrefs);
+    }
+    
+    _dynamics = new FGAirportDynamics(this);
+    XMLLoader::load(_dynamics);
 
-        //FGSidStar SIDs(this);
-        XMLLoader::load(_dynamics->getSIDs());
-   }
+    FGRunwayPreference rwyPrefs(this);
+    XMLLoader::load(&rwyPrefs);
+    _dynamics->setRwyUse(rwyPrefs);
+    XMLLoader::load(_dynamics->getSIDs());
+    
     return _dynamics;
 }
 
 unsigned int FGAirport::numRunways() const
 {
+  loadRunways();
   return mRunways.size();
 }
 
 FGRunway* FGAirport::getRunwayByIndex(unsigned int aIndex) const
 {
+  loadRunways();
+  
   assert(aIndex >= 0 && aIndex < mRunways.size());
   return mRunways[aIndex];
 }
@@ -129,6 +144,11 @@ FGRunway* FGAirport::getRunwayByIdent(const string& aIdent) const
 FGAirport::Runway_iterator
 FGAirport::getIteratorForRunwayIdent(const string& aIdent) const
 {
+  if (aIdent.empty())
+    return mRunways.end();
+
+  loadRunways();
+  
   string ident(aIdent);
   if ((aIdent.size() == 1) || !isdigit(aIdent[1])) {
     ident = "0" + aIdent;
@@ -146,6 +166,8 @@ FGAirport::getIteratorForRunwayIdent(const string& aIdent) const
 
 FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const
 {
+  loadRunways();
+  
   Runway_iterator it = mRunways.begin();
   FGRunway* result = NULL;
   double currentBestQuality = 0.0;
@@ -173,8 +195,34 @@ FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const
   return result;
 }
 
+FGRunway* FGAirport::findBestRunwayForPos(const SGGeod& aPos) const
+{
+  loadRunways();
+  
+  Runway_iterator it = mRunways.begin();
+  FGRunway* result = NULL;
+  double currentLowestDev = 180.0;
+  
+  for (; it != mRunways.end(); ++it) {
+    double inboundCourse = SGGeodesy::courseDeg(aPos, (*it)->end());
+    double dev = inboundCourse - (*it)->headingDeg();
+    SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
+
+    dev = fabs(dev);
+    if (dev < currentLowestDev) { // new best match
+      currentLowestDev = dev;
+      result = *it;
+    }
+  } // of runway iteration
+  
+  return result;
+
+}
+
 bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const
 {
+  loadRunways();
+  
   unsigned int numRunways(mRunways.size());
   for (unsigned int r=0; r<numRunways; ++r) {
     FGRunway* rwy = mRunways[r];
@@ -192,25 +240,42 @@ bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const
 
 unsigned int FGAirport::numTaxiways() const
 {
+  loadTaxiways();
   return mTaxiways.size();
 }
 
 FGTaxiway* FGAirport::getTaxiwayByIndex(unsigned int aIndex) const
 {
+  loadTaxiways();
   assert(aIndex >= 0 && aIndex < mTaxiways.size());
   return mTaxiways[aIndex];
 }
 
+unsigned int FGAirport::numPavements() const
+{
+  loadTaxiways();
+  return mPavements.size();
+}
+
+FGPavement* FGAirport::getPavementByIndex(unsigned int aIndex) const
+{
+  loadTaxiways();
+  assert(aIndex >= 0 && aIndex < mPavements.size());
+  return mPavements[aIndex];
+}
+
 void FGAirport::setRunwaysAndTaxiways(vector<FGRunwayPtr>& rwys,
-       vector<FGTaxiwayPtr>& txwys)
+       vector<FGTaxiwayPtr>& txwys,
+       vector<FGPavementPtr>& pvts)
 {
   mRunways.swap(rwys);
   Runway_iterator it = mRunways.begin();
   for (; it != mRunways.end(); ++it) {
     (*it)->setAirport(this);
   }
-  
+
   mTaxiways.swap(txwys);
+  mPavements.swap(pvts);
 }
 
 FGRunway* FGAirport::getActiveRunwayForUsage() const
@@ -220,13 +285,17 @@ FGRunway* FGAirport::getActiveRunwayForUsage() const
     envMgr = (FGEnvironmentMgr *) globals->get_subsystem("environment");
   }
   
-  FGEnvironment stationWeather(envMgr->getEnvironment(mPosition));
+  // This forces West-facing rwys to be used in no-wind situations
+  // which is consistent with Flightgear's initial setup.
+  double hdg = 270;
+  
+  if (envMgr) {
+    FGEnvironment stationWeather(envMgr->getEnvironment(mPosition));
   
-  double windSpeed = stationWeather.get_wind_speed_kt();
-  double hdg = stationWeather.get_wind_from_heading_deg();
-  if (windSpeed <= 0.0) {
-    hdg = 270; // This forces West-facing rwys to be used in no-wind situations
-    // which is consistent with Flightgear's initial setup.
+    double windSpeed = stationWeather.get_wind_speed_kt();
+    if (windSpeed > 0.0) {
+      hdg = stationWeather.get_wind_from_heading_deg();
+    }
   }
   
   return findBestRunwayForHeading(hdg);
@@ -260,7 +329,7 @@ bool FGAirport::HardSurfaceFilter::passAirport(FGAirport* aApt) const
 FGAirport* FGAirport::findByIdent(const std::string& aIdent)
 {
   FGPositionedRef r;
-  AirportFilter filter;
+  PortsFilter filter;
   r = FGPositioned::findNextWithPartialId(r, aIdent, &filter);
   if (!r) {
     return NULL; // we don't warn here, let the caller do that
@@ -271,7 +340,7 @@ FGAirport* FGAirport::findByIdent(const std::string& aIdent)
 FGAirport* FGAirport::getByIdent(const std::string& aIdent)
 {
   FGPositionedRef r;
-  AirportFilter filter;
+  PortsFilter filter;
   r = FGPositioned::findNextWithPartialId(r, aIdent, &filter);
   if (!r) {
     throw sg_range_exception("No such airport with ident: " + aIdent);
@@ -296,13 +365,361 @@ const FGAirport *fgFindAirportID( const string& id)
     return FGAirport::findByIdent(id);
 }
 
+void FGAirport::loadRunways() const
+{
+  if (mRunwaysLoaded) {
+    return; // already loaded, great
+  }
+  
+  mRunwaysLoaded = true;
+  loadSceneryDefinitions();
+}
+
+void FGAirport::loadTaxiways() const
+{
+  if (mTaxiwaysLoaded) {
+    return; // already loaded, great
+  }
+}
+
+void FGAirport::loadProcedures() const
+{
+  if (mProceduresLoaded) {
+    return;
+  }
+  
+  mProceduresLoaded = true;
+  SGPath path;
+  if (!XMLLoader::findAirportData(ident(), "procedures", path)) {
+    SG_LOG(SG_GENERAL, SG_INFO, "no procedures data available for " << ident());
+    return;
+  }
+  
+  SG_LOG(SG_GENERAL, SG_INFO, ident() << ": loading procedures from " << path.str());
+  Route::loadAirportProcedures(path, const_cast<FGAirport*>(this));
+}
+
+void FGAirport::loadSceneryDefinitions() const
+{  
+  // allow users to disable the scenery data in the short-term
+  // longer term, this option can probably disappear
+  if (!fgGetBool("/sim/paths/use-custom-scenery-data")) {
+    return; 
+  }
+  
+  SGPath path;
+  SGPropertyNode_ptr rootNode = new SGPropertyNode;
+  if (XMLLoader::findAirportData(ident(), "threshold", path)) {
+    readProperties(path.str(), rootNode);
+    const_cast<FGAirport*>(this)->readThresholdData(rootNode);
+  }
+  
+  // repeat for the tower data
+  rootNode = new SGPropertyNode;
+  if (XMLLoader::findAirportData(ident(), "twr", path)) {
+    readProperties(path.str(), rootNode);
+    const_cast<FGAirport*>(this)->readTowerData(rootNode);
+  }
+}
+
+void FGAirport::readThresholdData(SGPropertyNode* aRoot)
+{
+  SGPropertyNode* runway;
+  int runwayIndex = 0;
+  for (; (runway = aRoot->getChild("runway", runwayIndex)) != NULL; ++runwayIndex) {
+    SGPropertyNode* t0 = runway->getChild("threshold", 0),
+      *t1 = runway->getChild("threshold", 1);
+    assert(t0);
+    assert(t1); // too strict? mayeb we should finally allow single-ended runways
+    
+    processThreshold(t0);
+    processThreshold(t1);
+  } // of runways iteration
+}
+
+void FGAirport::processThreshold(SGPropertyNode* aThreshold)
+{
+  // first, let's identify the current runway
+  string id(aThreshold->getStringValue("rwy"));
+  if (!hasRunwayWithIdent(id)) {
+    SG_LOG(SG_GENERAL, SG_WARN, "FGAirport::processThreshold: "
+      "found runway not defined in the global data:" << ident() << "/" << id);
+    return;
+  }
+  
+  FGRunway* rwy = getRunwayByIdent(id);
+  rwy->processThreshold(aThreshold);
+}
+
+void FGAirport::readTowerData(SGPropertyNode* aRoot)
+{
+  SGPropertyNode* twrNode = aRoot->getChild("tower")->getChild("twr");
+  double lat = twrNode->getDoubleValue("lat"), 
+    lon = twrNode->getDoubleValue("lon"), 
+    elevM = twrNode->getDoubleValue("elev-m");  
+// tower elevation is AGL, not AMSL. Since we don't want to depend on the
+// scenery for a precise terrain elevation, we use the field elevation
+// (this is also what the apt.dat code does)
+  double fieldElevationM = geod().getElevationM();
+  
+  _tower_location = SGGeod::fromDegM(lon, lat, fieldElevationM + elevM);
+}
+
+bool FGAirport::buildApproach(Waypt* aEnroute, STAR* aSTAR, FGRunway* aRwy, WayptVec& aRoute)
+{
+  loadProcedures();
+
+  if ((aRwy && (aRwy->airport() != this))) {
+    throw sg_exception("invalid parameters", "FGAirport::buildApproach");
+  }
+  
+  if (aSTAR) {
+    bool ok = aSTAR->route(aRwy, aEnroute, aRoute);
+    if (!ok) {
+      SG_LOG(SG_GENERAL, SG_WARN, ident() << ": build approach, STAR " << aSTAR->ident() 
+         << " failed to route from transition " << aEnroute->ident());
+      return false;
+    }
+  } else if (aEnroute) {
+    // no a STAR specified, just use enroute point directly
+    aRoute.push_back(aEnroute);
+  }
+  
+  if (!aRwy) {
+    // no runway selected yet, but we loaded the STAR, so that's fine, we're done
+    return true;
+  }
+  
+// build the approach (possibly including transition), and including the missed segment
+  vector<Approach*> aps;
+  for (unsigned int j=0; j<mApproaches.size();++j) {
+    if (mApproaches[j]->runway() == aRwy) {
+      aps.push_back(mApproaches[j]);
+    }
+  } // of approach filter by runway
+  
+  if (aps.empty()) {
+    SG_LOG(SG_GENERAL, SG_INFO, ident() << "; no approaches defined for runway " << aRwy->ident());
+    // could build a fallback approach here
+    return false;
+  }
+  
+  for (unsigned int k=0; k<aps.size(); ++k) {
+    if (aps[k]->route(aRoute.back(), aRoute)) {
+      return true;
+    }
+  } // of initial approach iteration
+  
+  SG_LOG(SG_GENERAL, SG_INFO, ident() << ": unable to find transition to runway "
+    << aRwy->ident() << ", assume vectors");
+  
+  WayptRef v(new ATCVectors(NULL, this));
+  aRoute.push_back(v);
+  return aps.front()->routeFromVectors(aRoute);
+}
+
+pair<flightgear::SID*, WayptRef>
+FGAirport::selectSID(const SGGeod& aDest, FGRunway* aRwy)
+{
+  loadProcedures();
+  
+  WayptRef enroute;
+  flightgear::SID* sid = NULL;
+  double d = 1e9;
+  
+  for (unsigned int i=0; i<mSIDs.size(); ++i) {
+    if (aRwy && !mSIDs[i]->isForRunway(aRwy)) {
+      continue;
+    }
+  
+    WayptRef e = mSIDs[i]->findBestTransition(aDest);
+    if (!e) {
+      continue; // strange, but let's not worry about it
+    }
+    
+    // assert(e->isFixedPosition());
+    double ed = SGGeodesy::distanceM(aDest, e->position());
+    if (ed < d) { // new best match
+      enroute = e;
+      d = ed;
+      sid = mSIDs[i];
+    }
+  } // of SID iteration
+  
+  if (!mSIDs.empty() && !sid) {
+    SG_LOG(SG_GENERAL, SG_INFO, ident() << "selectSID, no SID found (runway=" 
+      << (aRwy ? aRwy->ident() : "no runway preference"));
+  }
+  
+  return make_pair(sid, enroute);
+}
+    
+pair<STAR*, WayptRef>
+FGAirport::selectSTAR(const SGGeod& aOrigin, FGRunway* aRwy)
+{
+  loadProcedures();
+  
+  WayptRef enroute;
+  STAR* star = NULL;
+  double d = 1e9;
+  
+  for (unsigned int i=0; i<mSTARs.size(); ++i) {
+    if (!mSTARs[i]->isForRunway(aRwy)) {
+      continue;
+    }
+    
+    SG_LOG(SG_GENERAL, SG_INFO, "STAR " << mSTARs[i]->ident() << " is valid for runway");
+    WayptRef e = mSTARs[i]->findBestTransition(aOrigin);
+    if (!e) {
+      continue; // strange, but let's not worry about it
+    }
+    
+    // assert(e->isFixedPosition());
+    double ed = SGGeodesy::distanceM(aOrigin, e->position());
+    if (ed < d) { // new best match
+      enroute = e;
+      d = ed;
+      star = mSTARs[i];
+    }
+  } // of STAR iteration
+  
+  return make_pair(star, enroute);
+}
+
+
+void FGAirport::addSID(flightgear::SID* aSid)
+{
+  mSIDs.push_back(aSid);
+}
+
+void FGAirport::addSTAR(STAR* aStar)
+{
+  mSTARs.push_back(aStar);
+}
+
+void FGAirport::addApproach(Approach* aApp)
+{
+  mApproaches.push_back(aApp);
+}
+
+unsigned int FGAirport::numSIDs() const
+{
+  loadProcedures();
+  return mSIDs.size();
+}
+
+flightgear::SID* FGAirport::getSIDByIndex(unsigned int aIndex) const
+{
+  loadProcedures();
+  return mSIDs[aIndex];
+}
+
+flightgear::SID* FGAirport::findSIDWithIdent(const std::string& aIdent) const
+{
+  loadProcedures();
+  for (unsigned int i=0; i<mSIDs.size(); ++i) {
+    if (mSIDs[i]->ident() == aIdent) {
+      return mSIDs[i];
+    }
+  }
+  
+  return NULL;
+}
+
+unsigned int FGAirport::numSTARs() const
+{
+  loadProcedures();
+  return mSTARs.size();
+}
+
+STAR* FGAirport::getSTARByIndex(unsigned int aIndex) const
+{
+  loadProcedures();
+  return mSTARs[aIndex];
+}
+
+STAR* FGAirport::findSTARWithIdent(const std::string& aIdent) const
+{
+  loadProcedures();
+  for (unsigned int i=0; i<mSTARs.size(); ++i) {
+    if (mSTARs[i]->ident() == aIdent) {
+      return mSTARs[i];
+    }
+  }
+  
+  return NULL;
+}
+
+unsigned int FGAirport::numApproaches() const
+{
+  loadProcedures();
+  return mApproaches.size();
+}
+
+Approach* FGAirport::getApproachByIndex(unsigned int aIndex) const
+{
+  loadProcedures();
+  return mApproaches[aIndex];
+}
+
+class AirportNodeListener : public SGPropertyChangeListener
+{
+public:
+    AirportNodeListener()
+    {
+        SGPropertyNode* airports = fgGetNode("/sim/airport");
+        airports->addChangeListener(this, false);
+    }
+
+    virtual void valueChanged(SGPropertyNode*)
+    {
+    }
+
+    virtual void childAdded(SGPropertyNode* pr, SGPropertyNode* child)
+    {
+       FGAirport* apt = FGAirport::findByIdent(child->getName());
+       if (!apt) {
+           return;
+       }
+       
+       flightgear::PositionedBinding::bind(apt, child);
+    }
+};
+    
+void FGAirport::installPropertyListener()
+{
+    new AirportNodeListener;  
+}
+
+flightgear::PositionedBinding*
+FGAirport::createBinding(SGPropertyNode* nd) const
+{
+    return new flightgear::AirportBinding(this, nd);
+}
+
+void FGAirport::setCommStations(CommStationList& comms)
+{
+    mCommStations.swap(comms);
+    for (unsigned int c=0; c<mCommStations.size(); ++c) {
+        mCommStations[c]->setAirport(this);
+    }
+}
+
+CommStationList
+FGAirport::commStationsOfType(FGPositioned::Type aTy) const
+{
+    CommStationList result;
+    for (unsigned int c=0; c<mCommStations.size(); ++c) {
+        if (mCommStations[c]->type() == aTy) {
+            result.push_back(mCommStations[c]);
+        }
+    }
+    return result;
+}
 
 // get airport elevation
 double fgGetAirportElev( const string& id )
 {
-    SG_LOG( SG_GENERAL, SG_BULK,
-            "Finding elevation for airport: " << id );
-
     const FGAirport *a=fgFindAirportID( id);
     if (a) {
         return a->getElevation();
@@ -315,9 +732,6 @@ double fgGetAirportElev( const string& id )
 // get airport position
 SGGeod fgGetAirportPos( const string& id )
 {
-    SG_LOG( SG_ATC, SG_BULK,
-            "Finding position for airport: " << id );
-
     const FGAirport *a = fgFindAirportID( id);
 
     if (a) {