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.
This commit is contained in:
parent
a18a0593d4
commit
bb2b03c7e3
9 changed files with 318 additions and 391 deletions
|
@ -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!
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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" );
|
||||
|
|
|
@ -29,24 +29,20 @@
|
|||
#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;
|
||||
while (!in.eof()) {
|
||||
FGNavRecord *r = FGNavRecord::createFromStream(in);
|
||||
if (!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 = " <<r->get_elev_ft() << endl;
|
||||
cout << " freq = " << r->get_freq() << endl;
|
||||
cout << " range = " << r->get_range() << endl;
|
||||
cout << " name = " << r->get_name() << endl << endl; */
|
||||
switch (r->type()) {
|
||||
case FGPositioned::NDB:
|
||||
case FGPositioned::VOR:
|
||||
navlist->add(r);
|
||||
break;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
case FGPositioned::ILS:
|
||||
case FGPositioned::LOC:
|
||||
loclist->add(r);
|
||||
break;
|
||||
|
||||
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 )
|
||||
case FGPositioned::GS:
|
||||
gslist->add(r);
|
||||
break;
|
||||
|
||||
case FGPositioned::OM:
|
||||
case FGPositioned::MM:
|
||||
case FGPositioned::IM:
|
||||
mkrlist->add(r);
|
||||
break;
|
||||
|
||||
case FGPositioned::DME:
|
||||
{
|
||||
// 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 );
|
||||
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 );
|
||||
if( loc1 != string::npos || loc2 != string::npos) {
|
||||
tacanlist->add(r);
|
||||
}
|
||||
|
||||
dmelist->add( r );
|
||||
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;*/
|
||||
FGNavRecord *r = FGNavRecord::createFromStream(incarrier);
|
||||
if (!r) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//if ( r->get_type() > 95 ) {
|
||||
// break;}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,31 +28,14 @@
|
|||
#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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -177,10 +173,9 @@ nav_list_type FGNavList::findFirstByIdent( const string& ident, fg_nav_types typ
|
|||
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);
|
||||
if ((type != FG_NAV_VOR) && (type != FG_NAV_NDB)) {
|
||||
return n2;
|
||||
}
|
||||
|
||||
nav_ident_map_iterator it;
|
||||
if(exact) {
|
||||
|
@ -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,53 +316,11 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
if ( d2 < min_dist ) {
|
||||
min_dist = d2;
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -25,111 +25,183 @@
|
|||
#endif
|
||||
|
||||
#include <istream>
|
||||
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
|
||||
if ( n.range < 0.1 ) {
|
||||
// assign default ranges
|
||||
case LOC:
|
||||
case ILS:
|
||||
case GS:
|
||||
range = FG_LOC_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;
|
||||
case DME:
|
||||
range = FG_DME_DEFAULT_RANGE;
|
||||
break;
|
||||
|
||||
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<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;
|
||||
}
|
||||
|
||||
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(""),
|
||||
|
|
|
@ -26,36 +26,33 @@
|
|||
|
||||
#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
|
||||
|
||||
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) {}
|
||||
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue