]> git.mxchange.org Git - flightgear.git/commitdiff
James Turner:
authorehofman <ehofman>
Fri, 12 Sep 2008 08:46:15 +0000 (08:46 +0000)
committerehofman <ehofman>
Fri, 12 Sep 2008 08:46:15 +0000 (08:46 +0000)
Convert FGNavRecord to inherit FGPositioned. This is much more self-contained than the FGRunway change, since FGNavRecord already had good encapsulation of its state. However, it's a large diff due to moving around two nasty pieces of code - the 'align navaid with extended runway centerline' logic and the 'penalise transmitters at the opposite runway end' logic.

In general things are more readable because I've replaced the Navaid type enum, and the use of Robin's integer type codes, with   switches on the FGPositioned::Type code - no more trying to recall that '6' is an outer marker in Robin's data. The creation code path is also pushed down from navdb into navrecord itself.

src/Instrumentation/dclgps.cxx
src/Instrumentation/marker_beacon.cxx
src/Main/fg_init.cxx
src/Navaids/navdb.cxx
src/Navaids/navdb.hxx
src/Navaids/navlist.cxx
src/Navaids/navlist.hxx
src/Navaids/navrecord.cxx
src/Navaids/navrecord.hxx

index ecd4b2211a32f619141c96502bc72483d4aee54a..d1ab7ef0279cd1a5857bc08a2082705d15140216 100644 (file)
@@ -1150,7 +1150,7 @@ FGNavRecord* DCLGPS::FindFirstVorById(const string& id, bool &multi, bool exact)
        if(nav.empty()) return(NULL);
        
        for(nav_list_iterator it = nav.begin(); it != nav.end(); ++it) {
-               if((*it)->get_type() == 3) return(*it);
+               if((*it)->type() == FGPositioned::VOR) return(*it);
        }
        return(NULL);   // Shouldn't get here!
 }
@@ -1170,7 +1170,7 @@ FGNavRecord* DCLGPS::FindFirstNDBById(const string& id, bool &multi, bool exact)
        if(nav.empty()) return(NULL);
        
        for(nav_list_iterator it = nav.begin(); it != nav.end(); ++it) {
-               if((*it)->get_type() == 2) return(*it);
+               if((*it)->type() == FGPositioned::NDB) return(*it);
        }
        return(NULL);   // Shouldn't get here!
 }
index 1cb91f0af1fe4256c3a6b7eaf318049716d10df5..9d8022efe070b83c28d68e281342d1cdece52a93 100644 (file)
@@ -269,11 +269,11 @@ void FGMarkerBeacon::search()
     fgMkrBeacType beacon_type = NOBEACON;
     bool inrange = false;
     if ( b != NULL ) {
-        if ( b->get_type() == 7 ) {
+        if ( b->type() == FGPositioned::OM ) {
             beacon_type = OUTER;
-        } else if ( b->get_type() == 8 ) {
+        } else if ( b->type() == FGPositioned::MM ) {
             beacon_type = MIDDLE;
-        } else if ( b->get_type() == 9 ) {
+        } else if ( b->type() == FGPositioned::IM ) {
             beacon_type = INNER;
         }
         inrange = check_beacon_range( pos, b );
index 98a459d10483c6c71abdd83319f42a219cc8b77e..926b91da0db62673474e3cda709d7992886f8833 100644 (file)
@@ -1159,22 +1159,11 @@ fgInitNav ()
     globals->set_carrierlist( carrierlist );
     globals->set_channellist( channellist );
 
-    if ( !fgNavDBInit(airports, navlist, loclist, gslist, dmelist, mkrlist, tacanlist, carrierlist, channellist) ) {
+    if ( !fgNavDBInit(navlist, loclist, gslist, dmelist, mkrlist, tacanlist, carrierlist, channellist) ) {
         SG_LOG( SG_GENERAL, SG_ALERT,
                 "Problems loading one or more navigational database" );
     }
 
-    if ( fgGetBool("/sim/navdb/localizers/auto-align", true) ) {
-        // align all the localizers with their corresponding runways
-        // since data sources are good for cockpit navigation
-        // purposes, but not always to the error tolerances needed to
-        // exactly place these items.
-        double threshold
-            = fgGetDouble( "/sim/navdb/localizers/auto-align-threshold-deg",
-                           5.0 );
-        fgNavDBAlignLOCwithRunway( airports, loclist, threshold );
-    }
-
     SG_LOG(SG_GENERAL, SG_INFO, "  Fixes");
     SGPath p_fix( globals->get_fg_root() );
     p_fix.append( "Navaids/fix.dat" );
index 2efab738876378aef4a2d8f7bbf05b71872c3594..dc95774e4b171b507f15a90979e807b1d765dfba 100644 (file)
 #include <string>
 
 #include <simgear/debug/logstream.hxx>
-#include <simgear/misc/sgstream.hxx>
-
 #include <simgear/math/sg_geodesy.hxx>
 #include <simgear/misc/strutils.hxx>
-
-#include <Airports/runways.hxx>
-#include <Airports/simple.hxx>
-#include <Main/globals.hxx>
+#include <simgear/structure/exception.hxx>
+#include <simgear/misc/sgstream.hxx>
 
 #include "navrecord.hxx"
 #include "navdb.hxx"
+#include "Main/globals.hxx"
 
 using std::string;
 
 
 // load and initialize the navigational databases
-bool fgNavDBInit( FGAirportList *airports,
-                  FGNavList *navlist, FGNavList *loclist, FGNavList *gslist,
+bool fgNavDBInit( FGNavList *navlist, FGNavList *loclist, FGNavList *gslist,
                   FGNavList *dmelist, FGNavList *mkrlist, 
                   FGNavList *tacanlist, FGNavList *carrierlist,
                   FGTACANList *channellist)
@@ -77,71 +73,56 @@ bool fgNavDBInit( FGAirportList *airports,
     in >> skipeol;
     in >> skipeol;
 
-    while ( ! in.eof() ) {
-        FGNavRecord *r = new FGNavRecord;
-        in >> (*r);
-        if ( r->get_type() > 95 ) {
-            delete r;
-            break;
+    while (!in.eof()) {
+      FGNavRecord *r = FGNavRecord::createFromStream(in);
+      if (!r) {
+        break;
+      }
+      
+      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::OM:
+      case FGPositioned::MM:
+      case FGPositioned::IM:
+        mkrlist->add(r);
+        break;
+      
+      case FGPositioned::DME:
+      {
+        dmelist->add(r);
+        string::size_type loc1= r->get_name().find( "TACAN", 0 );
+        string::size_type loc2 = r->get_name().find( "VORTAC", 0 );
+                       
+        if( loc1 != string::npos || loc2 != string::npos) {
+          tacanlist->add(r);
         }
 
-       /*cout << "id = " << r->get_ident() << endl;
-       cout << " type = " << r->get_type() << endl;
-       cout << " lon = " << r->get_lon() << endl;
-       cout << " lat = " << r->get_lat() << endl;
-       cout << " elev = " <<r->get_elev_ft() << endl;
-       cout << " freq = " << r->get_freq() << endl;
-       cout << " range = " << r->get_range() << endl;
-       cout << " name = " << r->get_name() << endl << endl; */
+        break;
+      }
+      
+      default:
+        throw sg_range_exception("got unsupported NavRecord type", "fgNavDBInit");
+      }
 
-        // fudge elevation to the field elevation if it's not specified
-        if ( fabs(r->get_elev_ft()) < 0.01 && r->get_apt_id().length() ) {
-            // cout << r->get_type() << " " << r->get_apt_id() << " zero elev"
-            //      << endl;
-            const FGAirport* a = airports->search( r->get_apt_id() );
-            if ( a ) {
-                r->set_elev_ft( a->getElevation() );
-                // cout << "  setting to " << a.elevation << endl;
-            }
-        }
-
-        if ( r->get_type() == 2 || r->get_type() == 3 ) {
-            // NDB=2, VOR=3
-            navlist->add( r );
-        } else if ( r->get_type() == 4 || r->get_type() == 5 ) {
-            // ILS=4, LOC(only)=5
-            loclist->add( r );
-        } else if ( r->get_type() == 6 ) {
-            // GS=6
-            gslist->add( r );
-        } else if ( r->get_type() == 7 || r->get_type() == 8
-                    || r->get_type() == 9 )
-        {
-            // Marker Beacon = 7,8,9
-            mkrlist->add( r );
-        } else if ( r->get_type() == 12 || r->get_type() == 13) {
-            // DME with ILS=12; standalone DME=13
-            string str1( r->get_name() );
-            string::size_type loc1= str1.find( "TACAN", 0 );
-            string::size_type loc2 = str1.find( "VORTAC", 0 );
-                       
-            if( loc1 != string::npos || loc2 != string::npos ){
-                 //cout << " name = " << r->get_name() ;
-                 //cout << " freq = " << r->get_freq() ;
-                 tacanlist->add( r );
-                 }
-                
-            dmelist->add( r );
-            
-        }
-               
-        in >> skipcomment;
-    }
+      in >> skipcomment;
+    } // of stream data loop
 
 // load the carrier navaids file
     
     string file, name;
-    path = "";
     path = globals->get_fg_root() ;
     path.append( "Navaids/carrier_nav.dat" );
     
@@ -160,16 +141,12 @@ bool fgNavDBInit( FGAirportList *airports,
     //incarrier >> skipeol;
     
     while ( ! incarrier.eof() ) {
-        FGNavRecord *r = new FGNavRecord;
-        incarrier >> (*r);
-        carrierlist->add ( r );
-        /*cout << " carrier lon: "<<  r->get_lon() ;
-        cout << " carrier lat: "<<  r->get_lat() ;
-        cout << " freq: " << r->get_freq() ;
-        cout << " carrier name: "<<  r->get_name() << endl;*/
-                
-        //if ( r->get_type() > 95 ) {
-        //   break;}
+      FGNavRecord *r = FGNavRecord::createFromStream(incarrier);
+      if (!r) {
+        continue;
+      }
+      
+      carrierlist->add (r);
     } // end while
 
 // end loading the carrier navaids file
@@ -206,95 +183,3 @@ bool fgNavDBInit( FGAirportList *airports,
 
     return true;
 }
-
-
-// Given a localizer record and it's corresponding runway record,
-// adjust the localizer position so it is in perfect alignment with
-// the runway.
-static void update_loc_position( FGNavRecord *loc, FGRunway *rwy,
-                                 double threshold )
-{
-    double hdg = rwy->headingDeg();
-    hdg += 180.0;
-    if ( hdg > 360.0 ) {
-        hdg -= 360.0;
-    }
-
-    // calculate runway threshold point
-    double thresh_lat, thresh_lon, return_az;
-    geo_direct_wgs_84 ( 0.0, rwy->latitude(), rwy->longitude(), hdg,
-                        rwy->lengthM() * 0.5,
-                        &thresh_lat, &thresh_lon, &return_az );
-    // cout << "Threshold = " << thresh_lat << "," << thresh_lon << endl;
-
-    // calculate distance from threshold to localizer
-    double az1, az2, dist_m;
-    geo_inverse_wgs_84( 0.0, loc->get_lat(), loc->get_lon(),
-                        thresh_lat, thresh_lon,
-                        &az1, &az2, &dist_m );
-    // cout << "Distance = " << dist_m << endl;
-
-    // back project that distance along the runway center line
-    double nloc_lat, nloc_lon;
-    geo_direct_wgs_84 ( 0.0, thresh_lat, thresh_lon, hdg + 180.0, 
-                        dist_m, &nloc_lat, &nloc_lon, &return_az );
-    // printf("New localizer = %.6f %.6f\n", nloc_lat, nloc_lon );
-
-    // sanity check, how far have we moved the localizer?
-    geo_inverse_wgs_84( 0.0, loc->get_lat(), loc->get_lon(),
-                        nloc_lat, nloc_lon,
-                        &az1, &az2, &dist_m );
-    // cout << "Distance moved = " << dist_m << endl;
-
-    // cout << "orig heading = " << loc->get_multiuse() << endl;
-    // cout << "new heading = " << rwy->_heading << endl;
-
-    double hdg_diff = loc->get_multiuse() - rwy->headingDeg();
-
-    // clamp to [-180.0 ... 180.0]
-    if ( hdg_diff < -180.0 ) {
-        hdg_diff += 360.0;
-    } else if ( hdg_diff > 180.0 ) {
-        hdg_diff -= 360.0;
-    }
-
-    if ( fabs(hdg_diff) <= threshold ) {
-        loc->set_lat( nloc_lat );
-        loc->set_lon( nloc_lon );
-        loc->set_multiuse( rwy->headingDeg() );
-    }
-}
-
-
-// This routines traverses the localizer list and attempts to match
-// each entry with it's corresponding runway.  When it is successful,
-// it then "moves" the localizer and updates it's heading so it
-// *perfectly* aligns with the runway, but is still the same distance
-// from the runway threshold.
-void fgNavDBAlignLOCwithRunway( FGAirportList *airports, FGNavList *loclist,
-                                double threshold ) {
-    nav_map_type navmap = loclist->get_navaids();
-
-    nav_map_const_iterator freq = navmap.begin();
-    while ( freq != navmap.end() ) {
-        nav_list_type locs = freq->second;
-        nav_list_const_iterator loc = locs.begin();
-        while ( loc != locs.end() ) {
-          vector<string> parts = simgear::strutils::split((*loc)->get_name());
-          if (parts.size() < 2) {
-            SG_LOG(SG_GENERAL, SG_ALERT, "can't parse navaid " << (*loc)->get_ident() 
-              << " name for airport/runway:" << (*loc)->get_name());
-            continue;
-          }
-          
-          FGAirport* airport = airports->search(parts[0]);
-          if (!airport) continue; // not found
-            
-          FGRunway* r = airport->getRunwayByIdent(parts[1]);
-          update_loc_position( (*loc), r, threshold );
-          ++loc;
-        }
-        ++freq;
-    }
-}
-
index 3c2321221261e57c58f0cb17a797f85493c9a5f4..cd524300f951dd5af5756991fa3914ba80716322 100644 (file)
 #include <simgear/compiler.h>
 #include <simgear/misc/sg_path.hxx>
 
-// #include <map>
-// #include <vector>
-// #include <string>
-
 #include "navlist.hxx"
 #include "fixlist.hxx"
 
-// using std::map;
-// using std::vector;
-// using std::string;
-
-
 // load and initialize the navigational databases
-bool fgNavDBInit( FGAirportList *airports,
-                  FGNavList *navlist, FGNavList *loclist, FGNavList *gslist,
+bool fgNavDBInit( FGNavList *navlist, FGNavList *loclist, FGNavList *gslist,
                   FGNavList *dmelist, FGNavList *mkrbeacons,
                   FGNavList *tacanlist, FGNavList *carrierlist,
                   FGTACANList *channellist );
 
-// This routines traverses the localizer list and attempts to match
-// each entry with it's corresponding runway.  When it is successful,
-// it then "moves" the localizer and updates it's heading so it
-// *perfectly* aligns with the runway, but is still the same distance
-// from the runway threshold.
-void fgNavDBAlignLOCwithRunway( FGAirportList *airport, FGNavList *loclist,
-                                double threshold );
 
 #endif // _FG_NAVDB_HXX
index a145bac0d9d5d7090e4c237597062da2234d6366..1f19f626a81708528d9a7dfd1323da6a132cb492 100644 (file)
 
 
 // Return true if the nav record matches the type
-static bool isTypeMatch(const FGNavRecord* n, fg_nav_types type) {
-    switch(type) {
-        case FG_NAV_ANY: return(true);
-        case FG_NAV_VOR: return(n->get_type() == 3);
-        case FG_NAV_NDB: return(n->get_type() == 2);
-        case FG_NAV_ILS: return(n->get_type() == 4);   // Note - very simplified, only matches loc as part of full ILS.
-        default: return false;
-    }
+static bool isTypeMatch(const FGNavRecord* n, fg_nav_types type)
+{
+  if (type == FG_NAV_ANY) return true;
+  return type == n->type();
 }
 
 
@@ -174,14 +170,13 @@ FGNavRecord *FGNavList::findByIdent( const char* ident,
 
 nav_list_type FGNavList::findFirstByIdent( const string& ident, fg_nav_types type, bool exact)
 {
-    nav_list_type n2;
-    n2.clear();
-
-    int iType;
-    if(type == FG_NAV_VOR) iType = 3;
-    else if(type == FG_NAV_NDB) iType = 2;
-    else return(n2);
+  nav_list_type n2;
+  n2.clear();
 
+  if ((type != FG_NAV_VOR) && (type != FG_NAV_NDB)) {
+    return n2;
+  }
+  
     nav_ident_map_iterator it;
     if(exact) {
         it = ident_navaids.find(ident);
@@ -195,7 +190,7 @@ nav_list_type FGNavList::findFirstByIdent( const string& ident, fg_nav_types typ
             // Remove the types that don't match request.
             for(nav_list_iterator it0 = n0.begin(); it0 != n0.end();) {
                 FGNavRecord* nv = *it0;
-                if(nv->get_type() == iType) {
+                if(nv->type() == type) {
                     typeMatch = true;
                     ++it0;
                 } else {
@@ -231,7 +226,7 @@ nav_list_type FGNavList::findFirstByIdent( const string& ident, fg_nav_types typ
         n2.clear();
         for(nav_list_iterator it2 = n1.begin(); it2 != n1.end(); ++it2) {
             FGNavRecord* nv = *it2;
-            if(nv->get_type() == iType) n2.push_back(nv);
+            if(nv->type() == type) n2.push_back(nv);
         }
         return(n2);
     }
@@ -261,6 +256,48 @@ FGNavRecord *FGNavList::findByIdentAndFreq( const char* ident, const double freq
     return NULL;
 }
 
+// LOC, ILS, GS, and DME antenna's could potentially be
+// installed at the opposite end of the runway.  So it's not
+// enough to simply find the closest antenna with the right
+// frequency.  We need the closest antenna with the right
+// frequency that is most oriented towards us.  (We penalize
+// stations that are facing away from us by adding 5000 meters
+// which is further than matching stations would ever be
+// placed from each other.  (Do the expensive check only for
+// directional atennas and only when there is a chance it is
+// the closest station.)
+
+static bool penaltyForNav(FGNavRecord* aNav, const SGVec3d &aPos)
+{
+  switch (aNav->type()) {
+  case FGPositioned::ILS:
+  case FGPositioned::LOC:
+  case FGPositioned::GS:
+// FIXME
+//  case FGPositioned::DME: we can't get the heading for a DME transmitter, oops
+    break;
+  default:
+    return false;
+  }
+  
+  double hdg_deg = 0.0;
+  if (aNav->type() == FGPositioned::GS) {
+    int tmp = (int)(aNav->get_multiuse() / 1000.0);
+    hdg_deg = aNav->get_multiuse() - (tmp * 1000);
+  } else {    
+    hdg_deg = aNav->get_multiuse();
+  }
+  
+  double az1 = 0.0, az2 = 0.0, s = 0.0;
+  SGGeod geod = SGGeod::fromCart(aPos);
+  geo_inverse_wgs_84( geod, aNav->geod(), &az1, &az2, &s);
+  az1 = az1 - hdg_deg;
+  
+  if ( az1 >  180.0) az1 -= 360.0;
+  if ( az1 < -180.0) az1 += 360.0;
+  
+  return fabs(az1 > 90.0);
+}
 
 // Given a point and a list of stations, return the closest one to the
 // specified point.
@@ -279,54 +316,12 @@ FGNavRecord *FGNavList::findNavFromList( const SGVec3d &aircraft,
         FGNavRecord *station = *it;
         // cout << "testing " << current->get_ident() << endl;
         d2 = distSqr(station->get_cart(), aircraft);
-
-        // cout << "  dist = " << sqrt(d)
-        //      << "  range = " << current->get_range() * SG_NM_TO_METER
-        //      << endl;
-
-        // LOC, ILS, GS, and DME antenna's could potentially be
-        // installed at the opposite end of the runway.  So it's not
-        // enough to simply find the closest antenna with the right
-        // frequency.  We need the closest antenna with the right
-        // frequency that is most oriented towards us.  (We penalize
-        // stations that are facing away from us by adding 5000 meters
-        // which is further than matching stations would ever be
-        // placed from each other.  (Do the expensive check only for
-        // directional atennas and only when there is a chance it is
-        // the closest station.)
-        int type = station->get_type();
-        if ( d2 < min_dist &&
-             (type == 4 || type == 5 || type == 6 || type == 12 || type == 13) )
+        if ( d2 < min_dist && penaltyForNav(station, aircraft))
         {
-            double hdg_deg = 0.0;
-            if ( type == 4 || type == 5 ){
-                hdg_deg = station->get_multiuse();
-
-            } else if ( type == 6 ) {
-                int tmp = (int)(station->get_multiuse() / 1000.0);
-                hdg_deg = station->get_multiuse() - (tmp * 1000);
-
-            } else if ( type == 12 || type == 13 ) {
-                // oops, Robin's data format doesn't give us the
-                // needed information to compute a heading for a DME
-                // transmitter.  FIXME Robin!
-            }
-
-            double az1 = 0.0, az2 = 0.0, s = 0.0;
-            SGGeod geod = SGGeod::fromCart(aircraft);
-            geo_inverse_wgs_84( geod, station->get_pos(), &az1, &az2, &s);
-            az1 = az1 - station->get_multiuse();
-            if ( az1 >  180.0) az1 -= 360.0;
-            if ( az1 < -180.0) az1 += 360.0;
-            // penalize opposite facing stations by adding 5000 meters
-            // (squared) which is further than matching stations would
-            // ever be placed from each other.
-            if ( fabs(az1) > 90.0 ) {
-                double dist = sqrt(d2);
-                d2 = (dist + 5000) * (dist + 5000);
-            }
+          double dist = sqrt(d2);
+          d2 = (dist + 5000) * (dist + 5000);
         }
-
+        
         if ( d2 < min_dist ) {
             min_dist = d2;
             nav = station;
index b975f95b0e9b197c229f605b4a39fdd4b1ee45f2..a1efefcf6251be6bbbc8ed3a82ee71b57c935689 100644 (file)
@@ -94,7 +94,7 @@ public:
     // (by ASCII code value) to that supplied.
     // Supplying true for exact forces only exact matches to be returned (similar to above function)
     // Returns an empty list if no match found - calling function should check for this!
-    nav_list_type findFirstByIdent( const string& ident, fg_nav_types type, bool exact = false );
+    nav_list_type findFirstByIdent( const string& ident, FGPositioned::Type type, bool exact = false );
 
     // Given an Ident and optional freqency, return the first matching
     // station.
@@ -102,7 +102,8 @@ public:
                                      const double freq = 0.0 );
 
     // returns the closest entry to the give lon/lat/elev
-    FGNavRecord *findClosest( double lon_rad, double lat_rad, double elev_m, fg_nav_types type = FG_NAV_ANY );
+    FGNavRecord *findClosest( double lon_rad, double lat_rad, double elev_m, 
+      FGPositioned::Type type = FG_NAV_ANY );
 
     // given a frequency returns the first matching entry
     FGNavRecord *findStationByFreq( double frequency );
index 2aa25424fe9dfc0c9bb36aa9c0a9a4675eb0f6ef..2e0cf1633f1848ecc7af2704f6e5819970cdfd64 100644 (file)
 #endif
 
 #include <istream>
-#include <simgear/misc/sgstream.hxx>
-#include "Navaids/navrecord.hxx"
 
-FGNavRecord::FGNavRecord(void) :
-    type(0),
-    pos(SGGeod::fromDeg(0, 0)),
-    cart(0, 0, 0),
-    freq(0),
-    range(0),
-    multiuse(0.0),
-    ident(""),
-    name(""),
-    apt_id(""),
-    serviceable(true),
-    trans_ident("")
-{
-}
+#include <simgear/misc/sgstream.hxx>
+#include <simgear/structure/exception.hxx>
+#include <simgear/misc/strutils.hxx>
+#include <simgear/debug/logstream.hxx>
 
-FGNavRecord::FGNavRecord(int aTy, const std::string& aIdent, 
-  const std::string& aName, const std::string& aAirport,
-  double lat, double lon, int aFreq, int aRange, double aMultiuse) :
-  type(aTy),
+#include "Navaids/navrecord.hxx"
+#include "Airports/simple.hxx"
+#include "Airports/runways.hxx"
+#include "Main/fg_props.hxx"
+
+FGNavRecord::FGNavRecord(Type aTy, const std::string& aIdent, 
+  const std::string& aName,
+  double lat, double lon, double aElevFt,
+  int aFreq, int aRange, double aMultiuse) :
+  FGPositioned(aTy, aIdent, lat, lon, aElevFt),
   freq(aFreq),
   range(aRange),
   multiuse(aMultiuse),
-  ident(aIdent),
   name(aName),
-  apt_id(aAirport),
   serviceable(true),
   trans_ident(aIdent)
-{
-  pos = SGGeod::fromDeg(lon, lat);
-  cart = SGVec3d::fromGeod(pos);
+{ 
+  initAirportRelation();
+
+  // Ranges are included with the latest data format, no need to
+  // assign our own defaults, unless the range is not set for some
+  // reason.
+  if (range < 0.1) {
+    SG_LOG(SG_GENERAL, SG_WARN, "navaid " << ident() << " has no range set, using defaults");
+    switch (type()) {
+    case NDB:
+    case VOR:
+      range = FG_NAV_DEFAULT_RANGE;
+      break;
+    
+    case LOC:
+    case ILS:
+    case GS:
+      range = FG_LOC_DEFAULT_RANGE;
+      break;
+      
+    case DME:
+      range = FG_DME_DEFAULT_RANGE;
+      break;
+    
+    default:
+      range = FG_LOC_DEFAULT_RANGE;
+    }
+  }
+  
 }
 
-fg_nav_types FGNavRecord::get_fg_type() const {
-    switch(type) {
-    case 2: return(FG_NAV_NDB);
-    case 3: return(FG_NAV_VOR);
-    case 4: return(FG_NAV_ILS);
-    default: return(FG_NAV_ANY);
-    }
+SGVec3d FGNavRecord::get_cart() const
+{
+  return SGVec3d::fromGeod(geod());
 }
 
 
-std::istream& operator>>( std::istream& in, FGNavRecord& n )
+static FGPositioned::Type
+mapRobinTypeToFGPType(int aTy)
 {
-    in >> n.type;
-    
-    if ( n.type == 99 ) {
-        return in >> skipeol;
-    }
-
-    double lat, lon, elev_ft;
-    in >> lat >> lon >> elev_ft >> n.freq >> n.range >> n.multiuse
-       >> n.ident;
-    n.pos.setLatitudeDeg(lat);
-    n.pos.setLongitudeDeg(lon);
-    n.pos.setElevationFt(elev_ft);
-    getline( in, n.name );
-
-    // silently multiply adf frequencies by 100 so that adf
-    // vs. nav/loc frequency lookups can use the same code.
-    if ( n.type == 2 ) {
-        n.freq *= 100;
-    }
+  switch (aTy) {
+ // case 1:
+  case 2: return FGPositioned::NDB;
+  case 3: return FGPositioned::VOR;
+  case 4: return FGPositioned::LOC;
+  case 5: return FGPositioned::ILS;
+  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
+  default:
+    throw sg_range_exception("Got a nav.dat type we don't recgonize", "FGNavRecord::createFromStream");
+  }
+}
 
-    // Remove any leading spaces before the name
-    while ( n.name.substr(0,1) == " " ) {
-        n.name = n.name.erase(0,1);
-    }
+FGNavRecord* FGNavRecord::createFromStream(std::istream& aStream)
+{
+  int rawType;
+  aStream >> rawType;
+  if (aStream.eof()) {
+    return NULL; // happens with, eg, carrier_nav.dat
+  }
+  
+  Type type = mapRobinTypeToFGPType(rawType);
+  if (type == INVALID) return NULL;
+  
+  double lat, lon, elev_ft, multiuse;
+  int freq, range;
+  std::string apt_id, name, ident;
+  aStream >> lat >> lon >> elev_ft >> freq >> range >> multiuse >> ident;
+  getline(aStream, name);
+  
+  // silently multiply adf frequencies by 100 so that adf
+  // vs. nav/loc frequency lookups can use the same code.
+  if (type == NDB) {
+    freq *= 100;
+  }
+  
+  FGNavRecord* result = new FGNavRecord(type, ident,
+    simgear::strutils::strip(name), 
+    lat, lon, elev_ft, freq, range, multiuse);
+    
+  return result;
+}
 
-    if ( n.type >= 4 && n.type <= 9 ) {
-        // these types are always associated with an airport id
-        std::string::size_type pos = n.name.find(" ");
-        n.apt_id = n.name.substr(0, pos);
+void FGNavRecord::initAirportRelation()
+{
+  if (type() < ILS || type() > IM) {
+    return; // not airport-located
+  }
+  
+  vector<string> parts = simgear::strutils::split(name);
+  apt_id = parts[0];
+  const FGAirport* apt = fgFindAirportID(apt_id);
+  
+  if (!apt) {
+    SG_LOG(SG_GENERAL, SG_WARN, "navaid " << ident() << " associated with bogus airport ID:" << apt_id);
+    return;
+  }
+  
+  // fudge elevation to the field elevation if it's not specified
+  if (fabs(elevation()) < 0.01) {
+    mPosition.setElevationFt(apt->getElevation());
+  }
+  
+  // align localizers with their runway
+  if ((type() == ILS) || (type() == LOC)) {
+    if (!fgGetBool("/sim/navdb/localizers/auto-align", true)) {
+      return;
     }
-
-    // Ranges are included with the latest data format, no need to
-    // assign our own defaults, unless the range is not set for some
-    // reason.
-
-    if ( n.range < 0.1 ) {
-        // assign default ranges
     
-        if ( n.type == 2 || n.type == 3 ) {
-            n.range = FG_NAV_DEFAULT_RANGE;
-        } else if ( n.type == 4 || n.type == 5 || n.type == 6 ) {
-            n.range = FG_LOC_DEFAULT_RANGE;
-        } else if ( n.type == 12 ) {
-            n.range = FG_DME_DEFAULT_RANGE;
-        } else {
-            n.range = FG_LOC_DEFAULT_RANGE;
-        }
+    if (parts.size() < 2) {
+      SG_LOG(SG_GENERAL, SG_ALERT, "can't parse navaid " << ident() 
+              << " name for airport/runway:" << name);
+      return;
     }
-
-    // transmitted ident (same as ident unless modeling a fault)
-    n.trans_ident = n.ident;
-
-    // generate cartesian coordinates
-    n.cart = SGVec3d::fromGeod(n.pos);
-
-    return in;
+    
+     double threshold
+            = fgGetDouble( "/sim/navdb/localizers/auto-align-threshold-deg",
+                           5.0 );
+    FGRunway* runway = apt->getRunwayByIdent(parts[1]);
+    alignLocaliserWithRunway(runway, threshold);
+  }
 }
 
+void FGNavRecord::alignLocaliserWithRunway(FGRunway* aRunway, double aThreshold)
+{
+// find the distance from the threshold to the localizer
+  SGGeod runwayThreshold(aRunway->threshold());
+  double dist, az1, az2;
+  SGGeodesy::inverse(mPosition, runwayThreshold, az1, az2, dist);
+
+// back project that distance along the runway center line
+  SGGeod newPos = aRunway->pointOnCenterline(dist);
+
+  double hdg_diff = get_multiuse() - aRunway->headingDeg();
+
+  // clamp to [-180.0 ... 180.0]
+  if ( hdg_diff < -180.0 ) {
+      hdg_diff += 360.0;
+  } else if ( hdg_diff > 180.0 ) {
+      hdg_diff -= 360.0;
+  }
+
+  if ( fabs(hdg_diff) <= aThreshold ) {
+    mPosition = newPos;
+    set_multiuse( aRunway->headingDeg() );
+  } else {
+    SG_LOG(SG_GENERAL, SG_WARN, "localizer:" << ident() << ", aligning with runway " 
+      << aRunway->_rwy_no << " exceeded heading threshold");
+  }
+}
 
 FGTACANRecord::FGTACANRecord(void) :
     channel(""),
index 7ee00ed43b13447c8288f422d5cfa9ce26aa99e6..36cb4f2976fa7e4e4201148010cecea0a4600f5d 100644 (file)
 
 #include <iosfwd>
 
-#include <simgear/math/SGMath.hxx>
-#include <simgear/structure/SGReferenced.hxx>
+#include "positioned.hxx"
 
 #define FG_NAV_DEFAULT_RANGE 50 // nm
 #define FG_LOC_DEFAULT_RANGE 18 // nm
 #define FG_DME_DEFAULT_RANGE 50 // nm
 #define FG_NAV_MAX_RANGE 300    // nm
 
-// Shield the rest of FG from possibly changing details of Robins navaid type numbering system.
-// Currently only the GPS code uses this - extra types (LOC, GS etc) may need to be added
-// should other FG code choose to use this. 
-enum fg_nav_types {
-    FG_NAV_VOR,
-    FG_NAV_NDB,
-    FG_NAV_ILS,
-    FG_NAV_ANY
-};
+// FIXME - get rid of these, and use the real enum directly
+#define FG_NAV_VOR FGPositioned::VOR
+#define FG_NAV_NDB FGPositioned::NDB
+#define FG_NAV_ILS FGPositioned::ILS
+#define FG_NAV_ANY FGPositioned::INVALID
+
+typedef FGPositioned::Type fg_nav_types;
+
+// forward decls
+class FGRunway;
 
-class FGNavRecord : public SGReferenced {
+class FGNavRecord : public FGPositioned 
+{
 
-    int type;
-    SGGeod pos;                // location in geodetic coords (degrees)
-    SGVec3d cart;              // location in cartesian coords (earth centered)
     int freq;
     int range;
     double multiuse;            // can be slaved variation of VOR
                                 // (degrees) or localizer heading
                                 // (degrees) or dme bias (nm)
 
-    std::string ident;         // navaid ident
     std::string name;                // verbose name in nav database
     std::string apt_id;              // corresponding airport id
 
@@ -63,36 +60,41 @@ class FGNavRecord : public SGReferenced {
     bool serviceable;          // for failure modeling
     std::string trans_ident;         // for failure modeling
 
+  /**
+   * Helper to init data when a navrecord is associated with an airport
+   */
+  void initAirportRelation();
+  
+  void alignLocaliserWithRunway(FGRunway* aRunway, double aThreshold);
 public:
-    FGNavRecord(void);
-    inline ~FGNavRecord(void) {}
+  inline ~FGNavRecord(void) {}
 
-    FGNavRecord(int type, const std::string& ident, const std::string& name, const std::string& airport,
-      double lat, double lon, int freq, int range, double multiuse);
+  static FGNavRecord* createFromStream(std::istream& aStream);
+
+    FGNavRecord(Type type, const std::string& ident, const std::string& name,
+      double lat, double lon, double aElevFt,
+      int freq, int range, double multiuse);
+    
+    inline double get_lon() const { return longitude(); } // degrees
+    inline double get_lat() const { return latitude(); } // degrees
+    inline double get_elev_ft() const { return elevation(); }
 
-    inline int get_type() const { return type; }
+    const SGGeod& get_pos() const { return geod(); }
+    SGVec3d get_cart() const;
     
-    fg_nav_types get_fg_type() const;
+    Type get_fg_type() const { return type(); }
     
-    inline double get_lon() const { return pos.getLongitudeDeg(); } // degrees
-    inline void set_lon( double l ) { pos.setLongitudeDeg(l); } // degrees
-    inline double get_lat() const { return pos.getLatitudeDeg(); } // degrees
-    inline void set_lat( double l ) { pos.setLatitudeDeg(l); } // degrees
-    inline double get_elev_ft() const { return pos.getElevationFt(); }
-    inline void set_elev_ft( double e ) { pos.setElevationFt(e); }
-    const SGGeod& get_pos() const { return pos; }
-    const SGVec3d& get_cart() const { return cart; }
     inline int get_freq() const { return freq; }
     inline int get_range() const { return range; }
     inline double get_multiuse() const { return multiuse; }
     inline void set_multiuse( double m ) { multiuse = m; }
-    inline const char *get_ident() const { return ident.c_str(); }
+    inline const char *get_ident() const { return ident().c_str(); }
     inline const std::string& get_name() const { return name; }
     inline const std::string& get_apt_id() const { return apt_id; }
     inline bool get_serviceable() const { return serviceable; }
     inline const char *get_trans_ident() const { return trans_ident.c_str(); }
 
-    friend std::istream& operator>> ( std::istream&, FGNavRecord& );
+  
 };
 
 class FGTACANRecord : public SGReferenced {