]> git.mxchange.org Git - flightgear.git/blobdiff - src/Navaids/navdb.cxx
Additional checks, for waypoint role setting.
[flightgear.git] / src / Navaids / navdb.cxx
index 36ccf9cdffa12335852bf9f16af6d0021b0cb245..99453022a826c45185b2b48a032939adffd3ca1e 100644 (file)
 #  include "config.h"
 #endif
 
-#include <simgear/compiler.h>
-
-#include <string>
+#include "navdb.hxx"
 
+#include <simgear/compiler.h>
 #include <simgear/debug/logstream.hxx>
 #include <simgear/math/sg_geodesy.hxx>
 #include <simgear/misc/strutils.hxx>
 #include <simgear/structure/exception.hxx>
 #include <simgear/misc/sgstream.hxx>
 #include <simgear/props/props_io.hxx>
+#include <simgear/sg_inlines.h>
 
 #include "navrecord.hxx"
 #include "navlist.hxx"
-#include "navdb.hxx"
 #include <Main/globals.hxx>
 #include <Navaids/markerbeacon.hxx>
-#include <Airports/simple.hxx>
+#include <Airports/airport.hxx>
 #include <Airports/runways.hxx>
 #include <Airports/xmlloader.hxx>
 #include <Main/fg_props.hxx>
+#include <Navaids/NavDataCache.hxx>
 
 using std::string;
 using std::vector;
 
-typedef std::map<FGAirport*, SGPropertyNode_ptr> AirportPropertyMap;
-
-static AirportPropertyMap static_airportIlsData;
-
 static FGPositioned::Type
 mapRobinTypeToFGPType(int aTy)
 {
@@ -63,6 +59,9 @@ mapRobinTypeToFGPType(int aTy)
   case 4: return FGPositioned::ILS;
   case 5: return FGPositioned::LOC;
   case 6: return FGPositioned::GS;
+  case 7: return FGPositioned::OM;
+  case 8: return FGPositioned::MM;
+  case 9: return FGPositioned::IM;
   case 12:
   case 13: return FGPositioned::DME;
   case 99: return FGPositioned::INVALID; // end-of-file code
@@ -71,12 +70,72 @@ mapRobinTypeToFGPType(int aTy)
   }
 }
 
-static FGNavRecord* createNavFromStream(std::istream& aStream)
+static bool autoAlignLocalizers = false;
+static double autoAlignThreshold = 0.0;
+
+/**
+ * Given a runway, and proposed localizer data (ident, positioned and heading),
+ * precisely align the localizer with the actual runway heading, providing the
+ * difference between the localizer course and runway heading is less than a
+ * threshold. (To allow for localizers such as Kai-Tak requiring a turn on final).
+ *
+ * The positioned and heading argument are modified if changes are made.
+ */
+void alignLocaliserWithRunway(FGRunway* rwy, const string& ident, SGGeod& pos, double& heading)
 {
+  assert(rwy);
+  // find the distance from the threshold to the localizer
+  double dist = SGGeodesy::distanceM(pos, rwy->threshold());
+  
+  // back project that distance along the runway center line
+  SGGeod newPos = rwy->pointOnCenterline(dist);
+  
+  double hdg_diff = heading - rwy->headingDeg();
+  SG_NORMALIZE_RANGE(hdg_diff, -180.0, 180.0);
+  
+  if ( fabs(hdg_diff) <= autoAlignThreshold ) {
+    pos = SGGeod::fromGeodFt(newPos, pos.getElevationFt());
+    heading = rwy->headingDeg();
+  } else {
+    SG_LOG(SG_NAVAID, SG_DEBUG, "localizer:" << ident << ", aligning with runway "
+           << rwy->ident() << " exceeded heading threshold");
+  }
+}
+
+static double defaultNavRange(const string& ident, FGPositioned::Type type)
+{
+  // Ranges are included with the latest data format, no need to
+  // assign our own defaults, unless the range is not set for some
+  // reason.
+  SG_LOG(SG_NAVAID, SG_DEBUG, "navaid " << ident << " has no range set, using defaults");
+  switch (type) {
+    case FGPositioned::NDB:
+    case FGPositioned::VOR:
+      return FG_NAV_DEFAULT_RANGE;
+      
+    case FGPositioned::LOC:
+    case FGPositioned::ILS:
+    case FGPositioned::GS:
+      return FG_LOC_DEFAULT_RANGE;
+      
+    case FGPositioned::DME: return FG_DME_DEFAULT_RANGE;
+    default: return FG_LOC_DEFAULT_RANGE;
+  }
+}
+
+
+namespace flightgear
+{
+
+static PositionedID readNavFromStream(std::istream& aStream,
+                                        FGPositioned::Type type = FGPositioned::INVALID)
+{
+  NavDataCache* cache = NavDataCache::instance();
+  
   int rawType;
   aStream >> rawType;
   if (aStream.eof() || (rawType == 99)) {
-    return NULL; // happens with, eg, carrier_nav.dat
+    return 0; // happens with, eg, carrier_nav.dat
   }
   
   double lat, lon, elev_ft, multiuse;
@@ -88,133 +147,156 @@ static FGNavRecord* createNavFromStream(std::istream& aStream)
   SGGeod pos(SGGeod::fromDegFt(lon, lat, elev_ft));
   name = simgear::strutils::strip(name);
   
-  if ((rawType >= 7) && (rawType <= 9)) {
-    // marker beacons use a different run-time class now
-     FGMarkerBeaconRecord::create(rawType, name, pos);
-     return NULL; // not a nav-record, but that's okay
+// the type can be forced by our caller, but normally we use th value
+// supplied in the .dat file
+  if (type == FGPositioned::INVALID) {
+    type = mapRobinTypeToFGPType(rawType);
   }
-  
-  FGPositioned::Type type = mapRobinTypeToFGPType(rawType);
   if (type == FGPositioned::INVALID) {
-    return NULL;
+    return 0;
+  }
+
+  if ((type >= FGPositioned::OM) && (type <= FGPositioned::IM)) {
+    AirportRunwayPair arp(cache->findAirportRunway(name));
+    if (arp.second && (elev_ft < 0.01)) {
+    // snap to runway elevation
+      FGPositioned* runway = cache->loadById(arp.second);
+      assert(runway);
+      pos.setElevationFt(runway->geod().getElevationFt());
+    }
+
+    return cache->insertNavaid(type, string(), name, pos, 0, 0, 0,
+                               arp.first, arp.second);
   }
   
+  if (range < 0.01) {
+    range = defaultNavRange(ident, type);
+  }
+  
+  AirportRunwayPair arp;
+  FGRunway* runway = NULL;
+  PositionedID navaid_dme = 0;
+
+  if (type == FGPositioned::DME) {
+    FGPositioned::TypeFilter f(FGPositioned::INVALID);
+    if ( name.find("VOR-DME") != std::string::npos ) {
+      f.addType(FGPositioned::VOR);
+    } else if ( name.find("DME-ILS") != std::string::npos ) {
+      f.addType(FGPositioned::ILS);
+      f.addType(FGPositioned::LOC);
+    } else if ( name.find("VORTAC") != std::string::npos ) {
+      f.addType(FGPositioned::VOR);
+    } else if ( name.find("NDB-DME") != std::string::npos ) {
+      f.addType(FGPositioned::NDB);
+    } else if ( name.find("TACAN") != std::string::npos ) {
+      f.addType(FGPositioned::VOR);
+    }
+
+    if (f.maxType() > 0) {
+      FGPositionedRef ref = FGPositioned::findClosestWithIdent(ident, pos, &f);
+      if (ref.valid()) {
+        string_list dme_part = simgear::strutils::split(name , 0 ,1);
+        string_list navaid_part = simgear::strutils::split(ref.get()->name(), 0 ,1);
+
+        if ( simgear::strutils::uppercase(navaid_part[0]) == simgear::strutils::uppercase(dme_part[0]) ) {
+          navaid_dme = ref.get()->guid();
+        }
+      }
+    }
+  }
+
+  if ((type >= FGPositioned::ILS) && (type <= FGPositioned::GS)) {
+    arp = cache->findAirportRunway(name);
+    if (arp.second) {
+      runway = FGPositioned::loadById<FGRunway>(arp.second);
+      assert(runway);
+#if 0
+      // code is disabled since it's causing some problems, see
+      // http://code.google.com/p/flightgear-bugs/issues/detail?id=926
+      if (elev_ft < 0.01) {
+        // snap to runway elevation
+        pos.setElevationFt(runway->geod().getElevationFt());
+      }
+#endif
+    } // of found runway in the DB
+  } // of type is runway-related
+  
+  bool isLoc = (type == FGPositioned::ILS) || (type == FGPositioned::LOC);
+  if (runway && autoAlignLocalizers && isLoc) {
+    alignLocaliserWithRunway(runway, ident, pos, multiuse);
+  }
+    
   // silently multiply adf frequencies by 100 so that adf
   // vs. nav/loc frequency lookups can use the same code.
   if (type == FGPositioned::NDB) {
     freq *= 100;
   }
   
-  return new FGNavRecord(type, ident, name, pos,
-    freq, range, multiuse);
-}
+  PositionedID r = cache->insertNavaid(type, ident, name, pos, freq, range, multiuse,
+                             arp.first, arp.second);
+  
+  if (isLoc) {
+    cache->setRunwayILS(arp.second, r);
+  }
 
+  if (navaid_dme) {
+    cache->setNavaidColocated(navaid_dme, r);
+  }
+  
+  return r;
+}
+  
 // load and initialize the navigational databases
-bool fgNavDBInit( FGNavList *navlist, FGNavList *loclist, FGNavList *gslist,
-                  FGNavList *dmelist, 
-                  FGNavList *tacanlist, FGNavList *carrierlist,
-                  FGTACANList *channellist)
+bool navDBInit(const SGPath& path)
 {
-    SG_LOG(SG_GENERAL, SG_INFO, "Loading Navaid Databases");
-
-    SGPath path( globals->get_fg_root() );
-    path.append( "Navaids/nav.dat" );
-
     sg_gzifstream in( path.str() );
     if ( !in.is_open() ) {
-        SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
-        exit(-1);
+        SG_LOG( SG_NAVAID, SG_ALERT, "Cannot open file: " << path.str() );
+      return false;
     }
+  
+  autoAlignLocalizers = fgGetBool("/sim/navdb/localizers/auto-align", true);
+  autoAlignThreshold = fgGetDouble( "/sim/navdb/localizers/auto-align-threshold-deg", 5.0 );
 
     // skip first two lines
     in >> skipeol;
     in >> skipeol;
 
     while (!in.eof()) {
-      FGNavRecord *r = createNavFromStream(in);
-      if (!r) {
-        continue;
-      }
-      
-      switch (r->type()) {
-      case FGPositioned::NDB:
-      case FGPositioned::VOR:
-        navlist->add(r);
-        break;
-        
-      case FGPositioned::ILS:
-      case FGPositioned::LOC:
-        loclist->add(r);
-        break;
-        
-      case FGPositioned::GS:
-        gslist->add(r);
-        break;
-      
-      case FGPositioned::DME:
-      {
-        dmelist->add(r);
-        string::size_type loc1= r->name().find( "TACAN", 0 );
-        string::size_type loc2 = r->name().find( "VORTAC", 0 );
-                       
-        if( loc1 != string::npos || loc2 != string::npos) {
-          tacanlist->add(r);
-        }
-
-        break;
-      }
-      
-      default:
-        throw sg_range_exception("got unsupported NavRecord type", "fgNavDBInit");
-      }
-
+      readNavFromStream(in);
       in >> skipcomment;
     } // of stream data loop
-
-// load the carrier navaids file
-    
-    string file, name;
-    path = globals->get_fg_root() ;
-    path.append( "Navaids/carrier_nav.dat" );
-    
-    file = path.str();
-    SG_LOG( SG_GENERAL, SG_INFO, "opening file: " << path.str() );
-    
+  
+  return true;
+}
+  
+  
+bool loadCarrierNav(const SGPath& path)
+{    
+    SG_LOG( SG_NAVAID, SG_INFO, "opening file: " << path.str() );    
     sg_gzifstream incarrier( path.str() );
     
     if ( !incarrier.is_open() ) {
-        SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
-        exit(-1);
+        SG_LOG( SG_NAVAID, SG_ALERT, "Cannot open file: " << path.str() );
+      return false;
     }
     
-    // skip first two lines
-    //incarrier >> skipeol;
-    //incarrier >> skipeol;
-    
     while ( ! incarrier.eof() ) {
-      FGNavRecord *r = createNavFromStream(incarrier);
-      if (!r) {
-        continue;
-      }
-      
-      carrierlist->add (r);
+      // force the type to be MOBILE_TACAN
+      readNavFromStream(incarrier, FGPositioned::MOBILE_TACAN);
     } // end while
 
-// end loading the carrier navaids file
-
-// load the channel/freqency file
-    string channel, freq;
-    path="";
-    path = globals->get_fg_root();
-    path.append( "Navaids/TACAN_freq.dat" );
-    
-    SG_LOG( SG_GENERAL, SG_INFO, "opening file: " << path.str() );
-        
+  return true;
+}
+  
+bool loadTacan(const SGPath& path, FGTACANList *channellist)
+{
+    SG_LOG( SG_NAVAID, SG_INFO, "opening file: " << path.str() );
     sg_gzifstream inchannel( path.str() );
     
     if ( !inchannel.is_open() ) {
-        SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
-        exit(-1);
+        SG_LOG( SG_NAVAID, SG_ALERT, "Cannot open file: " << path.str() );
+      return false;
     }
     
     // skip first line
@@ -223,89 +305,10 @@ bool fgNavDBInit( FGNavList *navlist, FGNavList *loclist, FGNavList *gslist,
         FGTACANRecord *r = new FGTACANRecord;
         inchannel >> (*r);
         channellist->add ( r );
-               //cout << "channel = " << r->get_channel() ;
-               //cout << " freq = " << r->get_freq() << endl;
        
     } // end while
 
- // end ReadChanFile
-
-
-  // flush all the parsed ils.xml data, we don't need it anymore,
-  // since it's been meregd into the FGNavRecords
-    static_airportIlsData.clear();
-
     return true;
 }
-
-SGPropertyNode* ilsDataForRunwayAndNavaid(FGRunway* aRunway, const std::string& aNavIdent)
-{
-  if (!fgGetBool("/sim/paths/use-custom-scenery-data")) {
-    return NULL; 
-  }
-  
-  if (!aRunway) {
-    return NULL;
-  }
   
-  FGAirport* apt = aRunway->airport();
-// find (or load) the airprot ILS data
-  AirportPropertyMap::iterator it = static_airportIlsData.find(apt);
-  if (it == static_airportIlsData.end()) {
-    SGPath path;
-    if (!XMLLoader::findAirportData(apt->ident(), "ils", path)) {
-    // no ils.xml file for this airpot, insert a NULL entry so we don't
-    // check again
-      static_airportIlsData.insert(it, std::make_pair(apt, SGPropertyNode_ptr()));
-      return NULL;
-    }
-    
-    SGPropertyNode_ptr rootNode = new SGPropertyNode;
-    readProperties(path.str(), rootNode);
-    it = static_airportIlsData.insert(it, std::make_pair(apt, rootNode));
-  } // of ils.xml file not loaded
-  
-  if (!it->second) {
-    return NULL;
-  }
-  
-// find the entry matching the runway
-  SGPropertyNode* runwayNode, *ilsNode;
-  for (int i=0; (runwayNode = it->second->getChild("runway", i)) != NULL; ++i) {
-    for (int j=0; (ilsNode = runwayNode->getChild("ils", j)) != NULL; ++j) {
-      // must match on both nav-ident and runway ident, to support the following:
-      // - runways with multiple distinct ILS installations (KEWD, for example)
-      // - runways where both ends share the same nav ident (LFAT, for example)
-      if ((ilsNode->getStringValue("nav-id") == aNavIdent) &&
-          (ilsNode->getStringValue("rwy") == aRunway->ident())) 
-      {
-        return ilsNode;
-      }
-    } // of ILS iteration
-  } // of runway iteration
-
-  return NULL;
-}
-
-FGRunway* getRunwayFromName(const std::string& aName)
-{
-  vector<string> parts = simgear::strutils::split(aName);
-  if (parts.size() < 2) {
-    SG_LOG(SG_GENERAL, SG_WARN, "getRunwayFromName: malformed name:" << aName);
-    return NULL;
-  }
-  
-  const FGAirport* apt = fgFindAirportID(parts[0]);
-  if (!apt) {
-    SG_LOG(SG_GENERAL, SG_DEBUG, "navaid " << aName << " associated with bogus airport ID:" << parts[0]);
-    return NULL;
-  }
-  
-  if (!apt->hasRunwayWithIdent(parts[1])) {
-    SG_LOG(SG_GENERAL, SG_DEBUG, "navaid " << aName << " associated with bogus runway ID:" << parts[1]);
-    return NULL;
-  }
-
-  return apt->getRunwayByIdent(parts[1]);
-}
+} // of namespace flightgear