From bb2b03c7e392e107aeaf7dbc4eecc59064b28512 Mon Sep 17 00:00:00 2001 From: ehofman Date: Fri, 12 Sep 2008 08:46:15 +0000 Subject: [PATCH] James Turner: 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 | 4 +- src/Instrumentation/marker_beacon.cxx | 6 +- src/Main/fg_init.cxx | 13 +- src/Navaids/navdb.cxx | 221 ++++++------------------ src/Navaids/navdb.hxx | 19 +- src/Navaids/navlist.cxx | 121 +++++++------ src/Navaids/navlist.hxx | 5 +- src/Navaids/navrecord.cxx | 240 +++++++++++++++++--------- src/Navaids/navrecord.hxx | 66 +++---- 9 files changed, 311 insertions(+), 384 deletions(-) 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; + 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 = " <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 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 "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 +#include +#include +#include -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 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(""), 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 + +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 { -- 2.39.5