diff --git a/src/Instrumentation/dclgps.cxx b/src/Instrumentation/dclgps.cxx index ecd4b2211..d1ab7ef02 100644 --- a/src/Instrumentation/dclgps.cxx +++ b/src/Instrumentation/dclgps.cxx @@ -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! } diff --git a/src/Instrumentation/marker_beacon.cxx b/src/Instrumentation/marker_beacon.cxx index 1cb91f0af..9d8022efe 100644 --- a/src/Instrumentation/marker_beacon.cxx +++ b/src/Instrumentation/marker_beacon.cxx @@ -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 ); diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 98a459d10..926b91da0 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -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" ); diff --git a/src/Navaids/navdb.cxx b/src/Navaids/navdb.cxx index 2efab7388..dc95774e4 100644 --- a/src/Navaids/navdb.cxx +++ b/src/Navaids/navdb.cxx @@ -29,24 +29,20 @@ #include #include -#include - #include #include - -#include -#include -#include
+#include +#include #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; - } - - /*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 = " <get_elev_ft() << endl; - cout << " freq = " << r->get_freq() << endl; - cout << " range = " << r->get_range() << endl; - cout << " name = " << r->get_name() << endl << endl; */ - - // 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 ); + 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 ){ - //cout << " name = " << r->get_name() ; - //cout << " freq = " << r->get_freq() ; - tacanlist->add( r ); - } - - dmelist->add( r ); - + if( loc1 != string::npos || loc2 != string::npos) { + tacanlist->add(r); } - - in >> skipcomment; - } + + break; + } + + default: + throw sg_range_exception("got unsupported NavRecord type", "fgNavDBInit"); + } + + 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 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; - } -} - diff --git a/src/Navaids/navdb.hxx b/src/Navaids/navdb.hxx index 3c2321221..cd524300f 100644 --- a/src/Navaids/navdb.hxx +++ b/src/Navaids/navdb.hxx @@ -28,31 +28,14 @@ #include #include -// #include -// #include -// #include - #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 diff --git a/src/Navaids/navlist.cxx b/src/Navaids/navlist.cxx index a145bac0d..1f19f626a 100644 --- a/src/Navaids/navlist.cxx +++ b/src/Navaids/navlist.cxx @@ -33,14 +33,10 @@ // 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; diff --git a/src/Navaids/navlist.hxx b/src/Navaids/navlist.hxx index b975f95b0..a1efefcf6 100644 --- a/src/Navaids/navlist.hxx +++ b/src/Navaids/navlist.hxx @@ -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 ); diff --git a/src/Navaids/navrecord.cxx b/src/Navaids/navrecord.cxx index 2aa25424f..2e0cf1633 100644 --- a/src/Navaids/navrecord.cxx +++ b/src/Navaids/navrecord.cxx @@ -25,111 +25,183 @@ #endif #include + #include +#include +#include +#include + #include "Navaids/navrecord.hxx" +#include "Airports/simple.hxx" +#include "Airports/runways.hxx" +#include "Main/fg_props.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("") -{ -} - -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), +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(); -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); - } -} - - -std::istream& operator>>( std::istream& in, FGNavRecord& n ) -{ - in >> n.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. + 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; - 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; - } - - // Remove any leading spaces before the name - while ( n.name.substr(0,1) == " " ) { - n.name = n.name.erase(0,1); - } - - 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); - } - - // 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 + case LOC: + case ILS: + case GS: + range = FG_LOC_DEFAULT_RANGE; + break; + + case DME: + range = FG_DME_DEFAULT_RANGE; + break; - 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; - } + default: + range = FG_LOC_DEFAULT_RANGE; } - - // 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; + } + } +SGVec3d FGNavRecord::get_cart() const +{ + return SGVec3d::fromGeod(geod()); +} + + +static FGPositioned::Type +mapRobinTypeToFGPType(int aTy) +{ + 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"); + } +} + +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; +} + +void FGNavRecord::initAirportRelation() +{ + if (type() < ILS || type() > IM) { + return; // not airport-located + } + + vector 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; + } + + if (parts.size() < 2) { + SG_LOG(SG_GENERAL, SG_ALERT, "can't parse navaid " << ident() + << " name for airport/runway:" << name); + return; + } + + 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(""), diff --git a/src/Navaids/navrecord.hxx b/src/Navaids/navrecord.hxx index 7ee00ed43..36cb4f297 100644 --- a/src/Navaids/navrecord.hxx +++ b/src/Navaids/navrecord.hxx @@ -26,36 +26,33 @@ #include -#include -#include +#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 -class FGNavRecord : public SGReferenced { +typedef FGPositioned::Type fg_nav_types; + +// forward decls +class FGRunway; + +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); - inline int get_type() const { return type; } + FGNavRecord(Type type, const std::string& ident, const std::string& name, + double lat, double lon, double aElevFt, + int freq, int range, double multiuse); - fg_nav_types get_fg_type() const; + inline double get_lon() const { return longitude(); } // degrees + inline double get_lat() const { return latitude(); } // degrees + inline double get_elev_ft() const { return elevation(); } + + const SGGeod& get_pos() const { return geod(); } + SGVec3d get_cart() 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 {