]> git.mxchange.org Git - flightgear.git/blobdiff - src/Airports/airport.cxx
commradio: improvements for atis speech
[flightgear.git] / src / Airports / airport.cxx
index 201fc28d4aa0c1389f2428f665689b60d170e8f5..750ccac8221613bfc756e41eeb2042ca9845635a 100644 (file)
@@ -52,6 +52,7 @@
 #include <Navaids/waypoint.hxx>
 #include <ATC/CommStation.hxx>
 #include <Navaids/NavDataCache.hxx>
+#include <Navaids/navrecord.hxx>
 
 using std::vector;
 using std::pair;
@@ -79,6 +80,7 @@ FGAirport::FGAirport( PositionedID aGuid,
     mHelipadsLoaded(false),
     mTaxiwaysLoaded(false),
     mProceduresLoaded(false),
+    mThresholdDataLoaded(false),
     mILSDataLoaded(false)
 {
 }
@@ -148,7 +150,7 @@ unsigned int FGAirport::numHelipads() const
 FGRunwayRef FGAirport::getRunwayByIndex(unsigned int aIndex) const
 {
   loadRunways();
-  return loadById<FGRunway>(mRunways, aIndex);
+  return mRunways.at(aIndex);
 }
 
 //------------------------------------------------------------------------------
@@ -166,10 +168,8 @@ FGRunwayMap FGAirport::getRunwayMap() const
 
   double minLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft");
 
-  BOOST_FOREACH(PositionedID id, mRunways)
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways)
   {
-    FGRunway* rwy = loadById<FGRunway>(id);
-
     // ignore unusably short runways
     // TODO other methods don't check this...
     if( rwy->lengthFt() >= minLengthFt )
@@ -197,8 +197,14 @@ FGHelipadMap FGAirport::getHelipadMap() const
 //------------------------------------------------------------------------------
 bool FGAirport::hasRunwayWithIdent(const std::string& aIdent) const
 {
-  return flightgear::NavDataCache::instance()
-    ->airportItemWithIdent(guid(), FGPositioned::RUNWAY, aIdent) != 0;
+  loadRunways();
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
+    if (rwy->ident() == aIdent) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
 //------------------------------------------------------------------------------
@@ -211,16 +217,15 @@ bool FGAirport::hasHelipadWithIdent(const std::string& aIdent) const
 //------------------------------------------------------------------------------
 FGRunwayRef FGAirport::getRunwayByIdent(const std::string& aIdent) const
 {
-  PositionedID id =
-    flightgear::NavDataCache::instance()
-      ->airportItemWithIdent(guid(), FGPositioned::RUNWAY, aIdent);
-
-  if (id == 0) {
-    SG_LOG(SG_GENERAL, SG_ALERT, "no such runway '" << aIdent << "' at airport " << ident());
-    throw sg_range_exception("unknown runway " + aIdent + " at airport:" + ident(), "FGAirport::getRunwayByIdent");
+  loadRunways();
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
+    if (rwy->ident() == aIdent) {
+      return rwy;
+    }
   }
   
-  return loadById<FGRunway>(id);
+  SG_LOG(SG_GENERAL, SG_ALERT, "no such runway '" << aIdent << "' at airport " << ident());
+  throw sg_range_exception("unknown runway " + aIdent + " at airport:" + ident(), "FGAirport::getRunwayByIdent");
 }
 
 //------------------------------------------------------------------------------
@@ -236,25 +241,30 @@ FGHelipadRef FGAirport::getHelipadByIdent(const std::string& aIdent) const
 }
 
 //------------------------------------------------------------------------------
-FGRunwayRef FGAirport::findBestRunwayForHeading(double aHeading) const
+FGRunwayRef FGAirport::findBestRunwayForHeading(double aHeading, struct FindBestRunwayForHeadingParams * parms ) const
 {
   loadRunways();
   
   FGRunway* result = NULL;
   double currentBestQuality = 0.0;
   
-  SGPropertyNode *param = fgGetNode("/sim/airport/runways/search", true);
-  double lengthWeight = param->getDoubleValue("length-weight", 0.01);
-  double widthWeight = param->getDoubleValue("width-weight", 0.01);
-  double surfaceWeight = param->getDoubleValue("surface-weight", 10);
-  double deviationWeight = param->getDoubleValue("deviation-weight", 1);
+  struct FindBestRunwayForHeadingParams fbrfhp;
+  if( NULL != parms ) fbrfhp = *parms;
+
+  SGPropertyNode_ptr searchNode = fgGetNode("/sim/airport/runways/search");
+  if( searchNode.valid() ) {
+    fbrfhp.lengthWeight = searchNode->getDoubleValue("length-weight", fbrfhp.lengthWeight );
+    fbrfhp.widthWeight = searchNode->getDoubleValue("width-weight", fbrfhp.widthWeight );
+    fbrfhp.surfaceWeight = searchNode->getDoubleValue("surface-weight", fbrfhp.surfaceWeight );
+    fbrfhp.deviationWeight = searchNode->getDoubleValue("deviation-weight", fbrfhp.deviationWeight );
+    fbrfhp.ilsWeight = searchNode->getDoubleValue("ils-weight", fbrfhp.ilsWeight );
+  }
     
-  BOOST_FOREACH(PositionedID id, mRunways) {
-    FGRunway* rwy = loadById<FGRunway>(id);
-    double good = rwy->score(lengthWeight, widthWeight, surfaceWeight);
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
+    double good = rwy->score( fbrfhp.lengthWeight,  fbrfhp.widthWeight,  fbrfhp.surfaceWeight,  fbrfhp.ilsWeight );
     double dev = aHeading - rwy->headingDeg();
     SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
-    double bad = fabs(deviationWeight * dev) + 1e-20;
+    double bad = fabs( fbrfhp.deviationWeight * dev) + 1e-20;
     double quality = good / bad;
     
     if (quality > currentBestQuality) {
@@ -274,9 +284,7 @@ FGRunwayRef FGAirport::findBestRunwayForPos(const SGGeod& aPos) const
   FGRunway* result = NULL;
   double currentLowestDev = 180.0;
   
-  BOOST_FOREACH(PositionedID id, mRunways) {
-    FGRunway* rwy = loadById<FGRunway>(id);
-
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
     double inboundCourse = SGGeodesy::courseDeg(aPos, rwy->end());
     double dev = inboundCourse - rwy->headingDeg();
     SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
@@ -297,8 +305,7 @@ bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const
 {
   loadRunways();
   
-  BOOST_FOREACH(PositionedID id, mRunways) {
-    FGRunway* rwy = loadById<FGRunway>(id);
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
     if (rwy->isHardSurface() && (rwy->lengthFt() >= aLengthFt)) {
       return true; // we're done!
     }
@@ -314,8 +321,7 @@ FGRunwayList FGAirport::getRunwaysWithoutReciprocals() const
   
   FGRunwayList r;
   
-  BOOST_FOREACH(PositionedID id, mRunways) {
-    FGRunway* rwy = loadById<FGRunway>(id);
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
     FGRunway* recip = rwy->reciprocalRunway();
     if (recip) {
       FGRunwayList::iterator it = std::find(r.begin(), r.end(), recip);
@@ -382,7 +388,7 @@ FGRunwayRef FGAirport::getActiveRunwayForUsage() const
   double hdg = 270;
   
   if (envMgr) {
-    FGEnvironment stationWeather(envMgr->getEnvironment(mPosition));
+    FGEnvironment stationWeather(envMgr->getEnvironment(geod()));
   
     double windSpeed = stationWeather.get_wind_speed_kt();
     if (windSpeed > 0.0) {
@@ -496,6 +502,12 @@ const FGAirport *fgFindAirportID( const std::string& id)
     return FGAirport::findByIdent(id);
 }
 
+PositionedIDVec FGAirport::itemsOfType(FGPositioned::Type ty) const
+{
+  flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
+  return cache->airportItemsOfType(guid(), ty);
+}
+
 void FGAirport::loadRunways() const
 {
   if (mRunwaysLoaded) {
@@ -505,7 +517,10 @@ void FGAirport::loadRunways() const
   loadSceneryDefinitions();
   
   mRunwaysLoaded = true;
-  mRunways = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::RUNWAY);
+  PositionedIDVec rwys(itemsOfType(FGPositioned::RUNWAY));
+  BOOST_FOREACH(PositionedID id, rwys) {
+    mRunways.push_back(loadById<FGRunway>(id));
+  }
 }
 
 void FGAirport::loadHelipads() const
@@ -514,10 +529,8 @@ void FGAirport::loadHelipads() const
     return; // already loaded, great
   }
 
-  loadSceneryDefinitions();
-
   mHelipadsLoaded = true;
-  mHelipads = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::HELIPAD);
+  mHelipads = itemsOfType(FGPositioned::HELIPAD);
 }
 
 void FGAirport::loadTaxiways() const
@@ -527,7 +540,7 @@ void FGAirport::loadTaxiways() const
   }
   
   mTaxiwaysLoaded =  true;
-  mTaxiways = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::TAXIWAY);
+  mTaxiways = itemsOfType(FGPositioned::TAXIWAY);
 }
 
 void FGAirport::loadProcedures() const
@@ -549,23 +562,24 @@ void FGAirport::loadProcedures() const
 
 void FGAirport::loadSceneryDefinitions() const
 {
-  NavDataCache* cache = NavDataCache::instance();
+  if (mThresholdDataLoaded) {
+    return;
+  }
+  
+  mThresholdDataLoaded = true;
+  
   SGPath path;
   if (!XMLLoader::findAirportData(ident(), "threshold", path)) {
     return; // no XML threshold data
   }
   
-  if (!cache->isCachedFileModified(path)) {
-    // cached values are correct, we're all done
-    return;
-  }
-  
-    flightgear::NavDataCache::Transaction txn(cache);
+  try {
     SGPropertyNode_ptr rootNode = new SGPropertyNode;
     readProperties(path.str(), rootNode);
     const_cast<FGAirport*>(this)->readThresholdData(rootNode);
-    cache->stampCacheFile(path);
-    txn.commit();
+  } catch (sg_exception& e) {
+    SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading threshold XML failed:" << e.getFormattedMessage());
+  }
 }
 
 void FGAirport::readThresholdData(SGPropertyNode* aRoot)
@@ -589,37 +603,44 @@ void FGAirport::processThreshold(SGPropertyNode* aThreshold)
   std::string rwyIdent(aThreshold->getStringValue("rwy"));
   NavDataCache* cache = NavDataCache::instance(); 
   PositionedID id = cache->airportItemWithIdent(guid(), FGPositioned::RUNWAY, rwyIdent);
-  if (id == 0) {
-    SG_LOG(SG_GENERAL, SG_DEBUG, "FGAirport::processThreshold: "
-           "found runway not defined in the global data:" << ident() << "/" << rwyIdent);
-    return;
-  }
   
   double lon = aThreshold->getDoubleValue("lon"),
   lat = aThreshold->getDoubleValue("lat");
-  SGGeod newThreshold(SGGeod::fromDegM(lon, lat, mPosition.getElevationM()));
+  SGGeod newThreshold(SGGeod::fromDegM(lon, lat, elevationM()));
   
   double newHeading = aThreshold->getDoubleValue("hdg-deg");
   double newDisplacedThreshold = aThreshold->getDoubleValue("displ-m");
   double newStopway = aThreshold->getDoubleValue("stopw-m");
   
-  cache->updateRunwayThreshold(id, newThreshold,
-                               newHeading, newDisplacedThreshold, newStopway);
+  if (id == 0) {
+    SG_LOG(SG_GENERAL, SG_DEBUG, "FGAirport::processThreshold: "
+           "found runway not defined in the global data:" << ident() << "/" << rwyIdent);
+    // enable this code when threshold.xml contains sufficient data to
+    // fully specify a new runway, *and* we figure out how to assign runtime
+    // Positioned IDs and insert temporary items into the spatial map.
+#if 0
+    double newLength = 0.0, newWidth = 0.0;
+    int surfaceCode = 0;
+    FGRunway* rwy = new FGRunway(id, guid(), rwyIdent, newThreshold,
+                       newHeading,
+                       newLength, newWidth,
+                       newDisplacedThreshold, newStopway,
+                       surfaceCode);
+    // insert into the spatial map too
+    mRunways.push_back(rwy);
+#endif
+  } else {
+    FGRunway* rwy = loadById<FGRunway>(id);
+    rwy->updateThreshold(newThreshold, newHeading,
+                         newDisplacedThreshold, newStopway);
+
+  }
 }
 
 SGGeod FGAirport::getTowerLocation() const
 {
   validateTowerData();
-  
-  NavDataCache* cache = NavDataCache::instance();
-  PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER);
-  if (towers.empty()) {
-    SG_LOG(SG_GENERAL, SG_ALERT, "No towers defined for:" <<ident());
-    return SGGeod();
-  }
-  
-  FGPositionedRef tower = cache->loadById(towers.front());
-  return tower->geod();
+  return mTowerPosition;
 }
 
 void FGAirport::validateTowerData() const
@@ -627,25 +648,34 @@ void FGAirport::validateTowerData() const
   if (mTowerDataLoaded) {
     return;
   }
-
+  
   mTowerDataLoaded = true;
+
+// first, load data from the cache (apt.dat)
   NavDataCache* cache = NavDataCache::instance();
+  PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER);
+  if (towers.empty()) {
+    SG_LOG(SG_GENERAL, SG_ALERT, "No towers defined for:" <<ident());
+    mTowerPosition = geod(); // use airport position
+    // increase tower elevation by 20 metres above the field elevation
+    mTowerPosition.setElevationM(geod().getElevationM() + 20.0);
+  } else {
+    FGPositionedRef tower = cache->loadById(towers.front());
+    mTowerPosition = tower->geod();
+  }
+  
   SGPath path;
   if (!XMLLoader::findAirportData(ident(), "twr", path)) {
-    return; // no XML tower data
+    return; // no XML tower data, base position is fine
   }
   
-  if (!cache->isCachedFileModified(path)) {
-  // cached values are correct, we're all done
-    return;
+  try {
+    SGPropertyNode_ptr rootNode = new SGPropertyNode;
+    readProperties(path.str(), rootNode);
+    const_cast<FGAirport*>(this)->readTowerData(rootNode);
+  } catch (sg_exception& e){
+    SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading twr XML failed:" << e.getFormattedMessage());
   }
-   
-  flightgear::NavDataCache::Transaction txn(cache);
-  SGPropertyNode_ptr rootNode = new SGPropertyNode;
-  readProperties(path.str(), rootNode);
-  const_cast<FGAirport*>(this)->readTowerData(rootNode);
-  cache->stampCacheFile(path);
-  txn.commit();
 }
 
 void FGAirport::readTowerData(SGPropertyNode* aRoot)
@@ -658,52 +688,36 @@ void FGAirport::readTowerData(SGPropertyNode* aRoot)
 // scenery for a precise terrain elevation, we use the field elevation
 // (this is also what the apt.dat code does)
   double fieldElevationM = geod().getElevationM();
-  SGGeod towerLocation(SGGeod::fromDegM(lon, lat, fieldElevationM + elevM));
-  
-  NavDataCache* cache = NavDataCache::instance();
-  PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER);
-  if (towers.empty()) {
-    cache->insertTower(guid(), towerLocation);
-  } else {
-    // update the position
-    cache->updatePosition(towers.front(), towerLocation);
-  }
+  mTowerPosition = SGGeod::fromDegM(lon, lat, fieldElevationM + elevM);
 }
 
-bool FGAirport::validateILSData()
+void FGAirport::validateILSData()
 {
   if (mILSDataLoaded) {
-    return false;
+    return;
   }
   
+  // to avoid re-entrancy on this code-path, ensure we set loaded
+  // immediately.
   mILSDataLoaded = true;
-  NavDataCache* cache = NavDataCache::instance();
+    
   SGPath path;
   if (!XMLLoader::findAirportData(ident(), "ils", path)) {
-    return false; // no XML tower data
+    return; // no XML tower data
   }
   
-  if (!cache->isCachedFileModified(path)) {
-    // cached values are correct, we're all done
-    return false;
+  try {
+      SGPropertyNode_ptr rootNode = new SGPropertyNode;
+      readProperties(path.str(), rootNode);
+      readILSData(rootNode);
+  } catch (sg_exception& e){
+      SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading ils XML failed:" << e.getFormattedMessage());
   }
-  
-  SGPropertyNode_ptr rootNode = new SGPropertyNode;
-  readProperties(path.str(), rootNode);
-
-  flightgear::NavDataCache::Transaction txn(cache);
-  readILSData(rootNode);
-  cache->stampCacheFile(path);
-  txn.commit();
-    
-// we loaded data, tell the caller it might need to reload things
-  return true;
 }
 
 void FGAirport::readILSData(SGPropertyNode* aRoot)
-{
+{  
   NavDataCache* cache = NavDataCache::instance();
-  
   // find the entry matching the runway
   SGPropertyNode* runwayNode, *ilsNode;
   for (int i=0; (runwayNode = aRoot->getChild("runway", i)) != NULL; ++i) {
@@ -715,7 +729,7 @@ void FGAirport::readILSData(SGPropertyNode* aRoot)
                                         ilsNode->getStringValue("nav-id"));
       if (ils == 0) {
         SG_LOG(SG_GENERAL, SG_INFO, "reading ILS data for " << ident() <<
-               ", couldn;t find runway/navaid for:" <<
+               ", couldn't find runway/navaid for:" <<
                ilsNode->getStringValue("rwy") << "/" <<
                ilsNode->getStringValue("nav-id"));
         continue;
@@ -726,7 +740,9 @@ void FGAirport::readILSData(SGPropertyNode* aRoot)
         lat = ilsNode->getDoubleValue("lat"),
         elevM = ilsNode->getDoubleValue("elev-m");
  
-      cache->updateILS(ils, SGGeod::fromDegM(lon, lat, elevM), hdgDeg);
+      FGNavRecordRef nav(FGPositioned::loadById<FGNavRecord>(ils));
+      assert(nav.valid());
+      nav->updateFromXML(SGGeod::fromDegM(lon, lat, elevM), hdgDeg);
     } // of ILS iteration
   } // of runway iteration
 }
@@ -883,6 +899,46 @@ FGAirport::commStationsOfType(FGPositioned::Type aTy) const
   return result;
 }
 
+class AirportWithSize
+{
+public:
+    AirportWithSize(FGPositionedRef pos) :
+        _pos(pos),
+        _sizeMetric(0)
+    {
+        assert(pos->type() == FGPositioned::AIRPORT);
+        FGAirport* apt = static_cast<FGAirport*>(pos.get());
+        BOOST_FOREACH(FGRunway* rwy, apt->getRunwaysWithoutReciprocals()) {
+            _sizeMetric += static_cast<int>(rwy->lengthFt());
+        }
+    }
+    
+    bool operator<(const AirportWithSize& other) const
+    {
+        return _sizeMetric < other._sizeMetric;
+    }
+    
+    FGPositionedRef pos() const
+    { return _pos; }
+private:
+    FGPositionedRef _pos;
+    unsigned int _sizeMetric;
+    
+};
+
+void FGAirport::sortBySize(FGPositionedList& airportList)
+{
+    std::vector<AirportWithSize> annotated;
+    BOOST_FOREACH(FGPositionedRef p, airportList) {
+        annotated.push_back(AirportWithSize(p));
+    }
+    std::sort(annotated.begin(), annotated.end());
+    
+    for (unsigned int i=0; i<annotated.size(); ++i) {
+        airportList[i] = annotated[i].pos();
+    }
+}
+
 // get airport elevation
 double fgGetAirportElev( const std::string& id )
 {