1
0
Fork 0

Implement a persistent cache for navigation data.

Cache the parsed navigation and airport data in a binary file to reduce
startup times and memory consumption (since only referenced FGPositioned
elements are held in memory).

Data will be reimported when the mod-time of any input file is changed.
If a global file is changed (nav.dat, awy.dat, apt.dat, etc), the cache
will be completely rebuilt, which takes approximately 30 seconds on
moderate hardware. (Future work may reduce this).
This commit is contained in:
James Turner 2012-08-28 00:26:36 +01:00
parent 48c26079e1
commit 9b900e9430
54 changed files with 149152 additions and 1903 deletions

View file

@ -1,33 +1,26 @@
#include "CommStation.hxx" #include "CommStation.hxx"
#include <map> #include <Navaids/NavDataCache.hxx>
namespace {
typedef std::multimap<int, flightgear::CommStation*> FrequencyMap;
static FrequencyMap static_frequencies;
typedef std::pair<FrequencyMap::const_iterator, FrequencyMap::const_iterator> FrequencyMapRange;
} // of anonymous namespace
namespace flightgear { namespace flightgear {
CommStation::CommStation(const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq) : CommStation::CommStation(PositionedID aGuid, const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq) :
FGPositioned(t, name, pos), FGPositioned(aGuid, t, name, pos),
mRangeNM(range), mRangeNM(range),
mFreqKhz(freq), mFreqKhz(freq),
mAirport(NULL) mAirport(0)
{ {
static_frequencies.insert(std::make_pair(freq, this));
init(true);
} }
void CommStation::setAirport(FGAirport* apt) void CommStation::setAirport(PositionedID apt)
{ {
mAirport = apt; mAirport = apt;
} }
FGAirport* CommStation::airport() const
{
return (FGAirport*) NavDataCache::instance()->loadById(mAirport);
}
double CommStation::freqMHz() const double CommStation::freqMHz() const
{ {
@ -37,23 +30,7 @@ double CommStation::freqMHz() const
CommStation* CommStation*
CommStation::findByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt) CommStation::findByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt)
{ {
FrequencyMapRange range = static_frequencies.equal_range(freqKhz); return (CommStation*) NavDataCache::instance()->findCommByFreq(freqKhz, pos, filt).ptr();
FGPositioned::List results;
for (; range.first != range.second; ++range.first) {
CommStation* sta = range.first->second;
if (filt && !filt->pass(sta)) {
continue; // filtered out
}
results.push_back(sta);
}
if (results.empty()) {
return NULL;
}
FGPositioned::sortByRange(results, pos);
return (CommStation*) results.front().ptr();
} }
} // of namespace flightgear } // of namespace flightgear

View file

@ -11,10 +11,10 @@ namespace flightgear
class CommStation : public FGPositioned class CommStation : public FGPositioned
{ {
public: public:
CommStation(const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq); CommStation(PositionedID aGuid, const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq);
void setAirport(FGAirport* apt); void setAirport(PositionedID apt);
FGAirport* airport() const { return mAirport; } FGAirport* airport() const;
int rangeNm() const int rangeNm() const
{ return mRangeNM; } { return mRangeNM; }
@ -28,7 +28,7 @@ public:
private: private:
int mRangeNM; int mRangeNM;
int mFreqKhz; int mFreqKhz;
FGAirport* mAirport; PositionedID mAirport;
}; };
} // of namespace flightgear } // of namespace flightgear

View file

@ -39,20 +39,22 @@
#include <simgear/misc/sgstream.hxx> #include <simgear/misc/sgstream.hxx>
#include <simgear/misc/strutils.hxx> #include <simgear/misc/strutils.hxx>
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
#include <simgear/bucket/newbucket.hxx> #include <simgear/misc/sg_path.hxx>
#include <string> #include <string>
#include "simple.hxx" #include "simple.hxx"
#include "runways.hxx" #include "runways.hxx"
#include "pavement.hxx" #include "pavement.hxx"
#include <Navaids/NavDataCache.hxx>
#include <ATC/CommStation.hxx> #include <ATC/CommStation.hxx>
#include <iostream> #include <iostream>
using namespace std; using namespace std;
typedef SGSharedPtr<FGPavement> FGPavementPtr;
static FGPositioned::Type fptypeFromRobinType(int aType) static FGPositioned::Type fptypeFromRobinType(int aType)
{ {
switch (aType) { switch (aType) {
@ -65,23 +67,26 @@ static FGPositioned::Type fptypeFromRobinType(int aType)
} }
} }
namespace flightgear
{
class APTLoader class APTLoader
{ {
public: public:
APTLoader() APTLoader()
: last_apt_id(""), : last_apt_id(""),
last_apt_name(""),
last_apt_elev(0.0), last_apt_elev(0.0),
last_apt_info(""), last_apt_info("")
last_apt_type("")
{}
void parseAPT(const string &aptdb_file)
{ {
sg_gzifstream in( aptdb_file ); currentAirportID = 0;
cache = NavDataCache::instance();
}
void parseAPT(const SGPath &aptdb_file)
{
sg_gzifstream in( aptdb_file.str() );
if ( !in.is_open() ) { if ( !in.is_open() ) {
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << aptdb_file ); SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << aptdb_file );
@ -148,6 +153,8 @@ public:
double elev = atof( token[3].c_str() ); double elev = atof( token[3].c_str() );
tower = SGGeod::fromDegFt(lon, lat, elev + last_apt_elev); tower = SGGeod::fromDegFt(lon, lat, elev + last_apt_elev);
got_tower = true; got_tower = true;
cache->insertTower(currentAirportID, tower);
} else if ( line_id == 19 ) { } else if ( line_id == 19 ) {
// windsock entry (ignore) // windsock entry (ignore)
} else if ( line_id == 20 ) { } else if ( line_id == 20 ) {
@ -181,7 +188,7 @@ public:
} }
} }
addAirport(); finishAirport();
} }
private: private:
@ -192,48 +199,50 @@ private:
int rwy_count; int rwy_count;
bool got_tower; bool got_tower;
string last_apt_id; string last_apt_id;
string last_apt_name;
double last_apt_elev; double last_apt_elev;
SGGeod tower; SGGeod tower;
string last_apt_info; string last_apt_info;
string last_apt_type;
string pavement_ident; string pavement_ident;
bool pavement; bool pavement;
vector<FGRunwayPtr> runways; //vector<FGRunwayPtr> runways;
vector<FGTaxiwayPtr> taxiways; //vector<FGTaxiwayPtr> taxiways;
vector<FGPavementPtr> pavements; vector<FGPavementPtr> pavements;
vector<flightgear::CommStation*> commStations;
void addAirport() NavDataCache* cache;
{ PositionedID currentAirportID;
if (last_apt_id.empty()) {
void finishAirport()
{
if (currentAirportID == 0) {
return; return;
} }
if (!rwy_count) { if (!rwy_count) {
SG_LOG(SG_GENERAL, SG_ALERT, "ERROR: No runways for " << last_apt_id currentAirportID = 0;
<< ", skipping." ); SG_LOG(SG_GENERAL, SG_ALERT, "ERROR: No runways for " << last_apt_id
return; << ", skipping." );
return;
} }
double lat = rwy_lat_accum / (double)rwy_count; double lat = rwy_lat_accum / (double)rwy_count;
double lon = rwy_lon_accum / (double)rwy_count; double lon = rwy_lon_accum / (double)rwy_count;
if (!got_tower) { if (!got_tower) {
// tower height hard coded for now... // tower height hard coded for now...
const float tower_height = 50.0f; const float tower_height = 50.0f;
// make a little off the heading for 1 runway airports... // make a little off the heading for 1 runway airports...
float fudge_lon = fabs(sin(last_rwy_heading * SGD_DEGREES_TO_RADIANS)) * .003f; float fudge_lon = fabs(sin(last_rwy_heading * SGD_DEGREES_TO_RADIANS)) * .003f;
float fudge_lat = .003f - fudge_lon; float fudge_lat = .003f - fudge_lon;
tower = SGGeod::fromDegFt(lon + fudge_lon, lat + fudge_lat, last_apt_elev + tower_height); tower = SGGeod::fromDegFt(lon + fudge_lon, lat + fudge_lat, last_apt_elev + tower_height);
cache->insertTower(currentAirportID, tower);
} }
SGGeod pos(SGGeod::fromDegFt(lon, lat, last_apt_elev)); SGGeod pos(SGGeod::fromDegFt(lon, lat, last_apt_elev));
FGAirport* apt = new FGAirport(last_apt_id, pos, tower, last_apt_name, false, cache->updatePosition(currentAirportID, pos);
fptypeFromRobinType(atoi(last_apt_type.c_str())));
apt->setRunwaysAndTaxiways(runways, taxiways, pavements); currentAirportID = 0;
apt->setCommStations(commStations);
} }
void parseAirportLine(const vector<string>& token) void parseAirportLine(const vector<string>& token)
@ -241,25 +250,26 @@ private:
const string& id(token[4]); const string& id(token[4]);
double elev = atof( token[1].c_str() ); double elev = atof( token[1].c_str() );
addAirport(); // finish the previous airport
finishAirport();
last_apt_id = id;
last_apt_elev = elev; last_apt_elev = elev;
last_apt_name = "";
got_tower = false; got_tower = false;
string name;
// build the name // build the name
for ( unsigned int i = 5; i < token.size() - 1; ++i ) { for ( unsigned int i = 5; i < token.size() - 1; ++i ) {
last_apt_name += token[i]; name += token[i] + " ";
last_apt_name += " ";
} }
last_apt_name += token[token.size() - 1]; name += token[token.size() - 1];
last_apt_type = token[0];
// clear runway list for start of next airport // clear runway list for start of next airport
rwy_lon_accum = 0.0; rwy_lon_accum = 0.0;
rwy_lat_accum = 0.0; rwy_lat_accum = 0.0;
rwy_count = 0; rwy_count = 0;
int robinType = atoi(token[0].c_str());
currentAirportID = cache->insertAirport(fptypeFromRobinType(robinType), id, name);
} }
void parseRunwayLine810(const vector<string>& token) void parseRunwayLine810(const vector<string>& token)
@ -282,9 +292,8 @@ private:
SGGeod pos(SGGeod::fromDegFt(lon, lat, last_apt_elev)); SGGeod pos(SGGeod::fromDegFt(lon, lat, last_apt_elev));
if (rwy_no[0] == 'x') { if (rwy_no[0] == 'x') {
// taxiway cache->insertRunway(FGPositioned::TAXIWAY,rwy_no, pos, currentAirportID,
FGTaxiway* t = new FGTaxiway(rwy_no, pos, heading, length, width, surface_code); heading, length, width, 0, 0, surface_code);
taxiways.push_back(t);
} else { } else {
// (pair of) runways // (pair of) runways
string rwy_displ_threshold = token[6]; string rwy_displ_threshold = token[6];
@ -299,18 +308,18 @@ private:
double stopway1 = atof( stop[0].c_str() ); double stopway1 = atof( stop[0].c_str() );
double stopway2 = atof( stop[1].c_str() ); double stopway2 = atof( stop[1].c_str() );
FGRunway* rwy = new FGRunway(NULL, rwy_no, pos, heading, length, PositionedID rwy = cache->insertRunway(FGPositioned::RUNWAY, rwy_no, pos,
width, displ_thresh1, stopway1, surface_code, false); currentAirportID, heading, length,
runways.push_back(rwy); width, displ_thresh1, stopway1,
surface_code);
FGRunway* reciprocal = new FGRunway(NULL, FGRunway::reverseIdent(rwy_no),
pos, heading + 180.0, length, width,
displ_thresh2, stopway2, surface_code, true);
runways.push_back(reciprocal);
rwy->setReciprocalRunway(reciprocal); PositionedID reciprocal = cache->insertRunway(FGPositioned::RUNWAY,
reciprocal->setReciprocalRunway(rwy); FGRunway::reverseIdent(rwy_no), pos,
currentAirportID, heading + 180.0, length,
width, displ_thresh2, stopway2,
surface_code);
cache->setRunwayReciprocal(rwy, reciprocal);
} }
} }
@ -352,17 +361,18 @@ private:
double stopway1 = atof( token[12].c_str() ); double stopway1 = atof( token[12].c_str() );
double stopway2 = atof( token[21].c_str() ); double stopway2 = atof( token[21].c_str() );
FGRunway* rwy = new FGRunway(NULL, rwy_no_1, pos, heading_1, length, PositionedID rwy = cache->insertRunway(FGPositioned::RUNWAY, rwy_no_1, pos,
width, displ_thresh1, stopway1, surface_code, false); currentAirportID, heading_1, length,
runways.push_back(rwy); width, displ_thresh1, stopway1,
surface_code);
FGRunway* reciprocal = new FGRunway(NULL, rwy_no_2,
pos, heading_2, length, width,
displ_thresh2, stopway2, surface_code, true);
runways.push_back(reciprocal);
rwy->setReciprocalRunway(reciprocal); PositionedID reciprocal = cache->insertRunway(FGPositioned::RUNWAY,
reciprocal->setReciprocalRunway(rwy); rwy_no_2, pos,
currentAirportID, heading_2, length,
width, displ_thresh2, stopway2,
surface_code);
cache->setRunwayReciprocal(rwy, reciprocal);
} }
void parseWaterRunwayLine850(const vector<string>& token) void parseWaterRunwayLine850(const vector<string>& token)
@ -393,17 +403,16 @@ private:
const string& rwy_no_1(token[3]); const string& rwy_no_1(token[3]);
const string& rwy_no_2(token[6]); const string& rwy_no_2(token[6]);
FGRunway* rwy = new FGRunway(NULL, rwy_no_1, pos, heading_1, length, PositionedID rwy = cache->insertRunway(FGPositioned::RUNWAY, rwy_no_1, pos,
width, 0.0, 0.0, 13, false); currentAirportID, heading_1, length,
runways.push_back(rwy); width, 0.0, 0.0, 13);
FGRunway* reciprocal = new FGRunway(NULL, rwy_no_2,
pos, heading_2, length, width,
0.0, 0.0, 13, true);
runways.push_back(reciprocal);
rwy->setReciprocalRunway(reciprocal); PositionedID reciprocal = cache->insertRunway(FGPositioned::RUNWAY,
reciprocal->setReciprocalRunway(rwy); rwy_no_2, pos,
currentAirportID, heading_2, length,
width, 0.0, 0.0, 13);
cache->setRunwayReciprocal(rwy, reciprocal);
} }
void parseHelipadLine850(const vector<string>& token) void parseHelipadLine850(const vector<string>& token)
@ -425,9 +434,9 @@ private:
const string& rwy_no(token[1]); const string& rwy_no(token[1]);
int surface_code = atoi( token[7].c_str() ); int surface_code = atoi( token[7].c_str() );
FGRunway* rwy = new FGRunway(NULL, rwy_no, pos, heading, length, cache->insertRunway(FGPositioned::RUNWAY, rwy_no, pos,
width, 0.0, 0.0, surface_code, false); currentAirportID, heading, length,
runways.push_back(rwy); width, 0.0, 0.0, surface_code);
} }
void parsePavementLine850(const vector<string>& token) void parsePavementLine850(const vector<string>& token)
@ -449,7 +458,7 @@ private:
FGPavement* pvt = 0; FGPavement* pvt = 0;
if ( !pavement_ident.empty() ) { if ( !pavement_ident.empty() ) {
pvt = new FGPavement( pavement_ident, pos ); pvt = new FGPavement( 0, pavement_ident, pos );
pavements.push_back( pvt ); pavements.push_back( pvt );
pavement_ident = ""; pavement_ident = "";
} else { } else {
@ -475,7 +484,7 @@ private:
rwy_lat_accum / (double)rwy_count, last_apt_elev); rwy_lat_accum / (double)rwy_count, last_apt_elev);
// short int representing tens of kHz: // short int representing tens of kHz:
int freqKhz = atoi(token[1].c_str()); int freqKhz = atoi(token[1].c_str()) * 10;
int rangeNm = 50; int rangeNm = 50;
FGPositioned::Type ty; FGPositioned::Type ty;
// Make sure we only pass on stations with at least a name // Make sure we only pass on stations with at least a name
@ -499,47 +508,43 @@ private:
throw sg_range_exception("unupported apt.dat comm station type"); throw sg_range_exception("unupported apt.dat comm station type");
} }
commStations.push_back(new flightgear::CommStation(token[2], ty, pos, rangeNm, freqKhz)); cache->insertCommStation(ty, token[2], pos, freqKhz, rangeNm, currentAirportID);
} }
else SG_LOG( SG_GENERAL, SG_DEBUG, "Found unnamed comm. Skipping: " << lineId); else SG_LOG( SG_GENERAL, SG_DEBUG, "Found unnamed comm. Skipping: " << lineId);
} }
}; };
// Load the airport data base from the specified aptdb file. The // Load the airport data base from the specified aptdb file. The
// metar file is used to mark the airports as having metar available // metar file is used to mark the airports as having metar available
// or not. // or not.
bool fgAirportDBLoad( const string &aptdb_file, const std::string &metar_file ) bool airportDBLoad( const SGPath &aptdb_file )
{ {
APTLoader ld;
APTLoader ld; ld.parseAPT(aptdb_file);
ld.parseAPT(aptdb_file); return true;
// }
// Load the metar.dat file and update apt db with stations that
// have metar data. bool metarDataLoad(const SGPath& metar_file)
// {
sg_gzifstream metar_in( metar_file.str() );
sg_gzifstream metar_in( metar_file ); if ( !metar_in.is_open() ) {
if ( !metar_in.is_open() ) { SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << metar_file );
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << metar_file ); return false;
}
NavDataCache* cache = NavDataCache::instance();
string ident;
while ( metar_in ) {
metar_in >> ident;
if ( ident == "#" || ident == "//" ) {
metar_in >> skipeol;
} else {
cache->setAirportMetar(ident, true);
} }
}
string ident;
while ( metar_in ) { return true;
metar_in >> ident;
if ( ident == "#" || ident == "//" ) {
metar_in >> skipeol;
} else {
FGAirport* apt = FGAirport::findByIdent(ident);
if (apt) {
apt->setMetar(true);
}
}
}
SG_LOG(SG_GENERAL, SG_INFO, "[FINISHED LOADING]");
return true;
} }
} // of namespace flightgear

View file

@ -27,13 +27,19 @@
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <string> class SGPath;
namespace flightgear
{
// Load the airport data base from the specified aptdb file. The // Load the airport data base from the specified aptdb file. The
// metar file is used to mark the airports as having metar available // metar file is used to mark the airports as having metar available
// or not. // or not.
bool fgAirportDBLoad( const std::string &aptdb_file, bool airportDBLoad(const SGPath& path);
const std::string &metar_file );
bool metarDataLoad(const SGPath& path);
} // of namespace flighgear
#endif // _FG_APT_LOADER_HXX #endif // _FG_APT_LOADER_HXX

View file

@ -251,7 +251,8 @@ bool compare_trafficrecords(FGTrafficRecord a, FGTrafficRecord b)
return (a.getIntentions().size() < b.getIntentions().size()); return (a.getIntentions().size() < b.getIntentions().size());
} }
FGGroundNetwork::FGGroundNetwork() FGGroundNetwork::FGGroundNetwork() :
parent(NULL)
{ {
hasNetwork = false; hasNetwork = false;
foundRoute = false; foundRoute = false;
@ -268,6 +269,13 @@ FGGroundNetwork::FGGroundNetwork()
FGGroundNetwork::~FGGroundNetwork() FGGroundNetwork::~FGGroundNetwork()
{ {
// JMT 2012-09-8 - disabling the groundnet-caching as part of enabling the
// navcache. The problem isn't the NavCache - it's that for the past few years,
// we have not being running destructors on FGPositioned classes, and hence,
// not running this code.
// When I fix FGPositioned lifetimes (unloading-at-runtime support), this
// will need to be re-visited so it can run safely during shutdown.
#if 0
//cerr << "Running Groundnetwork Destructor " << endl; //cerr << "Running Groundnetwork Destructor " << endl;
bool saveData = false; bool saveData = false;
ofstream cachefile; ofstream cachefile;
@ -309,6 +317,7 @@ FGGroundNetwork::~FGGroundNetwork()
if (saveData) { if (saveData) {
cachefile.close(); cachefile.close();
} }
#endif
} }
void FGGroundNetwork::saveElevationCache() { void FGGroundNetwork::saveElevationCache() {

View file

@ -37,9 +37,6 @@
#include <list> #include <list>
class Block; class Block;
using std::string;
using std::vector;
using std::list;
#include "gnnode.hxx" #include "gnnode.hxx"
#include "parking.hxx" #include "parking.hxx"

View file

@ -24,10 +24,9 @@
#include "pavement.hxx" #include "pavement.hxx"
FGPavement::FGPavement(const std::string& aIdent, const SGGeod& aPos) : FGPavement::FGPavement(PositionedID aGuid, const std::string& aIdent, const SGGeod& aPos) :
FGPositioned(PAVEMENT, aIdent, aPos) FGPositioned(aGuid, PAVEMENT, aIdent, aPos)
{ {
init(false); // FGPositioned::init
} }
void FGPavement::addNode(const SGGeod &aPos, bool aClose) void FGPavement::addNode(const SGGeod &aPos, bool aClose)

View file

@ -59,7 +59,7 @@ public:
typedef std::vector<SGSharedPtr<NodeBase> > NodeList; typedef std::vector<SGSharedPtr<NodeBase> > NodeList;
FGPavement(const std::string& aIdent, const SGGeod& aPos); FGPavement(PositionedID aGuid, const std::string& aIdent, const SGGeod& aPos);
void addNode(const SGGeod &aPos, bool aClose = false); void addNode(const SGGeod &aPos, bool aClose = false);
void addBezierNode(const SGGeod &aPos, const SGGeod &aCtrlPt, bool aClose = false); void addBezierNode(const SGGeod &aPos, const SGGeod &aCtrlPt, bool aClose = false);

View file

@ -46,20 +46,17 @@ using std::string;
* 12 - lakebed * 12 - lakebed
*/ */
FGRunwayBase::FGRunwayBase(Type aTy, const string& aIdent, FGRunwayBase::FGRunwayBase(PositionedID aGuid, Type aTy, const string& aIdent,
const SGGeod& aGeod, const SGGeod& aGeod,
const double heading, const double length, const double heading, const double length,
const double width, const double width,
const int surface_code, const int surface_code) :
bool index) : FGPositioned(aGuid, aTy, aIdent, aGeod)
FGPositioned(aTy, aIdent, aGeod)
{ {
_heading = heading; _heading = heading;
_length = length; _length = length;
_width = width; _width = width;
_surface_code = surface_code; _surface_code = surface_code;
init(index);
} }
SGGeod FGRunwayBase::pointOnCenterline(double aOffset) const SGGeod FGRunwayBase::pointOnCenterline(double aOffset) const
@ -100,11 +97,12 @@ bool FGRunwayBase::isHardSurface() const
return ((_surface_code == 1) || (_surface_code == 2)); return ((_surface_code == 1) || (_surface_code == 2));
} }
FGTaxiway::FGTaxiway(const string& aIdent, FGTaxiway::FGTaxiway(PositionedID aGuid,
const string& aIdent,
const SGGeod& aGeod, const SGGeod& aGeod,
const double heading, const double length, const double heading, const double length,
const double width, const double width,
const int surface_code) : const int surface_code) :
FGRunwayBase(TAXIWAY, aIdent, aGeod, heading, length, width, surface_code, false) FGRunwayBase(aGuid, TAXIWAY, aIdent, aGeod, heading, length, width, surface_code)
{ {
} }

View file

@ -39,12 +39,11 @@
class FGRunwayBase : public FGPositioned class FGRunwayBase : public FGPositioned
{ {
public: public:
FGRunwayBase(Type aTy, const std::string& aIdent, FGRunwayBase(PositionedID aGuid, Type aTy, const std::string& aIdent,
const SGGeod& aGeod, const SGGeod& aGeod,
const double heading, const double length, const double heading, const double length,
const double width, const double width,
const int surface_code, const int surface_code);
bool index);
/** /**
* Retrieve a position on the extended centerline. Positive values * Retrieve a position on the extended centerline. Positive values
@ -100,7 +99,8 @@ protected:
class FGTaxiway : public FGRunwayBase class FGTaxiway : public FGRunwayBase
{ {
public: public:
FGTaxiway(const std::string& aIdent, FGTaxiway(PositionedID aGuid,
const std::string& aIdent,
const SGGeod& aGeod, const SGGeod& aGeod,
const double heading, const double length, const double heading, const double length,
const double width, const double width,

View file

@ -40,33 +40,12 @@
#include <Airports/simple.hxx> #include <Airports/simple.hxx>
#include <Navaids/procedure.hxx> #include <Navaids/procedure.hxx>
#include <Navaids/navrecord.hxx> #include <Navaids/navrecord.hxx>
#include <Navaids/NavDataCache.hxx>
using std::string; using std::string;
static std::string cleanRunwayNo(const std::string& aRwyNo) FGRunway::FGRunway(PositionedID aGuid,
{ PositionedID aAirport, const string& aIdent,
if (aRwyNo[0] == 'x') {
return std::string(); // no ident for taxiways
}
string result(aRwyNo);
// canonicalise runway ident
if ((aRwyNo.size() == 1) || !isdigit(aRwyNo[1])) {
result = "0" + aRwyNo;
}
// trim off trailing garbage
if (result.size() > 2) {
char suffix = toupper(result[2]);
if (suffix == 'X') {
result = result.substr(0, 2);
}
}
return result;
}
FGRunway::FGRunway(FGAirport* aAirport, const string& aIdent,
const SGGeod& aGeod, const SGGeod& aGeod,
const double heading, const double length, const double heading, const double length,
const double width, const double width,
@ -74,13 +53,14 @@ FGRunway::FGRunway(FGAirport* aAirport, const string& aIdent,
const double stopway, const double stopway,
const int surface_code, const int surface_code,
bool reciprocal) : bool reciprocal) :
FGRunwayBase(RUNWAY, cleanRunwayNo(aIdent), aGeod, heading, length, width, surface_code, true), FGRunwayBase(aGuid, RUNWAY, aIdent, aGeod,
heading, length, width, surface_code),
_airport(aAirport), _airport(aAirport),
_isReciprocal(reciprocal), _isReciprocal(reciprocal),
_reciprocal(NULL), _reciprocal(0),
_displ_thresh(displ_thresh), _displ_thresh(displ_thresh),
_stopway(stopway), _stopway(stopway),
_ils(NULL) _ils(0)
{ {
} }
@ -144,39 +124,37 @@ SGGeod FGRunway::threshold() const
return pointOnCenterline(_displ_thresh * SG_FEET_TO_METER); return pointOnCenterline(_displ_thresh * SG_FEET_TO_METER);
} }
void FGRunway::processThreshold(SGPropertyNode* aThreshold) void FGRunway::setReciprocalRunway(PositionedID other)
{ {
assert(ident() == aThreshold->getStringValue("rwy")); assert(_reciprocal==0);
double lon = aThreshold->getDoubleValue("lon"),
lat = aThreshold->getDoubleValue("lat");
SGGeod newThreshold(SGGeod::fromDegM(lon, lat, mPosition.getElevationM()));
_heading = aThreshold->getDoubleValue("hdg-deg");
_displ_thresh = aThreshold->getDoubleValue("displ-m") * SG_METER_TO_FEET;
_stopway = aThreshold->getDoubleValue("stopw-m") * SG_METER_TO_FEET;
// compute the new runway center, based on the threshold lat/lon and length,
double offsetFt = (0.5 * _length);
SGGeod newCenter;
double dummy;
SGGeodesy::direct(newThreshold, _heading, offsetFt * SG_FEET_TO_METER, newCenter, dummy);
mPosition = newCenter;
}
void FGRunway::setReciprocalRunway(FGRunway* other)
{
assert(_reciprocal==NULL);
assert((other->_reciprocal == NULL) || (other->_reciprocal == this));
_reciprocal = other; _reciprocal = other;
} }
FGAirport* FGRunway::airport() const
{
return (FGAirport*) flightgear::NavDataCache::instance()->loadById(_airport);
}
FGRunway* FGRunway::reciprocalRunway() const
{
return (FGRunway*) flightgear::NavDataCache::instance()->loadById(_reciprocal);
}
FGNavRecord* FGRunway::ILS() const
{
if (_ils == 0) {
return NULL;
}
return (FGNavRecord*) flightgear::NavDataCache::instance()->loadById(_ils);
}
std::vector<flightgear::SID*> FGRunway::getSIDs() const std::vector<flightgear::SID*> FGRunway::getSIDs() const
{ {
FGAirport* apt = airport();
std::vector<flightgear::SID*> result; std::vector<flightgear::SID*> result;
for (unsigned int i=0; i<_airport->numSIDs(); ++i) { for (unsigned int i=0; i<apt->numSIDs(); ++i) {
flightgear::SID* s = _airport->getSIDByIndex(i); flightgear::SID* s = apt->getSIDByIndex(i);
if (s->isForRunway(this)) { if (s->isForRunway(this)) {
result.push_back(s); result.push_back(s);
} }
@ -187,9 +165,10 @@ std::vector<flightgear::SID*> FGRunway::getSIDs() const
std::vector<flightgear::STAR*> FGRunway::getSTARs() const std::vector<flightgear::STAR*> FGRunway::getSTARs() const
{ {
FGAirport* apt = airport();
std::vector<flightgear::STAR*> result; std::vector<flightgear::STAR*> result;
for (unsigned int i=0; i<_airport->numSTARs(); ++i) { for (unsigned int i=0; i<apt->numSTARs(); ++i) {
flightgear::STAR* s = _airport->getSTARByIndex(i); flightgear::STAR* s = apt->getSTARByIndex(i);
if (s->isForRunway(this)) { if (s->isForRunway(this)) {
result.push_back(s); result.push_back(s);
} }
@ -200,9 +179,10 @@ std::vector<flightgear::STAR*> FGRunway::getSTARs() const
std::vector<flightgear::Approach*> FGRunway::getApproaches() const std::vector<flightgear::Approach*> FGRunway::getApproaches() const
{ {
FGAirport* apt = airport();
std::vector<flightgear::Approach*> result; std::vector<flightgear::Approach*> result;
for (unsigned int i=0; i<_airport->numApproaches(); ++i) { for (unsigned int i=0; i<apt->numApproaches(); ++i) {
flightgear::Approach* s = _airport->getApproachByIndex(i); flightgear::Approach* s = apt->getApproachByIndex(i);
if (s->runway() == this) { if (s->runway() == this) {
result.push_back(s); result.push_back(s);
} }

View file

@ -41,15 +41,16 @@ namespace flightgear {
class FGRunway : public FGRunwayBase class FGRunway : public FGRunwayBase
{ {
FGAirport* _airport; PositionedID _airport;
bool _isReciprocal; bool _isReciprocal;
FGRunway* _reciprocal; PositionedID _reciprocal;
double _displ_thresh; double _displ_thresh;
double _stopway; double _stopway;
FGNavRecord* _ils; PositionedID _ils;
public: public:
FGRunway(FGAirport* aAirport, const std::string& rwy_no, FGRunway(PositionedID aGuid,
PositionedID aAirport, const std::string& rwy_no,
const SGGeod& aGeod, const SGGeod& aGeod,
const double heading, const double length, const double heading, const double length,
const double width, const double width,
@ -103,24 +104,15 @@ public:
/** /**
* Airport this runway is located at * Airport this runway is located at
*/ */
FGAirport* airport() const FGAirport* airport() const;
{ return _airport; }
// FIXME - should die once airport / runway creation is cleaned up FGNavRecord* ILS() const;
void setAirport(FGAirport* aAirport)
{ _airport = aAirport; }
FGNavRecord* ILS() const { return _ils; } void setILS(PositionedID nav) { _ils = nav; }
void setILS(FGNavRecord* nav) { _ils = nav; }
FGRunway* reciprocalRunway() const FGRunway* reciprocalRunway() const;
{ return _reciprocal; }
void setReciprocalRunway(FGRunway* other); void setReciprocalRunway(PositionedID other);
/**
* Helper to process property data loaded from an ICAO.threshold.xml file
*/
void processThreshold(SGPropertyNode* aThreshold);
/** /**
* Get SIDs (DPs) associated with this runway * Get SIDs (DPs) associated with this runway

View file

@ -31,6 +31,7 @@
#include "simple.hxx" #include "simple.hxx"
#include <cassert> #include <cassert>
#include <boost/foreach.hpp>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
@ -48,30 +49,28 @@
#include <Navaids/procedure.hxx> #include <Navaids/procedure.hxx>
#include <Navaids/waypoint.hxx> #include <Navaids/waypoint.hxx>
#include <ATC/CommStation.hxx> #include <ATC/CommStation.hxx>
#include <Navaids/NavDataCache.hxx>
using std::vector; using std::vector;
using std::pair; using std::pair;
using namespace flightgear; using namespace flightgear;
// magic import of a helper which uses FGPositioned internals
extern char** searchAirportNamesAndIdents(const std::string& aFilter);
/*************************************************************************** /***************************************************************************
* FGAirport * FGAirport
***************************************************************************/ ***************************************************************************/
FGAirport::FGAirport(const string &id, const SGGeod& location, const SGGeod& tower_location, FGAirport::FGAirport(PositionedID aGuid, const string &id, const SGGeod& location,
const string &name, bool has_metar, Type aType) : const string &name, bool has_metar, Type aType) :
FGPositioned(aType, id, location), FGPositioned(aGuid, aType, id, location),
_tower_location(tower_location),
_name(name), _name(name),
_has_metar(has_metar), _has_metar(has_metar),
_dynamics(0), _dynamics(0),
mTowerDataLoaded(false),
mRunwaysLoaded(false), mRunwaysLoaded(false),
mTaxiwaysLoaded(true) mTaxiwaysLoaded(true)
{ {
init(true); // init FGPositioned
} }
@ -132,53 +131,30 @@ FGRunway* FGAirport::getRunwayByIndex(unsigned int aIndex) const
loadRunways(); loadRunways();
assert(aIndex >= 0 && aIndex < mRunways.size()); assert(aIndex >= 0 && aIndex < mRunways.size());
return mRunways[aIndex]; return (FGRunway*) flightgear::NavDataCache::instance()->loadById(mRunways[aIndex]);
} }
bool FGAirport::hasRunwayWithIdent(const string& aIdent) const bool FGAirport::hasRunwayWithIdent(const string& aIdent) const
{ {
return (getIteratorForRunwayIdent(aIdent) != mRunways.end()); return flightgear::NavDataCache::instance()->airportItemWithIdent(guid(), FGPositioned::RUNWAY, aIdent) != 0;
} }
FGRunway* FGAirport::getRunwayByIdent(const string& aIdent) const FGRunway* FGAirport::getRunwayByIdent(const string& aIdent) const
{ {
Runway_iterator it = getIteratorForRunwayIdent(aIdent); PositionedID id = flightgear::NavDataCache::instance()->airportItemWithIdent(guid(), FGPositioned::RUNWAY, aIdent);
if (it == mRunways.end()) { if (id == 0) {
SG_LOG(SG_GENERAL, SG_ALERT, "no such runway '" << aIdent << "' at airport " << ident()); SG_LOG(SG_GENERAL, SG_ALERT, "no such runway '" << aIdent << "' at airport " << ident());
throw sg_range_exception("unknown runway " + aIdent + " at airport:" + ident(), "FGAirport::getRunwayByIdent"); throw sg_range_exception("unknown runway " + aIdent + " at airport:" + ident(), "FGAirport::getRunwayByIdent");
} }
return *it; return (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
} }
FGAirport::Runway_iterator
FGAirport::getIteratorForRunwayIdent(const string& aIdent) const
{
if (aIdent.empty())
return mRunways.end();
loadRunways();
string ident(aIdent);
if ((aIdent.size() == 1) || !isdigit(aIdent[1])) {
ident = "0" + aIdent;
}
Runway_iterator it = mRunways.begin();
for (; it != mRunways.end(); ++it) {
if ((*it)->ident() == ident) {
return it;
}
}
return it; // end()
}
FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const
{ {
loadRunways(); loadRunways();
Runway_iterator it = mRunways.begin();
FGRunway* result = NULL; FGRunway* result = NULL;
double currentBestQuality = 0.0; double currentBestQuality = 0.0;
@ -188,17 +164,17 @@ FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const
double surfaceWeight = param->getDoubleValue("surface-weight", 10); double surfaceWeight = param->getDoubleValue("surface-weight", 10);
double deviationWeight = param->getDoubleValue("deviation-weight", 1); double deviationWeight = param->getDoubleValue("deviation-weight", 1);
for (; it != mRunways.end(); ++it) { BOOST_FOREACH(PositionedID id, mRunways) {
double good = (*it)->score(lengthWeight, widthWeight, surfaceWeight); FGRunway* rwy = (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
double good = rwy->score(lengthWeight, widthWeight, surfaceWeight);
double dev = aHeading - (*it)->headingDeg(); double dev = aHeading - rwy->headingDeg();
SG_NORMALIZE_RANGE(dev, -180.0, 180.0); SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
double bad = fabs(deviationWeight * dev) + 1e-20; double bad = fabs(deviationWeight * dev) + 1e-20;
double quality = good / bad; double quality = good / bad;
if (quality > currentBestQuality) { if (quality > currentBestQuality) {
currentBestQuality = quality; currentBestQuality = quality;
result = *it; result = rwy;
} }
} }
@ -209,19 +185,20 @@ FGRunway* FGAirport::findBestRunwayForPos(const SGGeod& aPos) const
{ {
loadRunways(); loadRunways();
Runway_iterator it = mRunways.begin();
FGRunway* result = NULL; FGRunway* result = NULL;
double currentLowestDev = 180.0; double currentLowestDev = 180.0;
for (; it != mRunways.end(); ++it) { BOOST_FOREACH(PositionedID id, mRunways) {
double inboundCourse = SGGeodesy::courseDeg(aPos, (*it)->end()); FGRunway* rwy = (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
double dev = inboundCourse - (*it)->headingDeg();
double inboundCourse = SGGeodesy::courseDeg(aPos, rwy->end());
double dev = inboundCourse - rwy->headingDeg();
SG_NORMALIZE_RANGE(dev, -180.0, 180.0); SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
dev = fabs(dev); dev = fabs(dev);
if (dev < currentLowestDev) { // new best match if (dev < currentLowestDev) { // new best match
currentLowestDev = dev; currentLowestDev = dev;
result = *it; result = rwy;
} }
} // of runway iteration } // of runway iteration
@ -233,9 +210,9 @@ bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const
{ {
loadRunways(); loadRunways();
unsigned int numRunways(mRunways.size()); BOOST_FOREACH(PositionedID id, mRunways) {
for (unsigned int r=0; r<numRunways; ++r) { FGRunway* rwy = (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
FGRunway* rwy = mRunways[r];
if (rwy->isReciprocal()) { if (rwy->isReciprocal()) {
continue; // we only care about lengths, so don't do work twice continue; // we only care about lengths, so don't do work twice
} }
@ -258,7 +235,7 @@ FGTaxiway* FGAirport::getTaxiwayByIndex(unsigned int aIndex) const
{ {
loadTaxiways(); loadTaxiways();
assert(aIndex >= 0 && aIndex < mTaxiways.size()); assert(aIndex >= 0 && aIndex < mTaxiways.size());
return mTaxiways[aIndex]; return (FGTaxiway*) flightgear::NavDataCache::instance()->loadById(mTaxiways[aIndex]);
} }
unsigned int FGAirport::numPavements() const unsigned int FGAirport::numPavements() const
@ -271,21 +248,7 @@ FGPavement* FGAirport::getPavementByIndex(unsigned int aIndex) const
{ {
loadTaxiways(); loadTaxiways();
assert(aIndex >= 0 && aIndex < mPavements.size()); assert(aIndex >= 0 && aIndex < mPavements.size());
return mPavements[aIndex]; return (FGPavement*) flightgear::NavDataCache::instance()->loadById(mPavements[aIndex]);
}
void FGAirport::setRunwaysAndTaxiways(vector<FGRunwayPtr>& rwys,
vector<FGTaxiwayPtr>& txwys,
vector<FGPavementPtr>& pvts)
{
mRunways.swap(rwys);
Runway_iterator it = mRunways.begin();
for (; it != mRunways.end(); ++it) {
(*it)->setAirport(this);
}
mTaxiways.swap(txwys);
mPavements.swap(pvts);
} }
FGRunway* FGAirport::getActiveRunwayForUsage() const FGRunway* FGAirport::getActiveRunwayForUsage() const
@ -341,9 +304,8 @@ bool FGAirport::HardSurfaceFilter::passAirport(FGAirport* aApt) const
FGAirport* FGAirport::findByIdent(const std::string& aIdent) FGAirport* FGAirport::findByIdent(const std::string& aIdent)
{ {
FGPositionedRef r;
PortsFilter filter; PortsFilter filter;
r = FGPositioned::findNextWithPartialId(r, aIdent, &filter); FGPositionedRef r = FGPositioned::findFirstWithIdent(aIdent, &filter);
if (!r) { if (!r) {
return NULL; // we don't warn here, let the caller do that return NULL; // we don't warn here, let the caller do that
} }
@ -354,7 +316,7 @@ FGAirport* FGAirport::getByIdent(const std::string& aIdent)
{ {
FGPositionedRef r; FGPositionedRef r;
PortsFilter filter; PortsFilter filter;
r = FGPositioned::findNextWithPartialId(r, aIdent, &filter); r = FGPositioned::findFirstWithIdent(aIdent, &filter);
if (!r) { if (!r) {
throw sg_range_exception("No such airport with ident: " + aIdent); throw sg_range_exception("No such airport with ident: " + aIdent);
} }
@ -363,9 +325,7 @@ FGAirport* FGAirport::getByIdent(const std::string& aIdent)
char** FGAirport::searchNamesAndIdents(const std::string& aFilter) char** FGAirport::searchNamesAndIdents(const std::string& aFilter)
{ {
// we delegate all the work to a horrible helper in FGPositioned, which can return NavDataCache::instance()->searchAirportNamesAndIdents(aFilter);
// access the (private) index data.
return searchAirportNamesAndIdents(aFilter);
} }
// find basic airport location info from airport database // find basic airport location info from airport database
@ -384,8 +344,10 @@ void FGAirport::loadRunways() const
return; // already loaded, great return; // already loaded, great
} }
mRunwaysLoaded = true;
loadSceneryDefinitions(); loadSceneryDefinitions();
mRunwaysLoaded = true;
mRunways = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::RUNWAY);
} }
void FGAirport::loadTaxiways() const void FGAirport::loadTaxiways() const
@ -393,6 +355,9 @@ void FGAirport::loadTaxiways() const
if (mTaxiwaysLoaded) { if (mTaxiwaysLoaded) {
return; // already loaded, great return; // already loaded, great
} }
mTaxiwaysLoaded = true;
mTaxiways = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::TAXIWAY);
} }
void FGAirport::loadProcedures() const void FGAirport::loadProcedures() const
@ -413,20 +378,23 @@ void FGAirport::loadProcedures() const
} }
void FGAirport::loadSceneryDefinitions() const void FGAirport::loadSceneryDefinitions() const
{ {
NavDataCache* cache = NavDataCache::instance();
SGPath path; SGPath path;
SGPropertyNode_ptr rootNode = new SGPropertyNode; if (!XMLLoader::findAirportData(ident(), "threshold", path)) {
if (XMLLoader::findAirportData(ident(), "threshold", path)) { return; // no XML threshold data
readProperties(path.str(), rootNode);
const_cast<FGAirport*>(this)->readThresholdData(rootNode);
} }
// repeat for the tower data if (!cache->isCachedFileModified(path)) {
rootNode = new SGPropertyNode; // cached values are correct, we're all done
if (XMLLoader::findAirportData(ident(), "twr", path)) { return;
readProperties(path.str(), rootNode);
const_cast<FGAirport*>(this)->readTowerData(rootNode);
} }
SGPropertyNode_ptr rootNode = new SGPropertyNode;
readProperties(path.str(), rootNode);
const_cast<FGAirport*>(this)->readThresholdData(rootNode);
cache->stampCacheFile(path);
} }
void FGAirport::readThresholdData(SGPropertyNode* aRoot) void FGAirport::readThresholdData(SGPropertyNode* aRoot)
@ -447,15 +415,64 @@ void FGAirport::readThresholdData(SGPropertyNode* aRoot)
void FGAirport::processThreshold(SGPropertyNode* aThreshold) void FGAirport::processThreshold(SGPropertyNode* aThreshold)
{ {
// first, let's identify the current runway // first, let's identify the current runway
string id(aThreshold->getStringValue("rwy")); string rwyIdent(aThreshold->getStringValue("rwy"));
if (!hasRunwayWithIdent(id)) { NavDataCache* cache = NavDataCache::instance();
PositionedID id = cache->airportItemWithIdent(guid(), FGPositioned::RUNWAY, rwyIdent);
if (id == 0) {
SG_LOG(SG_GENERAL, SG_DEBUG, "FGAirport::processThreshold: " SG_LOG(SG_GENERAL, SG_DEBUG, "FGAirport::processThreshold: "
"found runway not defined in the global data:" << ident() << "/" << id); "found runway not defined in the global data:" << ident() << "/" << rwyIdent);
return; return;
} }
FGRunway* rwy = getRunwayByIdent(id); double lon = aThreshold->getDoubleValue("lon"),
rwy->processThreshold(aThreshold); lat = aThreshold->getDoubleValue("lat");
SGGeod newThreshold(SGGeod::fromDegM(lon, lat, mPosition.getElevationM()));
double newHeading = aThreshold->getDoubleValue("hdg-deg");
double newDisplacedThreshold = aThreshold->getDoubleValue("displ-m") * SG_METER_TO_FEET;
double newStopway = aThreshold->getDoubleValue("stopw-m") * SG_METER_TO_FEET;
cache->updateRunwayThreshold(id, newThreshold,
newHeading, newDisplacedThreshold, newStopway);
}
SGGeod FGAirport::getTowerLocation() const
{
validateTowerData();
NavDataCache* cache = NavDataCache::instance();
PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER);
if (towers.empty()) {
SG_LOG(SG_GENERAL, SG_ALERT, "No towers defined for:" <<ident());
return SGGeod();
}
FGPositionedRef tower = cache->loadById(towers.front());
return tower->geod();
}
void FGAirport::validateTowerData() const
{
if (mTowerDataLoaded) {
return;
}
mTowerDataLoaded = true;
NavDataCache* cache = NavDataCache::instance();
SGPath path;
if (!XMLLoader::findAirportData(ident(), "twr", path)) {
return; // no XML tower data
}
if (!cache->isCachedFileModified(path)) {
// cached values are correct, we're all done
return;
}
SGPropertyNode_ptr rootNode = new SGPropertyNode;
readProperties(path.str(), rootNode);
const_cast<FGAirport*>(this)->readTowerData(rootNode);
cache->stampCacheFile(path);
} }
void FGAirport::readTowerData(SGPropertyNode* aRoot) void FGAirport::readTowerData(SGPropertyNode* aRoot)
@ -468,8 +485,74 @@ void FGAirport::readTowerData(SGPropertyNode* aRoot)
// scenery for a precise terrain elevation, we use the field elevation // scenery for a precise terrain elevation, we use the field elevation
// (this is also what the apt.dat code does) // (this is also what the apt.dat code does)
double fieldElevationM = geod().getElevationM(); double fieldElevationM = geod().getElevationM();
SGGeod towerLocation(SGGeod::fromDegM(lon, lat, fieldElevationM + elevM));
_tower_location = SGGeod::fromDegM(lon, lat, fieldElevationM + elevM); NavDataCache* cache = NavDataCache::instance();
PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER);
if (towers.empty()) {
cache->insertTower(guid(), towerLocation);
} else {
// update the position
cache->updatePosition(towers.front(), towerLocation);
}
}
bool FGAirport::validateILSData()
{
if (mILSDataLoaded) {
return false;
}
mILSDataLoaded = true;
NavDataCache* cache = NavDataCache::instance();
SGPath path;
if (!XMLLoader::findAirportData(ident(), "ils", path)) {
return false; // no XML tower data
}
if (!cache->isCachedFileModified(path)) {
// cached values are correct, we're all done
return false;
}
SGPropertyNode_ptr rootNode = new SGPropertyNode;
readProperties(path.str(), rootNode);
readILSData(rootNode);
cache->stampCacheFile(path);
// we loaded data, tell the caller it might need to reload things
return true;
}
void FGAirport::readILSData(SGPropertyNode* aRoot)
{
NavDataCache* cache = NavDataCache::instance();
// find the entry matching the runway
SGPropertyNode* runwayNode, *ilsNode;
for (int i=0; (runwayNode = aRoot->getChild("runway", i)) != NULL; ++i) {
for (int j=0; (ilsNode = runwayNode->getChild("ils", j)) != NULL; ++j) {
// must match on both nav-ident and runway ident, to support the following:
// - runways with multiple distinct ILS installations (KEWD, for example)
// - runways where both ends share the same nav ident (LFAT, for example)
PositionedID ils = cache->findILS(guid(), ilsNode->getStringValue("rwy"),
ilsNode->getStringValue("nav-id"));
if (ils == 0) {
SG_LOG(SG_GENERAL, SG_INFO, "reading ILS data for " << ident() <<
", couldn;t find runway/navaid for:" <<
ilsNode->getStringValue("rwy") << "/" <<
ilsNode->getStringValue("nav-id"));
continue;
}
double hdgDeg = ilsNode->getDoubleValue("hdg-deg"),
lon = ilsNode->getDoubleValue("lon"),
lat = ilsNode->getDoubleValue("lat"),
elevM = ilsNode->getDoubleValue("elev-m");
cache->updateILS(ils, SGGeod::fromDegM(lon, lat, elevM), hdgDeg);
} // of ILS iteration
} // of runway iteration
} }
void FGAirport::addSID(flightgear::SID* aSid) void FGAirport::addSID(flightgear::SID* aSid)
@ -559,24 +642,31 @@ Approach* FGAirport::findApproachWithIdent(const std::string& aIdent) const
return NULL; return NULL;
} }
void FGAirport::setCommStations(CommStationList& comms) CommStationList
FGAirport::commStations() const
{ {
mCommStations.swap(comms); NavDataCache* cache = NavDataCache::instance();
for (unsigned int c=0; c<mCommStations.size(); ++c) { CommStationList result;
mCommStations[c]->setAirport(this); BOOST_FOREACH(PositionedID pos, cache->airportItemsOfType(guid(),
} FGPositioned::FREQ_GROUND,
FGPositioned::FREQ_UNICOM))
{
result.push_back((CommStation*) cache->loadById(pos));
}
return result;
} }
CommStationList CommStationList
FGAirport::commStationsOfType(FGPositioned::Type aTy) const FGAirport::commStationsOfType(FGPositioned::Type aTy) const
{ {
CommStationList result; NavDataCache* cache = NavDataCache::instance();
for (unsigned int c=0; c<mCommStations.size(); ++c) { CommStationList result;
if (mCommStations[c]->type() == aTy) { BOOST_FOREACH(PositionedID pos, cache->airportItemsOfType(guid(), aTy)) {
result.push_back(mCommStations[c]); result.push_back((CommStation*) cache->loadById(pos));
} }
}
return result; return result;
} }
// get airport elevation // get airport elevation

View file

@ -41,10 +41,6 @@ class FGTaxiway;
class FGPavement; class FGPavement;
class SGPropertyNode; class SGPropertyNode;
typedef SGSharedPtr<FGRunway> FGRunwayPtr;
typedef SGSharedPtr<FGTaxiway> FGTaxiwayPtr;
typedef SGSharedPtr<FGPavement> FGPavementPtr;
namespace flightgear { namespace flightgear {
class SID; class SID;
class STAR; class STAR;
@ -66,7 +62,7 @@ namespace flightgear {
class FGAirport : public FGPositioned class FGAirport : public FGPositioned
{ {
public: public:
FGAirport(const std::string& id, const SGGeod& location, const SGGeod& tower, FGAirport(PositionedID aGuid, const std::string& id, const SGGeod& location,
const std::string& name, bool has_metar, Type aType); const std::string& name, bool has_metar, Type aType);
~FGAirport(); ~FGAirport();
@ -87,7 +83,14 @@ public:
virtual const std::string& name() const virtual const std::string& name() const
{ return _name; } { return _name; }
const SGGeod& getTowerLocation() const { return _tower_location; } /**
* reload the ILS data from XML if required.
* @result true if the data was refreshed, false if no data was loaded
* or previously cached data is still correct.
*/
bool validateILSData();
SGGeod getTowerLocation() const;
void setMetar(bool value) { _has_metar = value; } void setMetar(bool value) { _has_metar = value; }
@ -122,10 +125,6 @@ public:
unsigned int numPavements() const; unsigned int numPavements() const;
FGPavement* getPavementByIndex(unsigned int aIndex) const; FGPavement* getPavementByIndex(unsigned int aIndex) const;
void setRunwaysAndTaxiways(std::vector<FGRunwayPtr>& rwys,
std::vector<FGTaxiwayPtr>& txwys,
std::vector<FGPavementPtr>& pvts);
class AirportFilter : public Filter class AirportFilter : public Filter
{ {
@ -216,20 +215,11 @@ public:
* matches in a format suitable for use by a puaList. * matches in a format suitable for use by a puaList.
*/ */
static char** searchNamesAndIdents(const std::string& aFilter); static char** searchNamesAndIdents(const std::string& aFilter);
void setCommStations(flightgear::CommStationList& comms);
flightgear::CommStationList commStationsOfType(FGPositioned::Type aTy) const; flightgear::CommStationList commStationsOfType(FGPositioned::Type aTy) const;
const flightgear::CommStationList& commStations() const flightgear::CommStationList commStations() const;
{ return mCommStations; }
private: private:
typedef std::vector<FGRunwayPtr>::const_iterator Runway_iterator;
/**
* Helper to locate a runway by ident
*/
Runway_iterator getIteratorForRunwayIdent(const std::string& aIdent) const;
// disable these // disable these
FGAirport operator=(FGAirport &other); FGAirport operator=(FGAirport &other);
FGAirport(const FGAirport&); FGAirport(const FGAirport&);
@ -244,13 +234,16 @@ private:
*/ */
void readThresholdData(SGPropertyNode* aRoot); void readThresholdData(SGPropertyNode* aRoot);
void processThreshold(SGPropertyNode* aThreshold); void processThreshold(SGPropertyNode* aThreshold);
void readILSData(SGPropertyNode* aRoot);
void validateTowerData() const;
/** /**
* Helper to parse property data loaded from an ICAO.twr.xml filke * Helper to parse property data loaded from an ICAO.twr.xml filke
*/ */
void readTowerData(SGPropertyNode* aRoot); void readTowerData(SGPropertyNode* aRoot);
SGGeod _tower_location;
std::string _name; std::string _name;
bool _has_metar; bool _has_metar;
FGAirportDynamics *_dynamics; FGAirportDynamics *_dynamics;
@ -259,20 +252,20 @@ private:
void loadTaxiways() const; void loadTaxiways() const;
void loadProcedures() const; void loadProcedures() const;
mutable bool mTowerDataLoaded;
mutable bool mRunwaysLoaded; mutable bool mRunwaysLoaded;
mutable bool mTaxiwaysLoaded; mutable bool mTaxiwaysLoaded;
mutable bool mProceduresLoaded; mutable bool mProceduresLoaded;
bool mILSDataLoaded;
std::vector<FGRunwayPtr> mRunways;
std::vector<FGTaxiwayPtr> mTaxiways; mutable PositionedIDVec mRunways;
std::vector<FGPavementPtr> mPavements; mutable PositionedIDVec mTaxiways;
PositionedIDVec mPavements;
std::vector<flightgear::SID*> mSIDs; std::vector<flightgear::SID*> mSIDs;
std::vector<flightgear::STAR*> mSTARs; std::vector<flightgear::STAR*> mSTARs;
std::vector<flightgear::Approach*> mApproaches; std::vector<flightgear::Approach*> mApproaches;
};
flightgear::CommStationList mCommStations;
};
// find basic airport location info from airport database // find basic airport location info from airport database
const FGAirport *fgFindAirportID( const std::string& id); const FGAirport *fgFindAirportID( const std::string& id);

View file

@ -42,10 +42,11 @@ void
AirportList::destroy_list() AirportList::destroy_list()
{ {
for (char **c = _content; *c; c++) { for (char **c = _content; *c; c++) {
delete *c; free(*c);
*c = 0; *c = 0;
} }
delete [] _content;
free(_content);
} }

View file

@ -937,11 +937,11 @@ public:
} }
virtual FGPositioned::Type minType() const { virtual FGPositioned::Type minType() const {
return _fixes ? FGPositioned::FIX : FGPositioned::VOR; return _fixes ? FGPositioned::FIX : FGPositioned::NDB;
} }
virtual FGPositioned::Type maxType() const { virtual FGPositioned::Type maxType() const {
return _navaids ? FGPositioned::NDB : FGPositioned::FIX; return _navaids ? FGPositioned::VOR : FGPositioned::FIX;
} }
private: private:
@ -1069,7 +1069,9 @@ void MapWidget::drawNavRadio(SGPropertyNode_ptr radio)
// identify the tuned station - unfortunately we don't get lat/lon directly, // identify the tuned station - unfortunately we don't get lat/lon directly,
// need to do the frequency search again // need to do the frequency search again
double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0); double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0);
FGNavRecord* nav = globals->get_navlist()->findByFreq(mhz, _aircraft);
FGNavRecord* nav = FGNavList::findByFreq(mhz, _aircraft,
FGNavList::navFilter());
if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) { if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) {
// mismatch between navradio selection logic and ours! // mismatch between navradio selection logic and ours!
return; return;
@ -1111,7 +1113,7 @@ void MapWidget::drawNavRadio(SGPropertyNode_ptr radio)
void MapWidget::drawTunedLocalizer(SGPropertyNode_ptr radio) void MapWidget::drawTunedLocalizer(SGPropertyNode_ptr radio)
{ {
double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0); double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0);
FGNavRecord* loc = globals->get_loclist()->findByFreq(mhz, _aircraft); FGNavRecord* loc = FGNavList::findByFreq(mhz, _aircraft, FGNavList::locFilter());
if (!loc || (loc->ident() != radio->getStringValue("nav-id"))) { if (!loc || (loc->ident() != radio->getStringValue("nav-id"))) {
// mismatch between navradio selection logic and ours! // mismatch between navradio selection logic and ours!
return; return;

View file

@ -1088,8 +1088,8 @@ void NavDisplay::processNavRadios()
FGNavRecord* NavDisplay::processNavRadio(const SGPropertyNode_ptr& radio) FGNavRecord* NavDisplay::processNavRadio(const SGPropertyNode_ptr& radio)
{ {
double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0); double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0);
FGNavRecord* nav = globals->get_navlist()->findByFreq(mhz, _pos); FGNavRecord* nav = FGNavList::findByFreq(mhz, _pos, FGNavList::navFilter());
if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) { if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) {
// station was not found // station was not found
return NULL; return NULL;

View file

@ -105,9 +105,6 @@ ADF::init ()
// foreign simulator properties // foreign simulator properties
_electrical_node = fgGetNode("/systems/electrical/outputs/adf", true); _electrical_node = fgGetNode("/systems/electrical/outputs/adf", true);
_longitude_node = fgGetNode("/position/longitude-deg", true);
_latitude_node = fgGetNode("/position/latitude-deg", true);
_altitude_node = fgGetNode("/position/altitude-ft", true);
_heading_node = fgGetNode("/orientation/heading-deg", true); _heading_node = fgGetNode("/orientation/heading-deg", true);
// backward compatibility check // backward compatibility check
@ -153,18 +150,12 @@ ADF::update (double delta_time_sec)
_last_frequency_khz = frequency_khz; _last_frequency_khz = frequency_khz;
} }
// Get the aircraft position SGGeod acPos(globals->get_aircraft_position());
double longitude_deg = _longitude_node->getDoubleValue();
double latitude_deg = _latitude_node->getDoubleValue();
double altitude_m = _altitude_node->getDoubleValue();
double longitude_rad = longitude_deg * SGD_DEGREES_TO_RADIANS;
double latitude_rad = latitude_deg * SGD_DEGREES_TO_RADIANS;
// On timeout, scan again // On timeout, scan again
_time_before_search_sec -= delta_time_sec; _time_before_search_sec -= delta_time_sec;
if (_time_before_search_sec < 0) if (_time_before_search_sec < 0)
search(frequency_khz, longitude_rad, latitude_rad, altitude_m); search(frequency_khz, acPos);
if (!_transmitter_valid) { if (!_transmitter_valid) {
_in_range_node->setBoolValue(false); _in_range_node->setBoolValue(false);
@ -173,12 +164,11 @@ ADF::update (double delta_time_sec)
} }
// Calculate the bearing to the transmitter // Calculate the bearing to the transmitter
SGGeod geod = SGGeod::fromRadM(longitude_rad, latitude_rad, altitude_m); SGVec3d location = globals->get_aircraft_positon_cart();
SGVec3d location = SGVec3d::fromGeod(geod);
double distance_nm = dist(_transmitter_cart, location) * SG_METER_TO_NM; double distance_nm = dist(_transmitter_cart, location) * SG_METER_TO_NM;
double range_nm = adjust_range(_transmitter_pos.getElevationFt(), double range_nm = adjust_range(_transmitter_pos.getElevationFt(),
altitude_m * SG_METER_TO_FEET, acPos.getElevationFt(),
_transmitter_range_nm); _transmitter_range_nm);
if (distance_nm <= range_nm) { if (distance_nm <= range_nm) {
@ -186,7 +176,7 @@ ADF::update (double delta_time_sec)
double bearing, az2, s; double bearing, az2, s;
double heading = _heading_node->getDoubleValue(); double heading = _heading_node->getDoubleValue();
geo_inverse_wgs_84(geod, _transmitter_pos, geo_inverse_wgs_84(acPos, _transmitter_pos,
&bearing, &az2, &s); &bearing, &az2, &s);
_in_range_node->setBoolValue(true); _in_range_node->setBoolValue(true);
@ -233,16 +223,14 @@ ADF::update (double delta_time_sec)
} }
void void
ADF::search (double frequency_khz, double longitude_rad, ADF::search (double frequency_khz, const SGGeod& pos)
double latitude_rad, double altitude_m)
{ {
string ident = ""; string ident = "";
// reset search time // reset search time
_time_before_search_sec = 1.0; _time_before_search_sec = 1.0;
// try the ILS list first FGNavList::TypeFilter filter(FGPositioned::NDB);
FGNavRecord *nav = globals->get_navlist()->findByFreq(frequency_khz, FGNavRecord *nav = FGNavList::findByFreq(frequency_khz, pos, &filter);
SGGeod::fromRadM(longitude_rad, latitude_rad, altitude_m));
_transmitter_valid = (nav != NULL); _transmitter_valid = (nav != NULL);
if ( _transmitter_valid ) { if ( _transmitter_valid ) {

View file

@ -60,15 +60,11 @@ private:
void set_bearing (double delta_time_sec, double bearing); void set_bearing (double delta_time_sec, double bearing);
void search (double frequency, double longitude_rad, void search (double frequency, const SGGeod& pos);
double latitude_rad, double altitude_m);
std::string _name; std::string _name;
unsigned int _num; unsigned int _num;
SGPropertyNode_ptr _longitude_node;
SGPropertyNode_ptr _latitude_node;
SGPropertyNode_ptr _altitude_node;
SGPropertyNode_ptr _heading_node; SGPropertyNode_ptr _heading_node;
SGPropertyNode_ptr _serviceable_node; SGPropertyNode_ptr _serviceable_node;
SGPropertyNode_ptr _error_node; SGPropertyNode_ptr _error_node;

View file

@ -1353,7 +1353,8 @@ void DCLGPS::CreateFlightPlan(GPSFlightPlan* fp, vector<string> ids, vector<GPSW
class DCLGPSFilter : public FGPositioned::Filter class DCLGPSFilter : public FGPositioned::Filter
{ {
public: public:
virtual bool pass(const FGPositioned* aPos) const { virtual bool pass(FGPositioned* aPos) const
{
switch (aPos->type()) { switch (aPos->type()) {
case FGPositioned::AIRPORT: case FGPositioned::AIRPORT:
// how about heliports and seaports? // how about heliports and seaports?
@ -1370,9 +1371,14 @@ public:
GPSWaypoint* DCLGPS::FindFirstById(const string& id) const GPSWaypoint* DCLGPS::FindFirstById(const string& id) const
{ {
DCLGPSFilter filter; DCLGPSFilter filter;
FGPositionedRef result = FGPositioned::findNextWithPartialId(NULL, id, &filter); FGPositioned::List matches = FGPositioned::findAllWithIdent(id, &filter, false);
return GPSWaypoint::createFromPositioned(result); if (matches.empty()) {
return NULL;
}
FGPositioned::sortByRange(matches, SGGeod::fromRad(_lon, _lat));
return GPSWaypoint::createFromPositioned(matches.front());
} }
GPSWaypoint* DCLGPS::FindFirstByExactId(const string& id) const GPSWaypoint* DCLGPS::FindFirstByExactId(const string& id) const
@ -1388,15 +1394,14 @@ FGPositioned* DCLGPS::FindTypedFirstById(const string& id, FGPositioned::Type ty
multi = false; multi = false;
FGPositioned::TypeFilter filter(ty); FGPositioned::TypeFilter filter(ty);
if (exact) { FGPositioned::List matches =
FGPositioned::List matches = FGPositioned::findAllWithIdent(id, &filter, exact);
FGPositioned::findAllWithIdent(id, &filter); if (matches.empty()) {
FGPositioned::sortByRange(matches, SGGeod::fromRad(_lon, _lat)); return NULL;
multi = (matches.size() > 1);
return matches.empty() ? NULL : matches.front().ptr();
} }
return FGPositioned::findNextWithPartialId(NULL, id, &filter); FGPositioned::sortByRange(matches, SGGeod::fromRad(_lon, _lat));
return matches.front();
} }
FGNavRecord* DCLGPS::FindFirstVorById(const string& id, bool &multi, bool exact) FGNavRecord* DCLGPS::FindFirstVorById(const string& id, bool &multi, bool exact)

View file

@ -40,6 +40,36 @@ adjust_range (double transmitter_elevation_ft, double aircraft_altitude_ft,
return range_nm + (range_nm * rand * rand); return range_nm + (range_nm * rand * rand);
} }
namespace {
class DMEFilter : public FGNavList::TypeFilter
{
public:
DMEFilter() :
TypeFilter(FGPositioned::DME),
_locEnabled(fgGetBool("/sim/realism/dme-fallback-to-loc", true))
{
if (_locEnabled) {
_mintype = FGPositioned::ILS;
}
}
virtual bool pass(FGPositioned* pos) const
{
switch (pos->type()) {
case FGPositioned::DME: return true;
case FGPositioned::ILS:
case FGPositioned::LOC: return _locEnabled;
default: return false;
}
}
private:
const bool _locEnabled;
};
} // of anonymous namespace
DME::DME ( SGPropertyNode *node ) DME::DME ( SGPropertyNode *node )
: _last_distance_nm(0), : _last_distance_nm(0),
@ -127,17 +157,9 @@ DME::update (double delta_time_sec)
if (_time_before_search_sec < 0) { if (_time_before_search_sec < 0) {
_time_before_search_sec = 1.0; _time_before_search_sec = 1.0;
if( fgGetBool( "/sim/realism/dme-fallback-to-loc", true ) ) { SGGeod pos(globals->get_aircraft_position());
if( NULL == (_navrecord = globals->get_loclist()->findByFreq( frequency_mhz, DMEFilter filter;
globals->get_aircraft_position())) ) { _navrecord = FGNavList::findByFreq(frequency_mhz, pos, &filter);
_navrecord = globals->get_dmelist()->findByFreq( frequency_mhz,
globals->get_aircraft_position());
}
} else {
_navrecord = globals->get_dmelist()->findByFreq( frequency_mhz,
globals->get_aircraft_position());
}
} }
// If it's off, don't bother. // If it's off, don't bother.

View file

@ -472,7 +472,8 @@ void FGKR_87::search() {
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
FGNavRecord *adf = globals->get_navlist()->findByFreq( freq, pos); FGNavList::TypeFilter filter(FGPositioned::NDB);
FGNavRecord *adf = FGNavList::findByFreq( freq, pos, &filter);
if ( adf != NULL ) { if ( adf != NULL ) {
char sfreq[128]; char sfreq[128];
snprintf( sfreq, 10, "%d", freq ); snprintf( sfreq, 10, "%d", freq );

View file

@ -871,12 +871,7 @@ void FGNavRadio::updateAudio( double dt )
FGNavRecord* FGNavRadio::findPrimaryNavaid(const SGGeod& aPos, double aFreqMHz) FGNavRecord* FGNavRadio::findPrimaryNavaid(const SGGeod& aPos, double aFreqMHz)
{ {
FGNavRecord* nav = globals->get_navlist()->findByFreq(aFreqMHz, aPos); return FGNavList::findByFreq(aFreqMHz, aPos, FGNavList::navFilter());
if (nav) {
return nav;
}
return globals->get_loclist()->findByFreq(aFreqMHz, aPos);
} }
// Update current nav/adf radio stations based on current position // Update current nav/adf radio stations based on current position
@ -907,7 +902,9 @@ void FGNavRadio::search()
// search glideslope station // search glideslope station
if ((_navaid.valid()) && (_navaid->type() != FGPositioned::VOR)) if ((_navaid.valid()) && (_navaid->type() != FGPositioned::VOR))
{ {
FGNavRecord* gs = globals->get_gslist()->findByFreq(freq, globals->get_aircraft_position()); FGNavList::TypeFilter gsFilter(FGPositioned::GS);
FGNavRecord* gs = FGNavList::findByFreq(freq, globals->get_aircraft_position(),
&gsFilter);
if ((!_nav_search) && (gs == _gs)) if ((!_nav_search) && (gs == _gs))
{ {
_nav_search = true; // search NAV on next iteration _nav_search = true; // search NAV on next iteration

View file

@ -148,7 +148,7 @@ public:
protected: protected:
virtual double computeSignalQuality_norm( const SGGeod & aircraftPosition ); virtual double computeSignalQuality_norm( const SGGeod & aircraftPosition );
virtual FGNavList * getNavaidList() = 0; virtual FGNavList::TypeFilter* getNavaidFilter() = 0;
// General-purpose sawtooth function. Graph looks like this: // General-purpose sawtooth function. Graph looks like this:
// /\ . // /\ .
@ -258,7 +258,8 @@ double NavRadioComponent::getRange_nm( const SGGeod & aircraftPosition )
void NavRadioComponent::search( double frequency, const SGGeod & aircraftPosition ) void NavRadioComponent::search( double frequency, const SGGeod & aircraftPosition )
{ {
if( NULL == (_navRecord = getNavaidList()->findByFreq(frequency, aircraftPosition )) ) { _navRecord = FGNavList::findByFreq(frequency, aircraftPosition, getNavaidFilter() );
if( NULL == _navRecord ) {
SG_LOG(SG_INSTR,SG_ALERT, "No " << _name << " available at " << frequency ); SG_LOG(SG_INSTR,SG_ALERT, "No " << _name << " available at " << frequency );
_ident = ""; _ident = "";
return; return;
@ -322,7 +323,7 @@ public:
virtual double getRange_nm(const SGGeod & aircraftPosition); virtual double getRange_nm(const SGGeod & aircraftPosition);
protected: protected:
virtual double computeSignalQuality_norm( const SGGeod & aircraftPosition ); virtual double computeSignalQuality_norm( const SGGeod & aircraftPosition );
virtual FGNavList * getNavaidList(); virtual FGNavList::TypeFilter* getNavaidFilter();
private: private:
double _totalTime; double _totalTime;
@ -392,9 +393,10 @@ double VOR::getRange_nm( const SGGeod & aircraftPosition )
return _serviceVolume.adjustRange( _heightAboveStation_ft, _navRecord->get_range() ); return _serviceVolume.adjustRange( _heightAboveStation_ft, _navRecord->get_range() );
} }
FGNavList * VOR::getNavaidList() FGNavList::TypeFilter* VOR::getNavaidFilter()
{ {
return globals->get_navlist(); static FGNavList::TypeFilter filter(FGPositioned::VOR);
return &filter;
} }
double VOR::computeSignalQuality_norm( const SGGeod & aircraftPosition ) double VOR::computeSignalQuality_norm( const SGGeod & aircraftPosition )
@ -467,7 +469,7 @@ public:
protected: protected:
virtual double computeSignalQuality_norm( const SGGeod & aircraftPosition ); virtual double computeSignalQuality_norm( const SGGeod & aircraftPosition );
virtual FGNavList * getNavaidList(); virtual FGNavList::TypeFilter* getNavaidFilter();
private: private:
class ServiceVolume { class ServiceVolume {
@ -531,9 +533,9 @@ LOC::~LOC()
{ {
} }
FGNavList * LOC::getNavaidList() FGNavList::TypeFilter* LOC::getNavaidFilter()
{ {
return globals->get_loclist(); return FGNavList::locFilter();
} }
void LOC::search( double frequency, const SGGeod & aircraftPosition ) void LOC::search( double frequency, const SGGeod & aircraftPosition )
@ -617,8 +619,7 @@ public:
virtual double getRange_nm(const SGGeod & aircraftPosition); virtual double getRange_nm(const SGGeod & aircraftPosition);
protected: protected:
virtual FGNavList * getNavaidList(); virtual FGNavList::TypeFilter* getNavaidFilter();
private: private:
class ServiceVolume { class ServiceVolume {
public: public:
@ -686,9 +687,10 @@ GS::~GS()
{ {
} }
FGNavList * GS::getNavaidList() FGNavList::TypeFilter* GS::getNavaidFilter()
{ {
return globals->get_gslist(); static FGNavList::TypeFilter filter(FGPositioned::GS);
return &filter;
} }
double GS::getRange_nm(const SGGeod & aircraftPosition) double GS::getRange_nm(const SGGeod & aircraftPosition)

View file

@ -58,8 +58,6 @@ TACAN::TACAN ( SGPropertyNode *node ) :
_transmitter_pos(SGGeod::fromDeg(0, 0)), _transmitter_pos(SGGeod::fromDeg(0, 0)),
_transmitter_range_nm(0), _transmitter_range_nm(0),
_transmitter_bias(0.0), _transmitter_bias(0.0),
_mobile_lat(0.0),
_mobile_lon(0.0),
_listener_active(0) _listener_active(0)
{ {
} }
@ -116,9 +114,6 @@ TACAN::init ()
SGPropertyNode *mnode = fgGetNode("/ai/models/multiplayer", _num, false); SGPropertyNode *mnode = fgGetNode("/ai/models/multiplayer", _num, false);
_mp_callsign_node = mnode ? mnode->getChild("callsign", 0, false) : 0; _mp_callsign_node = mnode ? mnode->getChild("callsign", 0, false) : 0;
_longitude_node = fgGetNode("/position/longitude-deg", true);
_latitude_node = fgGetNode("/position/latitude-deg", true);
_altitude_node = fgGetNode("/position/altitude-ft", true);
_heading_node = fgGetNode("/orientation/heading-deg", true); _heading_node = fgGetNode("/orientation/heading-deg", true);
_yaw_node = fgGetNode("/orientation/side-slip-deg", true); _yaw_node = fgGetNode("/orientation/side-slip-deg", true);
_electrical_node = fgGetNode("/systems/electrical/outputs/tacan", true); _electrical_node = fgGetNode("/systems/electrical/outputs/tacan", true);
@ -146,17 +141,11 @@ TACAN::update (double delta_time_sec)
return; return;
} }
// Get the aircraft position SGGeod pos(globals->get_aircraft_position());
double longitude_deg = _longitude_node->getDoubleValue();
double latitude_deg = _latitude_node->getDoubleValue();
double altitude_m = _altitude_node->getDoubleValue() * SG_FEET_TO_METER;
double longitude_rad = longitude_deg * SGD_DEGREES_TO_RADIANS;
double latitude_rad = latitude_deg * SGD_DEGREES_TO_RADIANS;
// On timeout, scan again // On timeout, scan again
_time_before_search_sec -= delta_time_sec; _time_before_search_sec -= delta_time_sec;
if ((_time_before_search_sec < 0 || _new_frequency) && _frequency_mhz >= 0) if ((_time_before_search_sec < 0 || _new_frequency) && _frequency_mhz >= 0)
search(_frequency_mhz, longitude_rad, latitude_rad, altitude_m); search(_frequency_mhz, pos);
// Calculate the distance to the transmitter // Calculate the distance to the transmitter
@ -165,15 +154,9 @@ TACAN::update (double delta_time_sec)
double mobile_bearing = 0; double mobile_bearing = 0;
double mobile_distance = 0; double mobile_distance = 0;
SG_LOG( SG_INSTR, SG_DEBUG, "mobile_lat " << _mobile_lat);
SG_LOG( SG_INSTR, SG_DEBUG, "mobile_lon " << _mobile_lon);
SG_LOG( SG_INSTR, SG_DEBUG, "mobile_name " << _mobile_name); SG_LOG( SG_INSTR, SG_DEBUG, "mobile_name " << _mobile_name);
SG_LOG( SG_INSTR, SG_DEBUG, "mobile_valid " << _mobile_valid); SG_LOG( SG_INSTR, SG_DEBUG, "mobile_valid " << _mobile_valid);
geo_inverse_wgs_84(altitude_m, geo_inverse_wgs_84(pos, _mobilePos,
latitude_deg,
longitude_deg,
_mobile_lat,
_mobile_lon,
&mobile_bearing, &mobile_az2, &mobile_distance); &mobile_bearing, &mobile_az2, &mobile_distance);
@ -182,7 +165,6 @@ TACAN::update (double delta_time_sec)
double bearing = 0; double bearing = 0;
double distance = 0; double distance = 0;
SGGeod pos = SGGeod::fromDegM(longitude_deg, latitude_deg, altitude_m);
geo_inverse_wgs_84(pos, _transmitter_pos, geo_inverse_wgs_84(pos, _transmitter_pos,
&bearing, &az2, &distance); &bearing, &az2, &distance);
@ -193,7 +175,7 @@ TACAN::update (double delta_time_sec)
SG_LOG( SG_INSTR, SG_DEBUG, "distance_m " << distance); SG_LOG( SG_INSTR, SG_DEBUG, "distance_m " << distance);
bearing = mobile_bearing; bearing = mobile_bearing;
distance = mobile_distance; distance = mobile_distance;
_transmitter_pos.setElevationFt(_mobile_elevation_ft); _transmitter_pos.setElevationFt(_mobilePos.getElevationFt());
_transmitter_range_nm = _mobile_range_nm; _transmitter_range_nm = _mobile_range_nm;
_transmitter_bias = _mobile_bias; _transmitter_bias = _mobile_bias;
_transmitter_name = _mobile_name; _transmitter_name = _mobile_name;
@ -231,7 +213,7 @@ TACAN::update (double delta_time_sec)
double rotation = 0; double rotation = 0;
double range_nm = adjust_range(_transmitter_pos.getElevationFt(), double range_nm = adjust_range(_transmitter_pos.getElevationFt(),
altitude_m * SG_METER_TO_FEET, pos.getElevationFt(),
_transmitter_range_nm); _transmitter_range_nm);
if (distance_nm <= range_nm) { if (distance_nm <= range_nm) {
@ -283,8 +265,7 @@ TACAN::update (double delta_time_sec)
} // end function update } // end function update
void void
TACAN::search (double frequency_mhz, double longitude_rad, TACAN::search (double frequency_mhz,const SGGeod& pos)
double latitude_rad, double altitude_m)
{ {
int number, i; int number, i;
_mobile_valid = false; _mobile_valid = false;
@ -295,8 +276,7 @@ TACAN::search (double frequency_mhz, double longitude_rad,
_time_before_search_sec = 1.0; _time_before_search_sec = 1.0;
//try any carriers first //try any carriers first
FGNavRecord *mobile_tacan FGNavRecord *mobile_tacan = FGNavList::findByFreq( frequency_mhz, FGNavList::carrierFilter() );
= globals->get_carrierlist()->findStationByFreq( frequency_mhz );
bool freq_valid = (mobile_tacan != NULL); bool freq_valid = (mobile_tacan != NULL);
SG_LOG( SG_INSTR, SG_DEBUG, "mobile freqency valid " << freq_valid ); SG_LOG( SG_INSTR, SG_DEBUG, "mobile freqency valid " << freq_valid );
@ -318,9 +298,10 @@ TACAN::search (double frequency_mhz, double longitude_rad,
string::size_type loc1= str1.find( str2, 0 ); string::size_type loc1= str1.find( str2, 0 );
if ( loc1 != string::npos && str2 != "" ) { if ( loc1 != string::npos && str2 != "" ) {
SG_LOG( SG_INSTR, SG_DEBUG, " string found" ); SG_LOG( SG_INSTR, SG_DEBUG, " string found" );
_mobile_lat = carrier[i]->getDoubleValue("position/latitude-deg"); _mobilePos = SGGeod::fromDegFt(
_mobile_lon = carrier[i]->getDoubleValue("position/longitude-deg"); carrier[i]->getDoubleValue("position/longitude-deg"),
_mobile_elevation_ft = mobile_tacan->get_elev_ft(); carrier[i]->getDoubleValue("position/latitude-deg"),
mobile_tacan->get_elev_ft());
_mobile_range_nm = mobile_tacan->get_range(); _mobile_range_nm = mobile_tacan->get_range();
_mobile_bias = mobile_tacan->get_multiuse(); _mobile_bias = mobile_tacan->get_multiuse();
_mobile_name = mobile_tacan->name(); _mobile_name = mobile_tacan->name();
@ -354,9 +335,12 @@ TACAN::search (double frequency_mhz, double longitude_rad,
string::size_type loc1= str1.find( str4, 0 ); string::size_type loc1= str1.find( str4, 0 );
if ( loc1 != string::npos && str4 != "" ) { if ( loc1 != string::npos && str4 != "" ) {
SG_LOG( SG_INSTR, SG_DEBUG, " string found" ); SG_LOG( SG_INSTR, SG_DEBUG, " string found" );
_mobile_lat = tanker[i]->getDoubleValue("position/latitude-deg"); _mobilePos = SGGeod::fromDegFt(
_mobile_lon = tanker[i]->getDoubleValue("position/longitude-deg"); tanker[i]->getDoubleValue("position/longitude-deg"),
_mobile_elevation_ft = tanker[i]->getDoubleValue("position/altitude-ft"); tanker[i]->getDoubleValue("position/latitude-deg"),
tanker[i]->getDoubleValue("position/altitude-ft"));
_mobile_range_nm = mobile_tacan->get_range(); _mobile_range_nm = mobile_tacan->get_range();
_mobile_bias = mobile_tacan->get_multiuse(); _mobile_bias = mobile_tacan->get_multiuse();
_mobile_name = mobile_tacan->name(); _mobile_name = mobile_tacan->name();
@ -392,9 +376,12 @@ TACAN::search (double frequency_mhz, double longitude_rad,
string::size_type loc1= str1.find( str6, 0 ); string::size_type loc1= str1.find( str6, 0 );
if ( loc1 != string::npos && str6 != "" ) { if ( loc1 != string::npos && str6 != "" ) {
SG_LOG( SG_INSTR, SG_DEBUG, " string found" ); SG_LOG( SG_INSTR, SG_DEBUG, " string found" );
_mobile_lat = mp_tanker[i]->getDoubleValue("position/latitude-deg"); _mobilePos = SGGeod::fromDegFt(
_mobile_lon = mp_tanker[i]->getDoubleValue("position/longitude-deg"); mp_tanker[i]->getDoubleValue("position/longitude-deg"),
_mobile_elevation_ft = mp_tanker[i]->getDoubleValue("position/altitude-ft"); mp_tanker[i]->getDoubleValue("position/latitude-deg"),
mp_tanker[i]->getDoubleValue("position/altitude-ft"));
_mobile_range_nm = mobile_tacan->get_range(); _mobile_range_nm = mobile_tacan->get_range();
_mobile_bias = mobile_tacan->get_multiuse(); _mobile_bias = mobile_tacan->get_multiuse();
_mobile_name = mobile_tacan->name(); _mobile_name = mobile_tacan->name();
@ -403,8 +390,6 @@ TACAN::search (double frequency_mhz, double longitude_rad,
SG_LOG( SG_INSTR, SG_DEBUG, " mp tanker transmitter valid " << _mobile_valid ); SG_LOG( SG_INSTR, SG_DEBUG, " mp tanker transmitter valid " << _mobile_valid );
SG_LOG( SG_INSTR, SG_DEBUG, " mp tanker name " << _mobile_name); SG_LOG( SG_INSTR, SG_DEBUG, " mp tanker name " << _mobile_name);
SG_LOG( SG_INSTR, SG_DEBUG, " mp lat " << _mobile_lat << "lon " << _mobile_lon);
SG_LOG( SG_INSTR, SG_DEBUG, " mp elev " << _mobile_elevation_ft);
SG_LOG( SG_INSTR, SG_DEBUG, " mp range " << _mobile_range_nm); SG_LOG( SG_INSTR, SG_DEBUG, " mp range " << _mobile_range_nm);
break; break;
} else { } else {
@ -420,8 +405,7 @@ TACAN::search (double frequency_mhz, double longitude_rad,
} }
// try the TACAN/VORTAC list next // try the TACAN/VORTAC list next
FGNavRecord *tacan = globals->get_tacanlist()->findByFreq( frequency_mhz, FGNavRecord *tacan = FGNavList::findByFreq( frequency_mhz, pos, FGNavList::tacanFilter() );
SGGeod::fromRadM(longitude_rad, latitude_rad, altitude_m));
_transmitter_valid = (tacan != NULL); _transmitter_valid = (tacan != NULL);

View file

@ -45,17 +45,13 @@ public:
private: private:
void search (double frequency, double longitude_rad, void search (double frequency, const SGGeod& pos);
double latitude_rad, double altitude_m);
double searchChannel (const std::string& channel); double searchChannel (const std::string& channel);
void valueChanged (SGPropertyNode *); void valueChanged (SGPropertyNode *);
std::string _name; std::string _name;
unsigned int _num; unsigned int _num;
SGPropertyNode_ptr _longitude_node;
SGPropertyNode_ptr _latitude_node;
SGPropertyNode_ptr _altitude_node;
SGPropertyNode_ptr _heading_node; SGPropertyNode_ptr _heading_node;
SGPropertyNode_ptr _yaw_node; SGPropertyNode_ptr _yaw_node;
SGPropertyNode_ptr _serviceable_node; SGPropertyNode_ptr _serviceable_node;
@ -102,8 +98,7 @@ private:
std::string _transmitter_name; std::string _transmitter_name;
std::string _transmitter_ident; std::string _transmitter_ident;
double _mobile_lat, _mobile_lon; SGGeod _mobilePos;
double _mobile_elevation_ft;
double _mobile_range_nm; double _mobile_range_nm;
double _mobile_bearing_deg; double _mobile_bearing_deg;
double _mobile_bias; double _mobile_bias;

View file

@ -65,6 +65,11 @@ source_group("Main\\Headers" FILES ${HEADERS})
source_group("Main\\Sources" FILES ${SOURCES}) source_group("Main\\Sources" FILES ${SOURCES})
add_executable(fgfs ${SOURCES} ${FG_SOURCES} ${FG_HEADERS} ${HEADERS}) add_executable(fgfs ${SOURCES} ${FG_SOURCES} ${FG_HEADERS} ${HEADERS})
# disable sqlite3 dynamic lib support
# this should really be a SOURCE property, but the way we handle
# Fcomponent sources is making that tricky
add_definitions(-DSQLITE_OMIT_LOAD_EXTENSION)
get_property(FG_LIBS GLOBAL PROPERTY FG_LIBS) get_property(FG_LIBS GLOBAL PROPERTY FG_LIBS)
#message(STATUS "fg libs ${FG_LIBS}") #message(STATUS "fg libs ${FG_LIBS}")
#message(STATUS "OSG libs ${OPENSCENEGRAPH_LIBRARIES}") #message(STATUS "OSG libs ${OPENSCENEGRAPH_LIBRARIES}")

View file

@ -63,7 +63,6 @@
#include <Aircraft/controls.hxx> #include <Aircraft/controls.hxx>
#include <Aircraft/replay.hxx> #include <Aircraft/replay.hxx>
#include <Airports/apt_loader.hxx>
#include <Airports/runways.hxx> #include <Airports/runways.hxx>
#include <Airports/simple.hxx> #include <Airports/simple.hxx>
#include <Airports/dynamics.hxx> #include <Airports/dynamics.hxx>
@ -90,9 +89,6 @@
#include <AIModel/AIManager.hxx> #include <AIModel/AIManager.hxx>
#include <Navaids/navdb.hxx> #include <Navaids/navdb.hxx>
#include <Navaids/navlist.hxx> #include <Navaids/navlist.hxx>
#include <Navaids/fix.hxx>
#include <Navaids/fixlist.hxx>
#include <Navaids/airways.hxx>
#include <Scenery/scenery.hxx> #include <Scenery/scenery.hxx>
#include <Scenery/tilemgr.hxx> #include <Scenery/tilemgr.hxx>
#include <Scripting/NasalSys.hxx> #include <Scripting/NasalSys.hxx>
@ -107,6 +103,7 @@
#include <Environment/environment_mgr.hxx> #include <Environment/environment_mgr.hxx>
#include <Viewer/renderer.hxx> #include <Viewer/renderer.hxx>
#include <Viewer/viewmgr.hxx> #include <Viewer/viewmgr.hxx>
#include <Navaids/NavDataCache.hxx>
#include "fg_init.hxx" #include "fg_init.hxx"
#include "fg_io.hxx" #include "fg_io.hxx"
@ -454,6 +451,366 @@ bool fgInitConfig ( int argc, char **argv )
return true; return true;
} }
// Set current tower position lon/lat given an airport id
static bool fgSetTowerPosFromAirportID( const string& id) {
const FGAirport *a = fgFindAirportID( id);
if (a) {
SGGeod tower = a->getTowerLocation();
fgSetDouble("/sim/tower/longitude-deg", tower.getLongitudeDeg());
fgSetDouble("/sim/tower/latitude-deg", tower.getLatitudeDeg());
fgSetDouble("/sim/tower/altitude-ft", tower.getElevationFt());
return true;
} else {
return false;
}
}
struct FGTowerLocationListener : SGPropertyChangeListener {
void valueChanged(SGPropertyNode* node) {
string id(node->getStringValue());
if (fgGetBool("/sim/tower/auto-position",true))
{
// enforce using closest airport when auto-positioning is enabled
const char* closest_airport = fgGetString("/sim/airport/closest-airport-id", "");
if (closest_airport && (id != closest_airport))
{
id = closest_airport;
node->setStringValue(id);
}
}
fgSetTowerPosFromAirportID(id);
}
};
struct FGClosestTowerLocationListener : SGPropertyChangeListener
{
void valueChanged(SGPropertyNode* )
{
// closest airport has changed
if (fgGetBool("/sim/tower/auto-position",true))
{
// update tower position
const char* id = fgGetString("/sim/airport/closest-airport-id", "");
if (id && *id!=0)
fgSetString("/sim/tower/airport-id", id);
}
}
};
void fgInitTowerLocationListener() {
fgGetNode("/sim/tower/airport-id", true)
->addChangeListener( new FGTowerLocationListener(), true );
FGClosestTowerLocationListener* ntcl = new FGClosestTowerLocationListener();
fgGetNode("/sim/airport/closest-airport-id", true)
->addChangeListener(ntcl , true );
fgGetNode("/sim/tower/auto-position", true)
->addChangeListener(ntcl, true );
}
static void fgApplyStartOffset(const SGGeod& aStartPos, double aHeading, double aTargetHeading = HUGE_VAL)
{
SGGeod startPos(aStartPos);
if (aTargetHeading == HUGE_VAL) {
aTargetHeading = aHeading;
}
if ( fabs( fgGetDouble("/sim/presets/offset-distance-nm") ) > SG_EPSILON ) {
double offsetDistance = fgGetDouble("/sim/presets/offset-distance-nm");
offsetDistance *= SG_NM_TO_METER;
double offsetAzimuth = aHeading;
if ( fabs(fgGetDouble("/sim/presets/offset-azimuth-deg")) > SG_EPSILON ) {
offsetAzimuth = fgGetDouble("/sim/presets/offset-azimuth-deg");
aHeading = aTargetHeading;
}
SGGeod offset;
double az2; // dummy
SGGeodesy::direct(startPos, offsetAzimuth + 180, offsetDistance, offset, az2);
startPos = offset;
}
// presets
fgSetDouble("/sim/presets/longitude-deg", startPos.getLongitudeDeg() );
fgSetDouble("/sim/presets/latitude-deg", startPos.getLatitudeDeg() );
fgSetDouble("/sim/presets/heading-deg", aHeading );
// other code depends on the actual values being set ...
fgSetDouble("/position/longitude-deg", startPos.getLongitudeDeg() );
fgSetDouble("/position/latitude-deg", startPos.getLatitudeDeg() );
fgSetDouble("/orientation/heading-deg", aHeading );
}
// Set current_options lon/lat given an airport id and heading (degrees)
bool fgSetPosFromAirportIDandHdg( const string& id, double tgt_hdg ) {
if ( id.empty() )
return false;
// set initial position from runway and heading
SG_LOG( SG_GENERAL, SG_INFO,
"Attempting to set starting position from airport code "
<< id << " heading " << tgt_hdg );
const FGAirport* apt = fgFindAirportID(id);
if (!apt) return false;
FGRunway* r = apt->findBestRunwayForHeading(tgt_hdg);
fgSetString("/sim/atc/runway", r->ident().c_str());
SGGeod startPos = r->pointOnCenterline(fgGetDouble("/sim/airport/runways/start-offset-m", 5.0));
fgApplyStartOffset(startPos, r->headingDeg(), tgt_hdg);
return true;
}
// Set current_options lon/lat given an airport id and parkig position name
static bool fgSetPosFromAirportIDandParkpos( const string& id, const string& parkpos ) {
if ( id.empty() )
return false;
// can't see an easy way around this const_cast at the moment
FGAirport* apt = const_cast<FGAirport*>(fgFindAirportID(id));
if (!apt) {
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find airport " << id );
return false;
}
FGAirportDynamics* dcs = apt->getDynamics();
if (!dcs) {
SG_LOG( SG_GENERAL, SG_ALERT,
"Airport " << id << "does not appear to have parking information available");
return false;
}
int park_index = dcs->getNrOfParkings() - 1;
bool succes;
double radius = fgGetDouble("/sim/dimensions/radius-m");
if ((parkpos == string("AVAILABLE")) && (radius > 0)) {
double lat, lon, heading;
string fltType;
string acOperator;
SGPath acData;
try {
acData = globals->get_fg_home();
acData.append("aircraft-data");
string acfile = fgGetString("/sim/aircraft") + string(".xml");
acData.append(acfile);
SGPropertyNode root;
readProperties(acData.str(), &root);
SGPropertyNode * node = root.getNode("sim");
fltType = node->getStringValue("aircraft-class", "NONE" );
acOperator = node->getStringValue("aircraft-operator", "NONE" );
} catch (const sg_exception &) {
SG_LOG(SG_GENERAL, SG_INFO,
"Could not load aircraft aircrat type and operator information from: " << acData.str() << ". Using defaults");
// cout << path.str() << endl;
}
if (fltType.empty() || fltType == "NONE") {
SG_LOG(SG_GENERAL, SG_INFO,
"Aircraft type information not found in: " << acData.str() << ". Using default value");
fltType = fgGetString("/sim/aircraft-class" );
}
if (acOperator.empty() || fltType == "NONE") {
SG_LOG(SG_GENERAL, SG_INFO,
"Aircraft operator information not found in: " << acData.str() << ". Using default value");
acOperator = fgGetString("/sim/aircraft-operator" );
}
cerr << "Running aircraft " << fltType << " of livery " << acOperator << endl;
string acType; // Currently not used by findAvailable parking, so safe to leave empty.
succes = dcs->getAvailableParking(&lat, &lon, &heading, &park_index, radius, fltType, acType, acOperator);
if (succes) {
fgGetString("/sim/presets/parkpos");
fgSetString("/sim/presets/parkpos", dcs->getParking(park_index)->getName());
} else {
SG_LOG( SG_GENERAL, SG_ALERT,
"Failed to find a suitable parking at airport " << id );
return false;
}
} else {
//cerr << "We shouldn't get here when AVAILABLE" << endl;
while (park_index >= 0 && dcs->getParkingName(park_index) != parkpos) park_index--;
if (park_index < 0) {
SG_LOG( SG_GENERAL, SG_ALERT,
"Failed to find parking position " << parkpos <<
" at airport " << id );
return false;
}
}
FGParking* parking = dcs->getParking(park_index);
parking->setAvailable(false);
fgApplyStartOffset(
SGGeod::fromDeg(parking->getLongitude(), parking->getLatitude()),
parking->getHeading());
return true;
}
// Set current_options lon/lat given an airport id and runway number
static bool fgSetPosFromAirportIDandRwy( const string& id, const string& rwy, bool rwy_req ) {
if ( id.empty() )
return false;
// set initial position from airport and runway number
SG_LOG( SG_GENERAL, SG_INFO,
"Attempting to set starting position for "
<< id << ":" << rwy );
const FGAirport* apt = fgFindAirportID(id);
if (!apt) {
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find airport:" << id);
return false;
}
if (!apt->hasRunwayWithIdent(rwy)) {
SG_LOG( SG_GENERAL, rwy_req ? SG_ALERT : SG_INFO,
"Failed to find runway " << rwy <<
" at airport " << id << ". Using default runway." );
return false;
}
FGRunway* r(apt->getRunwayByIdent(rwy));
fgSetString("/sim/atc/runway", r->ident().c_str());
SGGeod startPos = r->pointOnCenterline( fgGetDouble("/sim/airport/runways/start-offset-m", 5.0));
fgApplyStartOffset(startPos, r->headingDeg());
return true;
}
static void fgSetDistOrAltFromGlideSlope() {
// cout << "fgSetDistOrAltFromGlideSlope()" << endl;
string apt_id = fgGetString("/sim/presets/airport-id");
double gs = fgGetDouble("/sim/presets/glideslope-deg")
* SG_DEGREES_TO_RADIANS ;
double od = fgGetDouble("/sim/presets/offset-distance-nm");
double alt = fgGetDouble("/sim/presets/altitude-ft");
double apt_elev = 0.0;
if ( ! apt_id.empty() ) {
apt_elev = fgGetAirportElev( apt_id );
if ( apt_elev < -9990.0 ) {
apt_elev = 0.0;
}
} else {
apt_elev = 0.0;
}
if( fabs(gs) > 0.01 && fabs(od) > 0.1 && alt < -9990 ) {
// set altitude from glideslope and offset-distance
od *= SG_NM_TO_METER * SG_METER_TO_FEET;
alt = fabs(od*tan(gs)) + apt_elev;
fgSetDouble("/sim/presets/altitude-ft", alt);
fgSetBool("/sim/presets/onground", false);
SG_LOG( SG_GENERAL, SG_INFO, "Calculated altitude as: "
<< alt << " ft" );
} else if( fabs(gs) > 0.01 && alt > 0 && fabs(od) < 0.1) {
// set offset-distance from glideslope and altitude
od = (alt - apt_elev) / tan(gs);
od *= -1*SG_FEET_TO_METER * SG_METER_TO_NM;
fgSetDouble("/sim/presets/offset-distance-nm", od);
fgSetBool("/sim/presets/onground", false);
SG_LOG( SG_GENERAL, SG_INFO, "Calculated offset distance as: "
<< od << " nm" );
} else if( fabs(gs) > 0.01 ) {
SG_LOG( SG_GENERAL, SG_ALERT,
"Glideslope given but not altitude or offset-distance." );
SG_LOG( SG_GENERAL, SG_ALERT, "Resetting glideslope to zero" );
fgSetDouble("/sim/presets/glideslope-deg", 0);
fgSetBool("/sim/presets/onground", true);
}
}
// Set current_options lon/lat given an airport id and heading (degrees)
static bool fgSetPosFromNAV( const string& id, const double& freq, FGPositioned::Type type )
{
FGNavList::TypeFilter filter(type);
const nav_list_type navlist
= FGNavList::findByIdentAndFreq( id.c_str(), freq, &filter );
if (navlist.size() == 0 ) {
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate NAV = "
<< id << ":" << freq );
return false;
}
if( navlist.size() > 1 ) {
ostringstream buf;
buf << "Ambigous NAV-ID: '" << id << "'. Specify id and frequency. Available stations:" << endl;
for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
// NDB stored in kHz, VOR stored in MHz * 100 :-P
double factor = (*it)->type() == FGPositioned::NDB ? 1.0 : 1/100.0;
string unit = (*it)->type() == FGPositioned::NDB ? "kHz" : "MHz";
buf << (*it)->ident() << " "
<< setprecision(5) << (double)((*it)->get_freq() * factor) << " "
<< (*it)->get_lat() << "/" << (*it)->get_lon()
<< endl;
}
SG_LOG( SG_GENERAL, SG_ALERT, buf.str() );
return false;
}
FGNavRecord *nav = navlist[0];
fgApplyStartOffset(nav->geod(), fgGetDouble("/sim/presets/heading-deg"));
return true;
}
// Set current_options lon/lat given an aircraft carrier id
static bool fgSetPosFromCarrier( const string& carrier, const string& posid ) {
// set initial position from runway and heading
SGGeod geodPos;
double heading;
SGVec3d uvw;
if (FGAIManager::getStartPosition(carrier, posid, geodPos, heading, uvw)) {
double lon = geodPos.getLongitudeDeg();
double lat = geodPos.getLatitudeDeg();
double alt = geodPos.getElevationFt();
SG_LOG( SG_GENERAL, SG_INFO, "Attempting to set starting position for "
<< carrier << " at lat = " << lat << ", lon = " << lon
<< ", alt = " << alt << ", heading = " << heading);
fgSetDouble("/sim/presets/longitude-deg", lon);
fgSetDouble("/sim/presets/latitude-deg", lat);
fgSetDouble("/sim/presets/altitude-ft", alt);
fgSetDouble("/sim/presets/heading-deg", heading);
fgSetDouble("/position/longitude-deg", lon);
fgSetDouble("/position/latitude-deg", lat);
fgSetDouble("/position/altitude-ft", alt);
fgSetDouble("/orientation/heading-deg", heading);
fgSetString("/sim/presets/speed-set", "UVW");
fgSetDouble("/velocities/uBody-fps", uvw(0));
fgSetDouble("/velocities/vBody-fps", uvw(1));
fgSetDouble("/velocities/wBody-fps", uvw(2));
fgSetDouble("/sim/presets/uBody-fps", uvw(0));
fgSetDouble("/sim/presets/vBody-fps", uvw(1));
fgSetDouble("/sim/presets/wBody-fps", uvw(2));
fgSetBool("/sim/presets/onground", true);
return true;
} else {
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate aircraft carrier = "
<< carrier );
return false;
}
}
// Set current_options lon/lat given an airport id and heading (degrees)
static bool fgSetPosFromFix( const string& id )
{
FGPositioned::TypeFilter fixFilter(FGPositioned::FIX);
FGPositioned* fix = FGPositioned::findFirstWithIdent(id, &fixFilter);
if (!fix) {
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate fix = " << id );
return false;
}
fgApplyStartOffset(fix->geod(), fgGetDouble("/sim/presets/heading-deg"));
return true;
}
/** /**
* Initialize vor/ndb/ils/fix list management and query systems (as * Initialize vor/ndb/ils/fix list management and query systems (as
* well as simple airport db list) * well as simple airport db list)
@ -461,45 +818,23 @@ bool fgInitConfig ( int argc, char **argv )
bool bool
fgInitNav () fgInitNav ()
{ {
SG_LOG(SG_GENERAL, SG_INFO, "Loading Airport Database ..."); SG_LOG(SG_GENERAL, SG_INFO, "Loading Airport Database ...");
SGPath aptdb( globals->get_fg_root() ); flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
aptdb.append( "Airports/apt.dat" ); if (cache->isRebuildRequired()) {
SGTimeStamp st;
SGPath p_metar( globals->get_fg_root() ); st.stamp();
p_metar.append( "Airports/metar.dat" ); cache->rebuild();
fgAirportDBLoad( aptdb.str(), p_metar.str() ); SG_LOG(SG_GENERAL, SG_INFO, "rebuilding NavDataCache took:" << st.elapsedMSec());
FGNavList *navlist = new FGNavList;
FGNavList *loclist = new FGNavList;
FGNavList *gslist = new FGNavList;
FGNavList *dmelist = new FGNavList;
FGNavList *tacanlist = new FGNavList;
FGNavList *carrierlist = new FGNavList;
FGTACANList *channellist = new FGTACANList;
globals->set_navlist( navlist );
globals->set_loclist( loclist );
globals->set_gslist( gslist );
globals->set_dmelist( dmelist );
globals->set_tacanlist( tacanlist );
globals->set_carrierlist( carrierlist );
globals->set_channellist( channellist );
if ( !fgNavDBInit(navlist, loclist, gslist, dmelist, tacanlist, carrierlist, channellist) ) {
SG_LOG( SG_GENERAL, SG_ALERT,
"Problems loading one or more navigational database" );
} }
SG_LOG(SG_GENERAL, SG_INFO, " Fixes"); FGTACANList *channellist = new FGTACANList;
SGPath p_fix( globals->get_fg_root() ); globals->set_channellist( channellist );
p_fix.append( "Navaids/fix.dat" );
FGFixList fixlist;
fixlist.init( p_fix ); // adds fixes to the DB in positioned.cxx
SG_LOG(SG_GENERAL, SG_INFO, " Airways"); SGPath path(globals->get_fg_root());
flightgear::Airway::load(); path.append( "Navaids/TACAN_freq.dat" );
flightgear::loadTacan(path, channellist);
return true; return true;
} }

View file

@ -302,6 +302,7 @@ public:
inline FGFontCache *get_fontcache() const { return fontcache; } inline FGFontCache *get_fontcache() const { return fontcache; }
#if 0
inline FGNavList *get_navlist() const { return navlist; } inline FGNavList *get_navlist() const { return navlist; }
inline void set_navlist( FGNavList *n ) { navlist = n; } inline void set_navlist( FGNavList *n ) { navlist = n; }
inline FGNavList *get_loclist() const { return loclist; } inline FGNavList *get_loclist() const { return loclist; }
@ -314,9 +315,11 @@ public:
inline void set_tacanlist( FGNavList *n ) { tacanlist = n; } inline void set_tacanlist( FGNavList *n ) { tacanlist = n; }
inline FGNavList *get_carrierlist() const { return carrierlist; } inline FGNavList *get_carrierlist() const { return carrierlist; }
inline void set_carrierlist( FGNavList *n ) { carrierlist = n; } inline void set_carrierlist( FGNavList *n ) { carrierlist = n; }
#endif
inline FGTACANList *get_channellist() const { return channellist; } inline FGTACANList *get_channellist() const { return channellist; }
inline void set_channellist( FGTACANList *c ) { channellist = c; } inline void set_channellist( FGTACANList *c ) { channellist = c; }
/** /**
* Save the current state as the initial state. * Save the current state as the initial state.
*/ */

View file

@ -60,6 +60,7 @@
#include <Viewer/WindowSystemAdapter.hxx> #include <Viewer/WindowSystemAdapter.hxx>
#include <Viewer/splash.hxx> #include <Viewer/splash.hxx>
#include <Viewer/renderer.hxx> #include <Viewer/renderer.hxx>
#include <Navaids/NavDataCache.hxx>
#include "fg_commands.hxx" #include "fg_commands.hxx"
#include "fg_io.hxx" #include "fg_io.hxx"
@ -438,5 +439,9 @@ int fgMainInit( int argc, char **argv ) {
delete globals; delete globals;
globals = NULL; globals = NULL;
// delete the NavCache here. This will cause the destruction of many cached
// objects (eg, airports, navaids, runways).
delete flightgear::NavDataCache::instance();
return result; return result;
} }

View file

@ -307,10 +307,10 @@ static void fgSetDistOrAltFromGlideSlope() {
// Set current_options lon/lat given an airport id and heading (degrees) // Set current_options lon/lat given an airport id and heading (degrees)
static bool fgSetPosFromNAV( const string& id, const double& freq, FGPositioned::Type type ) { static bool fgSetPosFromNAV( const string& id, const double& freq, FGPositioned::Type type )
{
const nav_list_type navlist FGNavList::TypeFilter filter(type);
= globals->get_navlist()->findByIdentAndFreq( id.c_str(), freq, type ); const nav_list_type navlist = FGNavList::findByIdentAndFreq( id.c_str(), freq, &filter );
if (navlist.size() == 0 ) { if (navlist.size() == 0 ) {
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate NAV = " SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate NAV = "
@ -387,7 +387,7 @@ static bool fgSetPosFromCarrier( const string& carrier, const string& posid ) {
static bool fgSetPosFromFix( const string& id ) static bool fgSetPosFromFix( const string& id )
{ {
FGPositioned::TypeFilter fixFilter(FGPositioned::FIX); FGPositioned::TypeFilter fixFilter(FGPositioned::FIX);
FGPositioned* fix = FGPositioned::findNextWithPartialId(NULL, id, &fixFilter); FGPositioned* fix = FGPositioned::findFirstWithIdent(id, &fixFilter);
if (!fix) { if (!fix) {
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate fix = " << id ); SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate fix = " << id );
return false; return false;

View file

@ -14,6 +14,9 @@ set(SOURCES
waypoint.cxx waypoint.cxx
LevelDXML.cxx LevelDXML.cxx
FlightPlan.cxx FlightPlan.cxx
NavDataCache.cxx
sqlite3.c
PositionedOctree.cxx
) )
set(HEADERS set(HEADERS
@ -30,6 +33,9 @@ set(HEADERS
waypoint.hxx waypoint.hxx
LevelDXML.hxx LevelDXML.hxx
FlightPlan.hxx FlightPlan.hxx
NavDataCache.hxx
sqlite3.h
PositionedOctree.hxx
) )
flightgear_component(Navaids "${SOURCES}" "${HEADERS}") flightgear_component(Navaids "${SOURCES}" "${HEADERS}")

1616
src/Navaids/NavDataCache.cxx Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,186 @@
/**
* NavDataCache.hxx - defines a unified binary cache for navigation
* data, parsed from various text / XML sources.
*/
// Written by James Turner, started 2012.
//
// Copyright (C) 2012 James Turner
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef FG_NAVDATACACHE_HXX
#define FG_NAVDATACACHE_HXX
#include <memory>
#include <Navaids/positioned.hxx>
class SGPath;
namespace flightgear
{
/// a pair of airport ID, runway ID
typedef std::pair<PositionedID, PositionedID> AirportRunwayPair;
typedef std::pair<FGPositioned::Type, PositionedID> TypedPositioned;
typedef std::vector<TypedPositioned> TypedPositionedVec;
// pair of airway ID, destination node ID
typedef std::pair<int, PositionedID> AirwayEdge;
typedef std::vector<AirwayEdge> AirwayEdgeVec;
namespace Octree {
class Node;
class Branch;
}
class NavDataCache
{
public:
~NavDataCache();
// singleton accessor
static NavDataCache* instance();
/**
* predicate - check if the cache needs to be rebuilt.
* This can happen is the cache file is missing or damaged, or one of the
** global input files is changed.
*/
bool isRebuildRequired();
/**
* run the cache rebuild
*/
void rebuild();
bool isCachedFileModified(const SGPath& path) const;
void stampCacheFile(const SGPath& path);
int readIntProperty(const std::string& key);
double readDoubleProperty(const std::string& key);
std::string readStringProperty(const std::string& key);
void writeIntProperty(const std::string& key, int value);
void writeStringProperty(const std::string& key, const std::string& value);
void writeDoubleProperty(const std::string& key, const double& value);
FGPositioned* loadById(PositionedID guid);
PositionedID insertAirport(FGPositioned::Type ty, const std::string& ident,
const std::string& name);
void insertTower(PositionedID airportId, const SGGeod& pos);
PositionedID insertRunway(FGPositioned::Type ty, const std::string& ident,
const SGGeod& pos, PositionedID apt,
double heading, double length, double width, double displacedThreshold,
double stopway, int surfaceCode);
void setRunwayReciprocal(PositionedID runway, PositionedID recip);
void setRunwayILS(PositionedID runway, PositionedID ils);
void updateRunwayThreshold(PositionedID runwayID, const SGGeod &aThreshold,
double aHeading, double aDisplacedThreshold,
double aStopway);
PositionedID insertNavaid(FGPositioned::Type ty, const std::string& ident,
const std::string& name, const SGGeod& pos, int freq, int range, double multiuse,
PositionedID apt, PositionedID runway);
void updateILS(PositionedID ils, const SGGeod& newPos, double aHdg);
PositionedID insertCommStation(FGPositioned::Type ty,
const std::string& name, const SGGeod& pos, int freq, int range,
PositionedID apt);
PositionedID insertFix(const std::string& ident, const SGGeod& aPos);
PositionedID createUserWaypoint(const std::string& ident, const SGGeod& aPos);
void setAirportMetar(const std::string& icao, bool hasMetar);
void updatePosition(PositionedID item, const SGGeod &pos);
FGPositioned::List findAllWithIdent(const std::string& ident,
FGPositioned::Filter* filter, bool exact);
FGPositioned::List findAllWithName(const std::string& ident,
FGPositioned::Filter* filter, bool exact);
FGPositionedRef findClosestWithIdent(const std::string& aIdent,
const SGGeod& aPos, FGPositioned::Filter* aFilter);
char** searchAirportNamesAndIdents(const std::string& aFilter);
FGPositionedRef findCommByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt);
PositionedIDVec airportItemsOfType(PositionedID apt, FGPositioned::Type ty,
FGPositioned::Type maxTy = FGPositioned::INVALID);
PositionedID airportItemWithIdent(PositionedID apt, FGPositioned::Type ty, const std::string& ident);
PositionedIDVec findNavaidsByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt);
/// overload version of the above that does not consider positioned when
/// returning results. Only used by TACAN carrier search
PositionedIDVec findNavaidsByFreq(int freqKhz, FGPositioned::Filter* filt);
/**
* given a navaid name (or similar) from apt.dat / nav.dat, find the
* corresponding airport and runway IDs.
* Such names look like: 'LHBP 31L DME-ILS' or 'UEEE 23L MM'
*/
AirportRunwayPair findAirportRunway(const std::string& name);
/**
* Given an aiport, and runway and ILS identifier, find the corresponding cache
* entry. This matches the data we get in the ils.xml files for airports.
*/
PositionedID findILS(PositionedID airport, const std::string& runway, const std::string& navIdent);
/**
* Given an Octree node ID, return a bit-mask defining which of the child
* nodes exist. In practice this means an 8-bit value be sufficent, but
* an int works fine too.
*/
int getOctreeBranchChildren(int64_t octreeNodeId);
void defineOctreeNode(Octree::Branch* pr, Octree::Node* nd);
/**
* given an octree leaf, return all its child positioned items and their types
*/
TypedPositionedVec getOctreeLeafChildren(int64_t octreeNodeId);
// airways
int findAirway(int network, const std::string& aName);
void insertEdge(int network, int airwayID, PositionedID from, PositionedID to);
bool isInAirwayNetwork(int network, PositionedID pos);
/**
* retrive all the destination points reachcable from a positioned
* in an airway
*/
AirwayEdgeVec airwayEdgesFrom(int network, PositionedID pos);
private:
NavDataCache();
class NavDataCachePrivate;
std::auto_ptr<NavDataCachePrivate> d;
};
} // of namespace flightgear
#endif // of FG_NAVDATACACHE_HXX

View file

@ -0,0 +1,286 @@
/**
* PositionedOctree - define a spatial octree containing Positioned items
* arranged by their global cartesian position.
*/
// Written by James Turner, started 2012.
//
// Copyright (C) 2012 James Turner
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "PositionedOctree.hxx"
#include "positioned.hxx"
#include <cassert>
#include <algorithm> // for sort
#include <cstring> // for memset
#include <iostream>
#include <boost/foreach.hpp>
#include <simgear/debug/logstream.hxx>
namespace flightgear
{
namespace Octree
{
Node* global_spatialOctree = NULL;
Leaf::Leaf(const SGBoxd& aBox, int64_t aIdent) :
Node(aBox, aIdent),
childrenLoaded(false)
{
}
void Leaf::visit(const SGVec3d& aPos, double aCutoff,
FGPositioned::Filter* aFilter,
FindNearestResults& aResults, FindNearestPQueue&)
{
int previousResultsSize = aResults.size();
int addedCount = 0;
NavDataCache* cache = NavDataCache::instance();
loadChildren();
ChildMap::const_iterator it = children.lower_bound(aFilter->minType());
ChildMap::const_iterator end = children.upper_bound(aFilter->maxType());
for (; it != end; ++it) {
FGPositioned* p = cache->loadById(it->second);
assert(intersects(_box, p->cart()));
double d = dist(aPos, p->cart());
if (d > aCutoff) {
continue;
}
if (aFilter && !aFilter->pass(p)) {
continue;
}
++addedCount;
aResults.push_back(OrderedPositioned(p, d));
}
if (addedCount == 0) {
return;
}
// keep aResults sorted
// sort the new items, usually just one or two items
std::sort(aResults.begin() + previousResultsSize, aResults.end());
// merge the two sorted ranges together - in linear time
std::inplace_merge(aResults.begin(),
aResults.begin() + previousResultsSize, aResults.end());
}
void Leaf::insertChild(FGPositioned::Type ty, PositionedID id)
{
assert(childrenLoaded);
children.insert(children.end(), TypedPositioned(ty, id));
}
void Leaf::loadChildren()
{
if (childrenLoaded) {
return;
}
NavDataCache* cache = NavDataCache::instance();
BOOST_FOREACH(TypedPositioned tp, cache->getOctreeLeafChildren(guid())) {
children.insert(children.end(), tp);
} // of leaf members iteration
childrenLoaded = true;
}
Branch::Branch(const SGBoxd& aBox, int64_t aIdent) :
Node(aBox, aIdent),
childrenLoaded(false)
{
memset(children, 0, sizeof(Node*) * 8);
}
void Branch::visit(const SGVec3d& aPos, double aCutoff,
FGPositioned::Filter*,
FindNearestResults&, FindNearestPQueue& aQ)
{
loadChildren();
for (unsigned int i=0; i<8; ++i) {
if (!children[i]) {
continue;
}
double d = children[i]->distToNearest(aPos);
if (d > aCutoff) {
continue; // exceeded cutoff
}
aQ.push(Ordered<Node*>(children[i], d));
} // of child iteration
}
Node* Branch::childForPos(const SGVec3d& aCart) const
{
assert(contains(aCart));
int childIndex = 0;
SGVec3d center(_box.getCenter());
// tests must match indices in SGbox::getCorner
if (aCart.x() < center.x()) {
childIndex += 1;
}
if (aCart.y() < center.y()) {
childIndex += 2;
}
if (aCart.z() < center.z()) {
childIndex += 4;
}
return childAtIndex(childIndex);
}
Node* Branch::childAtIndex(int childIndex) const
{
Node* child = children[childIndex];
if (!child) { // lazy building of children
SGBoxd cb(boxForChild(childIndex));
double d2 = dot(cb.getSize(), cb.getSize());
assert(((_ident << 3) >> 3) == _ident);
// child index is 0..7, so 3-bits is sufficient, and hence we can
// pack 20 levels of octree into a int64, which is plenty
int64_t childIdent = (_ident << 3) | childIndex;
if (d2 < LEAF_SIZE_SQR) {
child = new Leaf(cb, childIdent);
} else {
child = new Branch(cb, childIdent);
}
children[childIndex] = child;
if (childrenLoaded) {
// childrenLoad is done, so we're defining a new node - add it to the
// cache too.
NavDataCache::instance()->defineOctreeNode(const_cast<Branch*>(this), child);
}
}
return children[childIndex];
}
void Branch::loadChildren() const
{
if (childrenLoaded) {
return;
}
int childrenMask = NavDataCache::instance()->getOctreeBranchChildren(guid());
for (int i=0; i<8; ++i) {
if ((1 << i) & childrenMask) {
childAtIndex(i); // accessing will create!
}
} // of child index iteration
// set this after creating the child nodes, so the cache update logic
// in childAtIndex knows any future created children need to be added.
childrenLoaded = true;
}
int Branch::childMask() const
{
int result = 0;
for (int i=0; i<8; ++i) {
if (children[i]) {
result |= 1 << i;
}
}
return result;
}
void findNearestN(const SGVec3d& aPos, unsigned int aN, double aCutoffM, FGPositioned::Filter* aFilter, FGPositioned::List& aResults)
{
aResults.clear();
FindNearestPQueue pq;
FindNearestResults results;
pq.push(Ordered<Node*>(global_spatialOctree, 0));
double cut = aCutoffM;
while (!pq.empty()) {
if (!results.empty()) {
// terminate the search if we have sufficent results, and we are
// sure no node still on the queue contains a closer match
double furthestResultOrder = results.back().order();
// std::cout << "furthest result:" << furthestResultOrder << ", top node order:" << pq.top().order() << std::endl;
if ((results.size() >= aN) && (furthestResultOrder < pq.top().order())) {
break;
}
}
Node* nd = pq.top().get();
pq.pop();
// std::cout << "visiting:" << std::oct << nd->guid() << "(" << std::dec << nd->guid() << ")" << std::endl;
nd->visit(aPos, cut, aFilter, results, pq);
} // of queue iteration
// depending on leaf population, we may have (slighty) more results
// than requested
unsigned int numResults = std::min((unsigned int) results.size(), aN);
// copy results out
aResults.resize(numResults);
for (unsigned int r=0; r<numResults; ++r) {
aResults[r] = results[r].get();
}
}
void findAllWithinRange(const SGVec3d& aPos, double aRangeM, FGPositioned::Filter* aFilter, FGPositioned::List& aResults)
{
aResults.clear();
FindNearestPQueue pq;
FindNearestResults results;
pq.push(Ordered<Node*>(global_spatialOctree, 0));
double rng = aRangeM;
while (!pq.empty()) {
Node* nd = pq.top().get();
pq.pop();
nd->visit(aPos, rng, aFilter, results, pq);
} // of queue iteration
unsigned int numResults = results.size();
// copy results out
aResults.resize(numResults);
for (unsigned int r=0; r<numResults; ++r) {
aResults[r] = results[r].get();
}
}
} // of namespace Octree
} // of namespace flightgear

View file

@ -0,0 +1,224 @@
/**
* PositionedOctree - define a spatial octree containing Positioned items
* arranged by their global cartesian position.
*/
// Written by James Turner, started 2012.
//
// Copyright (C) 2012 James Turner
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef FG_POSITIONED_OCTREE_HXX
#define FG_POSITIONED_OCTREE_HXX
// std
#include <vector>
#include <set>
#include <queue>
#include <cassert>
#include <map>
// SimGear
#include <simgear/math/SGGeometry.hxx>
#include <Navaids/positioned.hxx>
#include <Navaids/NavDataCache.hxx>
namespace flightgear
{
namespace Octree
{
const double LEAF_SIZE = SG_NM_TO_METER * 8.0;
const double LEAF_SIZE_SQR = LEAF_SIZE * LEAF_SIZE;
/**
* Decorate an object with a double value, and use that value to order
* items, for the purpoises of the STL algorithms
*/
template <class T>
class Ordered
{
public:
Ordered(const T& v, double x) :
_order(x),
_inner(v)
{
assert(!isnan(x));
}
Ordered(const Ordered<T>& a) :
_order(a._order),
_inner(a._inner)
{
}
Ordered<T>& operator=(const Ordered<T>& a)
{
_order = a._order;
_inner = a._inner;
return *this;
}
bool operator<(const Ordered<T>& other) const
{
return _order < other._order;
}
bool operator>(const Ordered<T>& other) const
{
return _order > other._order;
}
const T& get() const
{ return _inner; }
double order() const
{ return _order; }
private:
double _order;
T _inner;
};
class Node;
typedef Ordered<Node*> OrderedNode;
typedef std::greater<OrderedNode> FNPQCompare;
/**
* the priority queue is fundamental to our search algorithm. When searching,
* we know the front of the queue is the nearest unexpanded node (to the search
* location). The default STL pqueue returns the 'largest' item from top(), so
* to get the smallest, we need to replace the default Compare functor (less<>)
* with greater<>.
*/
typedef std::priority_queue<OrderedNode, std::vector<OrderedNode>, FNPQCompare> FindNearestPQueue;
typedef Ordered<FGPositioned*> OrderedPositioned;
typedef std::vector<OrderedPositioned> FindNearestResults;
extern Node* global_spatialOctree;
class Leaf;
/**
* Octree node base class, tracks its bounding box and provides various
* queries relating to it
*/
class Node
{
public:
int64_t guid() const
{ return _ident; }
const SGBoxd& bbox() const
{ return _box; }
bool contains(const SGVec3d& aPos) const
{
return intersects(aPos, _box);
}
double distToNearest(const SGVec3d& aPos) const
{
return dist(aPos, _box.getClosestPoint(aPos));
}
virtual void visit(const SGVec3d& aPos, double aCutoff,
FGPositioned::Filter* aFilter,
FindNearestResults& aResults, FindNearestPQueue&) = 0;
virtual Leaf* findLeafForPos(const SGVec3d& aPos) const = 0;
protected:
Node(const SGBoxd &aBox, int64_t aIdent) :
_ident(aIdent),
_box(aBox)
{
}
const int64_t _ident;
const SGBoxd _box;
};
class Leaf : public Node
{
public:
Leaf(const SGBoxd& aBox, int64_t aIdent);
virtual void visit(const SGVec3d& aPos, double aCutoff,
FGPositioned::Filter* aFilter,
FindNearestResults& aResults, FindNearestPQueue&);
virtual Leaf* findLeafForPos(const SGVec3d&) const
{
return const_cast<Leaf*>(this);
}
void insertChild(FGPositioned::Type ty, PositionedID id);
private:
bool childrenLoaded;
typedef std::multimap<FGPositioned::Type, PositionedID> ChildMap;
ChildMap children;
void loadChildren();
};
class Branch : public Node
{
public:
Branch(const SGBoxd& aBox, int64_t aIdent);
virtual void visit(const SGVec3d& aPos, double aCutoff,
FGPositioned::Filter*,
FindNearestResults&, FindNearestPQueue& aQ);
virtual Leaf* findLeafForPos(const SGVec3d& aPos) const
{
loadChildren();
return childForPos(aPos)->findLeafForPos(aPos);
}
int childMask() const;
private:
Node* childForPos(const SGVec3d& aCart) const;
Node* childAtIndex(int childIndex) const;
/**
* Return the box for a child touching the specified corner
*/
SGBoxd boxForChild(unsigned int aCorner) const
{
SGBoxd r(_box.getCenter());
r.expandBy(_box.getCorner(aCorner));
return r;
}
void loadChildren() const;
mutable Node* children[8];
mutable bool childrenLoaded;
};
void findNearestN(const SGVec3d& aPos, unsigned int aN, double aCutoffM, FGPositioned::Filter* aFilter, FGPositioned::List& aResults);
void findAllWithinRange(const SGVec3d& aPos, double aRangeM, FGPositioned::Filter* aFilter, FGPositioned::List& aResults);
} // of namespace Octree
} // of namespace flightgear
#endif // of FG_POSITIONED_OCTREE_HXX

View file

@ -31,55 +31,31 @@
#include <simgear/misc/sgstream.hxx> #include <simgear/misc/sgstream.hxx>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
#include <Main/globals.hxx> #include <Main/globals.hxx>
#include <Navaids/positioned.hxx> #include <Navaids/positioned.hxx>
#include <Navaids/waypoint.hxx> #include <Navaids/waypoint.hxx>
#include <Navaids/NavDataCache.hxx>
using std::make_pair; using std::make_pair;
using std::string; using std::string;
using std::set; using std::set;
using std::vector; using std::vector;
#define DEBUG_AWY_SEARCH 1 //#define DEBUG_AWY_SEARCH 1
namespace flightgear namespace flightgear
{ {
Airway::Network* Airway::static_lowLevel = NULL;
Airway::Network* Airway::static_highLevel = NULL;
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/**
* information about an edge in the network.
* Some of this information is computed lazily
*/
class AdjacentWaypoint
{
public:
AdjacentWaypoint(const FGPositionedRef& aOrigin,
const FGPositionedRef& aDest, Airway* aWay);
double distanceM() const;
const FGPositionedRef& other(const FGPositionedRef& aEnd) const;
const FGPositionedRef origin;
const FGPositionedRef destination;
const Airway* airway;
private:
void validate() const;
mutable double _distanceM;
};
class AStarOpenNode : public SGReferenced class AStarOpenNode : public SGReferenced
{ {
public: public:
AStarOpenNode(FGPositionedRef aNode, double aLegDist, AStarOpenNode(FGPositionedRef aNode, double aLegDist,
Airway* aAirway, int aAirway,
FGPositionedRef aDest, AStarOpenNode* aPrev) : FGPositionedRef aDest, AStarOpenNode* aPrev) :
node(aNode), node(aNode),
airway(aAirway), airway(aAirway),
@ -98,7 +74,7 @@ public:
} }
FGPositionedRef node; FGPositionedRef node;
Airway* airway; int airway;
SGSharedPtr<AStarOpenNode> previous; SGSharedPtr<AStarOpenNode> previous;
double distanceFromStart; // aka 'g(x)' double distanceFromStart; // aka 'g(x)'
double directDistanceToDestination; // aka 'h(x)' double directDistanceToDestination; // aka 'h(x)'
@ -117,11 +93,24 @@ typedef SGSharedPtr<AStarOpenNode> AStarOpenNodeRef;
Airway::Network* Airway::lowLevel() Airway::Network* Airway::lowLevel()
{ {
static Network* static_lowLevel = NULL;
if (!static_lowLevel) {
static_lowLevel = new Network;
static_lowLevel->_networkID = 1;
}
return static_lowLevel; return static_lowLevel;
} }
Airway::Network* Airway::highLevel() Airway::Network* Airway::highLevel()
{ {
static Network* static_highLevel = NULL;
if (!static_highLevel) {
static_highLevel = new Network;
static_highLevel->_networkID = 2;
}
return static_highLevel; return static_highLevel;
} }
@ -132,14 +121,8 @@ Airway::Airway(const std::string& aIdent, double aTop, double aBottom) :
{ {
} }
void Airway::load() void Airway::load(const SGPath& path)
{ {
static_lowLevel = new Network;
static_highLevel = new Network;
SGPath path( globals->get_fg_root() );
path.append( "Navaids/awy.dat" );
std::string identStart, identEnd, name; std::string identStart, identEnd, name;
double latStart, lonStart, latEnd, lonEnd; double latStart, lonStart, latEnd, lonEnd;
int type, base, top; int type, base, top;
@ -168,28 +151,22 @@ void Airway::load()
// type = 1; low-altitude // type = 1; low-altitude
// type = 2; high-altitude // type = 2; high-altitude
Network* net = (type == 1) ? static_lowLevel : static_highLevel; Network* net = (type == 1) ? lowLevel() : highLevel();
SGGeod startPos(SGGeod::fromDeg(lonStart, latStart)), SGGeod startPos(SGGeod::fromDeg(lonStart, latStart)),
endPos(SGGeod::fromDeg(lonEnd, latEnd)); endPos(SGGeod::fromDeg(lonEnd, latEnd));
Airway* awy = net->findAirway(name, top, base); int awy = net->findAirway(name, top, base);
net->addEdge(awy, startPos, identStart, endPos, identEnd); net->addEdge(awy, startPos, identStart, endPos, identEnd);
} // of file line iteration } // of file line iteration
} }
Airway* Airway::Network::findAirway(const std::string& aName, double aTop, double aBase) int Airway::Network::findAirway(const std::string& aName, double aTop, double aBase)
{ {
AirwayDict::iterator it = _airways.find(aName); return NavDataCache::instance()->findAirway(_networkID, aName);
if (it == _airways.end()) {
Airway* awy = new Airway(aName, aTop, aBase);
it = _airways.insert(it, make_pair(aName, awy));
}
return it->second;
} }
void Airway::Network::addEdge(Airway* aWay, const SGGeod& aStartPos, void Airway::Network::addEdge(int aWay, const SGGeod& aStartPos,
const std::string& aStartIdent, const std::string& aStartIdent,
const SGGeod& aEndPos, const std::string& aEndIdent) const SGGeod& aEndPos, const std::string& aEndIdent)
{ {
@ -208,17 +185,21 @@ void Airway::Network::addEdge(Airway* aWay, const SGGeod& aStartPos,
return; return;
} }
AdjacentWaypoint* edge = new AdjacentWaypoint(start, end, aWay); NavDataCache::instance()->insertEdge(_networkID, aWay, start->guid(), end->guid());
_graph.insert(make_pair(start, edge));
_graph.insert(make_pair(end, edge));
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
bool Airway::Network::inNetwork(const FGPositioned* aPos) const bool Airway::Network::inNetwork(PositionedID posID) const
{ {
FGPositioned* pos = const_cast<FGPositioned*>(aPos); NetworkMembershipDict::iterator it = _inNetworkCache.find(posID);
return (_graph.find(pos) != _graph.end()); if (it != _inNetworkCache.end()) {
return it->second; // cached, easy
}
bool r = NavDataCache::instance()->isInAirwayNetwork(_networkID, posID);
_inNetworkCache.insert(it, std::make_pair(posID, r));
return r;
} }
bool Airway::Network::route(WayptRef aFrom, WayptRef aTo, bool Airway::Network::route(WayptRef aFrom, WayptRef aTo,
@ -278,7 +259,7 @@ public:
virtual bool pass(FGPositioned* aPos) const virtual bool pass(FGPositioned* aPos) const
{ {
return _net->inNetwork(aPos); return _net->inNetwork(aPos->guid());
} }
virtual FGPositioned::Type minType() const virtual FGPositioned::Type minType() const
@ -319,7 +300,7 @@ static void buildWaypoints(AStarOpenNodeRef aNode, WayptVec& aRoute)
// run over the route, creating waypoints // run over the route, creating waypoints
for (n = aNode; n; n=n->previous) { for (n = aNode; n; n=n->previous) {
aRoute[--count] = new NavaidWaypoint(n->node, n->airway); aRoute[--count] = new NavaidWaypoint(n->node, NULL);
} }
} }
@ -349,16 +330,14 @@ public:
bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest, bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest,
WayptVec& aRoute) WayptVec& aRoute)
{ {
typedef set<PositionedID> ClosedNodeSet;
typedef set<FGPositioned*> ClosedNodeSet;
typedef std::pair<AdjacencyMap::iterator,AdjacencyMap::iterator> AdjacencyMapRange;
OpenNodeHeap openNodes; OpenNodeHeap openNodes;
ClosedNodeSet closedNodes; ClosedNodeSet closedNodes;
HeapOrder ordering; HeapOrder ordering;
openNodes.push_back(new AStarOpenNode(aStart, 0.0, NULL, aDest, NULL)); openNodes.push_back(new AStarOpenNode(aStart, 0.0, 0, aDest, NULL));
// A* open node iteration // A* open node iteration
while (!openNodes.empty()) { while (!openNodes.empty()) {
@ -366,9 +345,11 @@ bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest,
AStarOpenNodeRef x = openNodes.back(); AStarOpenNodeRef x = openNodes.back();
FGPositioned* xp = x->node; FGPositioned* xp = x->node;
openNodes.pop_back(); openNodes.pop_back();
closedNodes.insert(xp); closedNodes.insert(xp->guid());
// SG_LOG(SG_GENERAL, SG_INFO, "x:" << xp->ident() << ", f(x)=" << x->totalCost()); #ifdef DEBUG_AWY_SEARCH
SG_LOG(SG_GENERAL, SG_INFO, "x:" << xp->ident() << ", f(x)=" << x->totalCost());
#endif
// check if xp is the goal; if so we're done, since there cannot be an open // check if xp is the goal; if so we're done, since there cannot be an open
// node with lower f(x) value. // node with lower f(x) value.
@ -378,35 +359,40 @@ bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest,
} }
// adjacent (neighbour) iteration // adjacent (neighbour) iteration
AdjacencyMapRange r(_graph.equal_range(xp)); NavDataCache* cache = NavDataCache::instance();
for (; r.first != r.second; ++r.first) { BOOST_FOREACH(AirwayEdge other, cache->airwayEdgesFrom(_networkID, xp->guid())) {
AdjacentWaypoint* adj(r.first->second); if (closedNodes.count(other.second)) {
FGPositioned* yp = adj->other(xp);
if (closedNodes.count(yp)) {
continue; // closed, ignore continue; // closed, ignore
} }
FGPositioned* yp = cache->loadById(other.second);
double edgeDistanceM = SGGeodesy::distanceM(xp->geod(), yp->geod());
AStarOpenNodeRef y = findInOpen(openNodes, yp); AStarOpenNodeRef y = findInOpen(openNodes, yp);
if (y) { // already open if (y) { // already open
double g = x->distanceFromStart + adj->distanceM(); double g = x->distanceFromStart + edgeDistanceM;
if (g > y->distanceFromStart) { if (g > y->distanceFromStart) {
// worse path, ignore // worse path, ignore
//SG_LOG(SG_GENERAL, SG_INFO, "\tabandoning " << yp->ident() << #ifdef DEBUG_AWY_SEARCH
// " path is worse: g(y)" << y->distanceFromStart << ", g'=" << g); SG_LOG(SG_GENERAL, SG_INFO, "\tabandoning " << yp->ident() <<
" path is worse: g(y)" << y->distanceFromStart << ", g'=" << g);
#endif
continue; continue;
} }
// we need to update y. Unfortunately this means rebuilding the heap, // we need to update y. Unfortunately this means rebuilding the heap,
// since y's score can change arbitrarily // since y's score can change arbitrarily
//SG_LOG(SG_GENERAL, SG_INFO, "\tfixing up previous for new path to " << yp->ident() << ", d =" << g); #ifdef DEBUG_AWY_SEARCH
SG_LOG(SG_GENERAL, SG_INFO, "\tfixing up previous for new path to " << yp->ident() << ", d =" << g);
#endif
y->previous = x; y->previous = x;
y->distanceFromStart = g; y->distanceFromStart = g;
y->airway = (Airway*) adj->airway; y->airway = other.first;
std::make_heap(openNodes.begin(), openNodes.end(), ordering); std::make_heap(openNodes.begin(), openNodes.end(), ordering);
} else { // not open, insert a new node for y into the heap } else { // not open, insert a new node for y into the heap
y = new AStarOpenNode(yp, adj->distanceM(), y = new AStarOpenNode(yp, edgeDistanceM, other.first, aDest, x);
(Airway*) adj->airway, aDest, x); #ifdef DEBUG_AWY_SEARCH
//SG_LOG(SG_GENERAL, SG_INFO, "\ty=" << yp->ident() << ", f(y)=" << y->totalCost()); SG_LOG(SG_GENERAL, SG_INFO, "\ty=" << yp->ident() << ", f(y)=" << y->totalCost());
#endif
openNodes.push_back(y); openNodes.push_back(y);
std::push_heap(openNodes.begin(), openNodes.end(), ordering); std::push_heap(openNodes.begin(), openNodes.end(), ordering);
} }
@ -417,38 +403,4 @@ bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest,
return false; return false;
} }
//////////////////////////////////////////////////////////////////////////////
// AdjacentWaypoint definitions
AdjacentWaypoint::AdjacentWaypoint(
const FGPositionedRef& aOrigin, const FGPositionedRef& aDest, Airway* aWay) :
origin(aOrigin),
destination(aDest),
airway(aWay),
_distanceM(-1.0)
{
}
const FGPositionedRef&
AdjacentWaypoint::other(const FGPositionedRef& aEnd) const
{
return (aEnd == origin) ? destination : origin;
}
double AdjacentWaypoint::distanceM() const
{
validate();
return _distanceM;
}
void AdjacentWaypoint::validate() const
{
if (_distanceM > 0.0) {
return; // already validated
}
_distanceM = SGGeodesy::distanceM(origin->geod(), destination->geod());
}
} // of namespace flightgear } // of namespace flightgear

View file

@ -24,8 +24,9 @@
#include <vector> #include <vector>
#include <Navaids/route.hxx> #include <Navaids/route.hxx>
#include <Navaids/positioned.hxx>
class FGPositioned; class SGPath;
typedef SGSharedPtr<FGPositioned> FGPositionedRef; typedef SGSharedPtr<FGPositioned> FGPositionedRef;
namespace flightgear { namespace flightgear {
@ -41,7 +42,7 @@ public:
virtual std::string ident() const virtual std::string ident() const
{ return _ident; } { return _ident; }
static void load(); static void load(const SGPath& path);
/** /**
* Track a network of airways * Track a network of airways
@ -53,7 +54,6 @@ public:
friend class Airway; friend class Airway;
friend class InAirwayFilter; friend class InAirwayFilter;
/** /**
* Principal routing algorithm. Attempts to find the best route beween * Principal routing algorithm. Attempts to find the best route beween
@ -65,26 +65,18 @@ public:
*/ */
bool route(WayptRef aFrom, WayptRef aTo, WayptVec& aPath); bool route(WayptRef aFrom, WayptRef aTo, WayptVec& aPath);
private: private:
void addEdge(Airway* aWay, const SGGeod& aStartPos, void addEdge(int aWay, const SGGeod& aStartPos,
const std::string& aStartIdent, const std::string& aStartIdent,
const SGGeod& aEndPos, const std::string& aEndIdent); const SGGeod& aEndPos, const std::string& aEndIdent);
Airway* findAirway(const std::string& aName, double aTop, double aBase); int findAirway(const std::string& aName, double aTop, double aBase);
typedef std::multimap<FGPositioned*, AdjacentWaypoint*> AdjacencyMap;
AdjacencyMap _graph;
typedef std::vector<AdjacentWaypoint*> AdjacentWaypointVec;
typedef std::map<std::string, Airway*> AirwayDict;
AirwayDict _airways;
bool search2(FGPositionedRef aStart, FGPositionedRef aDest, WayptVec& aRoute); bool search2(FGPositionedRef aStart, FGPositionedRef aDest, WayptVec& aRoute);
/** /**
* Test if a positioned item is part of this airway network or not. * Test if a positioned item is part of this airway network or not.
*/ */
bool inNetwork(const FGPositioned* aRef) const; bool inNetwork(PositionedID pos) const;
/** /**
* Find the closest node on the network, to the specified waypoint * Find the closest node on the network, to the specified waypoint
@ -103,7 +95,16 @@ public:
/** /**
* Overloaded version working with a raw SGGeod * Overloaded version working with a raw SGGeod
*/ */
std::pair<FGPositionedRef, bool> findClosestNode(const SGGeod& aGeod); std::pair<FGPositionedRef, bool> findClosestNode(const SGGeod& aGeod);
/**
* cache which positioned items are in this network
*/
typedef std::map<PositionedID, bool> NetworkMembershipDict;
mutable NetworkMembershipDict _inNetworkCache;
int _networkID;
}; };
@ -115,15 +116,9 @@ private:
friend class Network; friend class Network;
static Network* static_highLevel;
static Network* static_lowLevel;
std::string _ident; std::string _ident;
double _topAltitudeFt; double _topAltitudeFt;
double _bottomAltitudeFt; double _bottomAltitudeFt;
// high-level vs low-level flag
// ... ?
WayptVec _elements; WayptVec _elements;
}; };

View file

@ -33,7 +33,7 @@
class FGFix : public FGPositioned class FGFix : public FGPositioned
{ {
public: public:
FGFix(const std::string& aIdent, const SGGeod& aPos); FGFix(PositionedID aGuid, const std::string& aIdent, const SGGeod& aPos);
inline ~FGFix(void) {} inline ~FGFix(void) {}
inline const std::string& get_ident() const { return ident(); } inline const std::string& get_ident() const { return ident(); }

View file

@ -34,47 +34,42 @@
#include "fixlist.hxx" #include "fixlist.hxx"
#include <Navaids/fix.hxx> #include <Navaids/fix.hxx>
#include <Airports/simple.hxx> #include <Navaids/NavDataCache.hxx>
FGFix::FGFix(const std::string& aIdent, const SGGeod& aPos) : FGFix::FGFix(PositionedID aGuid, const std::string& aIdent, const SGGeod& aPos) :
FGPositioned(FIX, aIdent, aPos) FGPositioned(aGuid, FIX, aIdent, aPos)
{ {
init(true); // init FGPositioned
}
// Constructor
FGFixList::FGFixList( void ) {
} }
// Destructor namespace flightgear
FGFixList::~FGFixList( void ) { {
}
void loadFixes(const SGPath& path)
{
// load the navaids and build the map sg_gzifstream in( path.str() );
bool FGFixList::init(const SGPath& path ) { if ( !in.is_open() ) {
sg_gzifstream in( path.str() ); SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
if ( !in.is_open() ) { exit(-1);
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() ); }
exit(-1);
} // toss the first two lines of the file
in >> skipeol;
// toss the first two lines of the file in >> skipeol;
in >> skipeol;
in >> skipeol; NavDataCache* cache = NavDataCache::instance();
// read in each remaining line of the file // read in each remaining line of the file
while ( ! in.eof() ) { while ( ! in.eof() ) {
double lat, lon; double lat, lon;
std::string ident; std::string ident;
in >> lat >> lon >> ident; in >> lat >> lon >> ident;
if (lat > 95) break; if (lat > 95) break;
// fix gets added to the FGPositioned spatial indices, so we don't need cache->insertFix(ident, SGGeod::fromDeg(lon, lat));
// to hold onto it here. in >> skipcomment;
new FGFix(ident, SGGeod::fromDeg(lon, lat)); }
in >> skipcomment;
}
return true;
} }
} // of namespace flightgear;

View file

@ -27,18 +27,13 @@
#include <simgear/compiler.h> #include <simgear/compiler.h>
class FGFix;
class SGPath; class SGPath;
class FGFixList { namespace flightgear
public: {
FGFixList(); void loadFixes(const SGPath& path);
~FGFixList();
}
// load the navaids and build the map
bool init(const SGPath& path);
};
#endif // _FG_FIXLIST_HXX #endif // _FG_FIXLIST_HXX

View file

@ -24,51 +24,13 @@
#include "config.h" #include "config.h"
#endif #endif
#include <simgear/structure/exception.hxx>
#include <simgear/debug/logstream.hxx>
#include <Navaids/markerbeacon.hxx> #include <Navaids/markerbeacon.hxx>
#include <Airports/runways.hxx>
#include <Navaids/navdb.hxx>
using std::string; using std::string;
FGPositioned::Type FGMarkerBeaconRecord::FGMarkerBeaconRecord(PositionedID aGuid, Type aTy,
FGMarkerBeaconRecord::mapType(int aTy) PositionedID aRunway, const SGGeod& aPos) :
{ FGPositioned(aGuid, aTy, string(), aPos),
switch (aTy) {
case 7: return FGPositioned::OM;
case 8: return FGPositioned::MM;
case 9: return FGPositioned::IM;
default:
throw sg_range_exception("Got a non-marker-beacon-type",
"FGMarkerBeaconRecord::mapType");
}
}
FGMarkerBeaconRecord*
FGMarkerBeaconRecord::create(int aTy, const string& aName, const SGGeod& aPos)
{
Type fgpTy = mapType(aTy);
FGRunway* runway = getRunwayFromName(aName);
if (!runway)
{
SG_LOG(SG_GENERAL, SG_WARN, "Failed to create beacon for unknown runway '" << aName << "'.");
return NULL;
}
SGGeod pos(aPos);
// fudge elevation to the runway elevation if it's not specified
if (fabs(pos.getElevationFt()) < 0.01) {
pos.setElevationFt(runway->elevation());
}
return new FGMarkerBeaconRecord(fgpTy, runway, pos);
}
FGMarkerBeaconRecord::FGMarkerBeaconRecord(Type aTy, FGRunway* aRunway, const SGGeod& aPos) :
FGPositioned(aTy, string(), aPos),
_runway(aRunway) _runway(aRunway)
{ {
init(true); // init FGPositioned
} }

View file

@ -28,23 +28,15 @@
#include "positioned.hxx" #include "positioned.hxx"
// forward decls
class FGRunway;
class FGMarkerBeaconRecord : public FGPositioned class FGMarkerBeaconRecord : public FGPositioned
{ {
public: public:
static FGMarkerBeaconRecord* create(int aTy, const std::string& aName, const SGGeod& aPos);
FGMarkerBeaconRecord(PositionedID aGuid, Type aTy, PositionedID aRunway, const SGGeod& aPos);
private: private:
FGMarkerBeaconRecord(Type aTy, FGRunway* aRunway, const SGGeod& aPos);
FGRunway* _runway; // should this be ref-ptr? PositionedID _runway;
/**
* Helper to map a 'Robin' integer type to an FGPositioned::Type
*/
static Type mapType(int aTy);
}; };
#endif // _FG_MARKERBEACON_HXX #endif // _FG_MARKERBEACON_HXX

View file

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

View file

@ -26,16 +26,23 @@
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <string>
class FGNavList; // forward decls
class FGTACANList; class FGTACANList;
class SGPath;
class SGPropertyNode;
class FGRunway;
namespace flightgear
{
// load and initialize the navigational databases // load and initialize the navigational databases
bool fgNavDBInit( FGNavList *navlist, FGNavList *loclist, FGNavList *gslist, bool navDBInit(const SGPath& path);
FGNavList *dmelist,
FGNavList *tacanlist, FGNavList *carrierlist, bool loadCarrierNav(const SGPath& path);
FGTACANList *channellist );
bool loadTacan(const SGPath& path, FGTACANList *channellist);
/** /**
* Return the property node corresponding to the runway ILS installation, * Return the property node corresponding to the runway ILS installation,
@ -44,10 +51,6 @@ bool fgNavDBInit( FGNavList *navlist, FGNavList *loclist, FGNavList *gslist,
*/ */
SGPropertyNode* ilsDataForRunwayAndNavaid(FGRunway* aRunway, const std::string& aNavIdent); SGPropertyNode* ilsDataForRunwayAndNavaid(FGRunway* aRunway, const std::string& aNavIdent);
/** } // of namespace flightgear
* Helper to map a nav.data name (eg 'KBWI 33R GS') into a FGRunway reference.
* returns NULL, and complains loudly, if the airport/runway is not found.
*/
FGRunway* getRunwayFromName(const std::string& aName);
#endif // _FG_NAVDB_HXX #endif // _FG_NAVDB_HXX

View file

@ -25,6 +25,7 @@
# include <config.h> # include <config.h>
#endif #endif
#include <cassert>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <algorithm> #include <algorithm>
@ -35,6 +36,7 @@
#include "navlist.hxx" #include "navlist.hxx"
#include <Airports/runways.hxx> #include <Airports/runways.hxx>
#include <Navaids/NavDataCache.hxx>
using std::string; using std::string;
@ -56,6 +58,34 @@ private:
}; };
// discount navids if they conflict with another on the same frequency
// this only applies to navids associated with opposite ends of a runway,
// with matching frequencies.
bool navidUsable(FGNavRecord* aNav, const SGGeod &aircraft)
{
FGRunway* r(aNav->runway());
if (!r || !r->reciprocalRunway()) {
return true;
}
// check if the runway frequency is paired
FGNavRecord* locA = r->ILS();
FGNavRecord* locB = r->reciprocalRunway()->ILS();
if (!locA || !locB || (locA->get_freq() != locB->get_freq())) {
return true; // not paired, ok
}
// okay, both ends have an ILS, and they're paired. We need to select based on
// aircraft position. What we're going to use is *runway* (not navid) position,
// ie whichever runway end we are closer too. This makes back-course / missed
// approach behaviour incorrect, but that's the price we accept.
double crs = SGGeodesy::courseDeg(aircraft, r->geod());
double hdgDiff = crs - r->headingDeg();
SG_NORMALIZE_RANGE(hdgDiff, -180.0, 180.0);
return (fabs(hdgDiff) < 90.0);
}
} // of anonymous namespace } // of anonymous namespace
// FGNavList ------------------------------------------------------------------ // FGNavList ------------------------------------------------------------------
@ -64,7 +94,7 @@ private:
FGNavList::TypeFilter::TypeFilter(const FGPositioned::Type type) FGNavList::TypeFilter::TypeFilter(const FGPositioned::Type type)
{ {
if (type == FGPositioned::INVALID) { if (type == FGPositioned::INVALID) {
_mintype = FGPositioned::VOR; _mintype = FGPositioned::NDB;
_maxtype = FGPositioned::GS; _maxtype = FGPositioned::GS;
} else { } else {
_mintype = _maxtype = type; _mintype = _maxtype = type;
@ -72,77 +102,142 @@ FGNavList::TypeFilter::TypeFilter(const FGPositioned::Type type)
} }
FGNavList::FGNavList( void ) FGNavList::TypeFilter::TypeFilter(const FGPositioned::Type minType,
const FGPositioned::Type maxType) :
_mintype(minType),
_maxtype(maxType)
{ {
} }
/**
FGNavList::~FGNavList( void ) * Filter returning Tacan stations. Checks for both pure TACAN stations
* but also co-located VORTACs. This is done by searching for DMEs whose
* name indicates they are a TACAN or VORTAC; not a great solution.
*/
class TacanFilter : public FGNavList::TypeFilter
{ {
nav_list_type navlist = navaids.begin()->second; public:
navaids.erase( navaids.begin(), navaids.end() ); TacanFilter() :
} TypeFilter(FGPositioned::DME, FGPositioned::TACAN)
{
}
// load the navaids and build the map
bool FGNavList::init() virtual bool pass(FGPositioned* pos) const
{ {
// No need to delete the original navaid structures if (pos->type() == FGPositioned::TACAN) {
// since we're using an SGSharedPointer return true;
nav_list_type navlist = navaids.begin()->second; }
navaids.erase( navaids.begin(), navaids.end() );
return true; assert(pos->type() == FGPositioned::DME);
} string::size_type loc1 = pos->name().find( "TACAN" );
string::size_type loc2 = pos->name().find( "VORTAC" );
// add an entry to the lists return (loc1 != string::npos) || (loc2 != string::npos);
bool FGNavList::add( FGNavRecord *n ) }
{ };
navaids[n->get_freq()].push_back(n);
return true; FGNavList::TypeFilter* FGNavList::locFilter()
} {
static TypeFilter tf(FGPositioned::ILS, FGPositioned::LOC);
FGNavRecord *FGNavList::findByFreq( double freq, const SGGeod& position) return &tf;
{ }
const nav_list_type& stations = navaids[(int)(freq*100.0 + 0.5)];
SG_LOG( SG_INSTR, SG_DEBUG, "findbyFreq " << freq << " size " << stations.size() ); FGNavList::TypeFilter* FGNavList::ndbFilter()
return findNavFromList( position, stations ); {
} static TypeFilter tf(FGPositioned::NDB);
return &tf;
nav_list_type FGNavList::findAllByFreq( double freq, const SGGeod& position, const FGPositioned::Type type) }
{
nav_list_type stations; FGNavList::TypeFilter* FGNavList::navFilter()
TypeFilter filter(type); {
static TypeFilter tf(FGPositioned::VOR, FGPositioned::LOC);
BOOST_FOREACH(nav_rec_ptr nav, navaids[(int)(freq*100.0 + 0.5)]) { return &tf;
if (filter.pass(nav.ptr())) { }
stations.push_back(nav);
} FGNavList::TypeFilter* FGNavList::tacanFilter()
{
static TacanFilter tf;
return &tf;
}
FGNavList::TypeFilter* FGNavList::carrierFilter()
{
static TypeFilter tf(FGPositioned::MOBILE_TACAN);
return &tf;
}
FGNavRecord *FGNavList::findByFreq( double freq, const SGGeod& position,
TypeFilter* filter)
{
flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
int freqKhz = static_cast<int>(freq * 100);
PositionedIDVec stations(cache->findNavaidsByFreq(freqKhz, position, filter));
if (stations.empty()) {
return NULL;
}
// now walk the (sorted) results list to find a usable, in-range navaid
SGVec3d acCart(SGVec3d::fromGeod(position));
double min_dist
= FG_NAV_MAX_RANGE*SG_NM_TO_METER*FG_NAV_MAX_RANGE*SG_NM_TO_METER;
BOOST_FOREACH(PositionedID id, stations) {
FGNavRecord* station = (FGNavRecord*) cache->loadById(id);
double d2 = distSqr(station->cart(), acCart);
if (d2 > min_dist) {
// since results are sorted by proximity, as soon as we pass the
// distance cutoff we're done - fall out and return NULL
break;
}
if (navidUsable(station, position)) {
return station;
}
}
// fell out of the loop, no usable match
return NULL;
}
FGNavRecord *FGNavList::findByFreq( double freq, TypeFilter* filter)
{
flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
int freqKhz = static_cast<int>(freq * 1000);
PositionedIDVec stations(cache->findNavaidsByFreq(freqKhz, filter));
if (stations.empty()) {
return NULL;
}
return (FGNavRecord*) cache->loadById(stations.front());
}
nav_list_type FGNavList::findAllByFreq( double freq, const SGGeod& position,
TypeFilter* filter)
{
nav_list_type stations;
flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
int freqKhz = static_cast<int>(freq * 1000);
PositionedIDVec ids(cache->findNavaidsByFreq(freqKhz, position, filter));
BOOST_FOREACH(PositionedID id, ids) {
stations.push_back((FGNavRecord*) cache->loadById(id));
} }
NavRecordDistanceSortPredicate sortPredicate( position );
std::sort( stations.begin(), stations.end(), sortPredicate );
return stations; return stations;
} }
nav_list_type FGNavList::findByIdentAndFreq(const string& ident, const double freq,
// Given an Ident and optional frequency, return the first matching TypeFilter* filter)
// station.
const nav_list_type FGNavList::findByIdentAndFreq(const string& ident, const double freq, const FGPositioned::Type type )
{ {
FGPositionedRef cur;
TypeFilter filter(type);
nav_list_type reply; nav_list_type reply;
cur = FGPositioned::findNextWithPartialId(cur, ident, &filter);
int f = (int)(freq*100.0 + 0.5); int f = (int)(freq*100.0 + 0.5);
while (cur) {
FGNavRecord* nav = static_cast<FGNavRecord*>(cur.ptr()); FGPositioned::List stations = FGPositioned::findAllWithIdent(ident, filter);
BOOST_FOREACH(FGPositionedRef ref, stations) {
FGNavRecord* nav = static_cast<FGNavRecord*>(ref.ptr());
if ( f <= 0.0 || nav->get_freq() == f) { if ( f <= 0.0 || nav->get_freq() == f) {
reply.push_back( nav ); reply.push_back( nav );
} }
cur = FGPositioned::findNextWithPartialId(cur, ident, &filter);
} }
return reply; return reply;
@ -150,87 +245,17 @@ const nav_list_type FGNavList::findByIdentAndFreq(const string& ident, const dou
// Given an Ident and optional frequency and type , // Given an Ident and optional frequency and type ,
// return a list of matching stations sorted by distance to the given position // return a list of matching stations sorted by distance to the given position
const nav_list_type FGNavList::findByIdentAndFreq( const SGGeod & position, nav_list_type FGNavList::findByIdentAndFreq( const SGGeod & position,
const std::string& ident, const double freq, const FGPositioned::Type type ) const std::string& ident, const double freq,
TypeFilter* filter)
{ {
nav_list_type reply = findByIdentAndFreq( ident, freq, type ); nav_list_type reply = findByIdentAndFreq( ident, freq, filter );
NavRecordDistanceSortPredicate sortPredicate( position ); NavRecordDistanceSortPredicate sortPredicate( position );
std::sort( reply.begin(), reply.end(), sortPredicate ); std::sort( reply.begin(), reply.end(), sortPredicate );
return reply; return reply;
} }
// discount navids if they conflict with another on the same frequency
// this only applies to navids associated with opposite ends of a runway,
// with matching frequencies.
static bool navidUsable(FGNavRecord* aNav, const SGGeod &aircraft)
{
FGRunway* r(aNav->runway());
if (!r || !r->reciprocalRunway()) {
return true;
}
// check if the runway frequency is paired
FGNavRecord* locA = r->ILS();
FGNavRecord* locB = r->reciprocalRunway()->ILS();
if (!locA || !locB || (locA->get_freq() != locB->get_freq())) {
return true; // not paired, ok
}
// okay, both ends have an ILS, and they're paired. We need to select based on
// aircraft position. What we're going to use is *runway* (not navid) position,
// ie whichever runway end we are closer too. This makes back-course / missed
// approach behaviour incorrect, but that's the price we accept.
double crs = SGGeodesy::courseDeg(aircraft, r->geod());
double hdgDiff = crs - r->headingDeg();
SG_NORMALIZE_RANGE(hdgDiff, -180.0, 180.0);
return (fabs(hdgDiff) < 90.0);
}
// Given a point and a list of stations, return the closest one to
// the specified point.
FGNavRecord* FGNavList::findNavFromList( const SGGeod &aircraft,
const nav_list_type &stations )
{
FGNavRecord *nav = NULL;
double d2; // in meters squared
double min_dist
= FG_NAV_MAX_RANGE*SG_NM_TO_METER*FG_NAV_MAX_RANGE*SG_NM_TO_METER;
SGVec3d aircraftCart = SGVec3d::fromGeod(aircraft);
nav_list_const_iterator it;
nav_list_const_iterator end = stations.end();
// find the closest station within a sensible range (FG_NAV_MAX_RANGE)
for ( it = stations.begin(); it != end; ++it ) {
FGNavRecord *station = *it;
d2 = distSqr(station->cart(), aircraftCart);
if ( d2 > min_dist || !navidUsable(station, aircraft)) {
continue;
}
min_dist = d2;
nav = station;
}
return nav;
}
// Given a frequency, return the first matching station.
FGNavRecord *FGNavList::findStationByFreq( double freq )
{
const nav_list_type& stations = navaids[(int)(freq*100.0 + 0.5)];
SG_LOG( SG_INSTR, SG_DEBUG, "findStationByFreq " << freq << " size " << stations.size() );
if (!stations.empty()) {
return stations[0];
}
return NULL;
}
// FGTACANList ---------------------------------------------------------------- // FGTACANList ----------------------------------------------------------------
FGTACANList::FGTACANList( void ) FGTACANList::FGTACANList( void )

View file

@ -42,62 +42,18 @@ class SGGeod;
typedef SGSharedPtr<FGNavRecord> nav_rec_ptr; typedef SGSharedPtr<FGNavRecord> nav_rec_ptr;
typedef std::vector < nav_rec_ptr > nav_list_type; typedef std::vector < nav_rec_ptr > nav_list_type;
typedef nav_list_type::iterator nav_list_iterator;
typedef nav_list_type::const_iterator nav_list_const_iterator;
typedef std::map < int, nav_list_type > nav_map_type;
typedef nav_map_type::iterator nav_map_iterator;
typedef nav_map_type::const_iterator nav_map_const_iterator;
class FGNavList {
nav_list_type carrierlist;
nav_map_type navaids;
// Given a point and a list of stations, return the closest one to
// the specified point.
FGNavRecord *findNavFromList( const SGGeod &aircraft,
const nav_list_type &stations );
class FGNavList
{
public: public:
FGNavList();
~FGNavList();
// initialize the nav list
bool init();
// add an entry
bool add( FGNavRecord *n );
/** Query the database for the specified station. It is assumed
* that there will be multiple stations with matching frequencies
* so a position must be specified.
*/
FGNavRecord *findByFreq( double freq, const SGGeod& position);
nav_list_type findAllByFreq( double freq, const SGGeod& position,
const FGPositioned::Type type = FGPositioned::INVALID);
// Given an Ident and optional frequency and type ,
// return a list of matching stations.
const nav_list_type findByIdentAndFreq( const std::string& ident,
const double freq = 0.0, const FGPositioned::Type = FGPositioned::INVALID );
// Given an Ident and optional frequency and type ,
// return a list of matching stations sorted by distance to the given position
const nav_list_type findByIdentAndFreq( const SGGeod & position,
const std::string& ident, const double freq = 0.0,
const FGPositioned::Type = FGPositioned::INVALID );
// given a frequency returns the first matching entry
FGNavRecord *findStationByFreq( double frequency );
class TypeFilter : public FGPositioned::Filter class TypeFilter : public FGPositioned::Filter
{ {
public: public:
TypeFilter(const FGPositioned::Type type); TypeFilter(const FGPositioned::Type type);
TypeFilter(const FGPositioned::Type minType,
const FGPositioned::Type maxType);
virtual FGPositioned::Type minType() const { virtual FGPositioned::Type minType() const {
return _mintype; return _mintype;
} }
@ -105,13 +61,62 @@ public:
virtual FGPositioned::Type maxType() const { virtual FGPositioned::Type maxType() const {
return _maxtype; return _maxtype;
} }
private:
protected:
FGPositioned::Type _mintype; FGPositioned::Type _mintype;
FGPositioned::Type _maxtype; FGPositioned::Type _maxtype;
}; };
};
/**
filter matching VOR & ILS/LOC transmitters
*/
static TypeFilter* navFilter();
/**
* filter matching ILS/LOC transmitter
*/
static TypeFilter* locFilter();
static TypeFilter* ndbFilter();
/**
* Filter returning TACANs and VORTACs
*/
static TypeFilter* tacanFilter();
static TypeFilter* carrierFilter();
/** Query the database for the specified station. It is assumed
* that there will be multiple stations with matching frequencies
* so a position must be specified.
*/
static FGNavRecord *findByFreq( double freq, const SGGeod& position,
TypeFilter* filter = NULL);
/**
* Overloaded version above - no positioned supplied so can be used with
* mobile TACANs which have no valid position. The first match is
* returned only.
*/
static FGNavRecord *findByFreq( double freq, TypeFilter* filter = NULL);
static nav_list_type findAllByFreq( double freq, const SGGeod& position,
TypeFilter* filter = NULL);
// Given an Ident and optional frequency and type ,
// return a list of matching stations.
static nav_list_type findByIdentAndFreq( const std::string& ident,
const double freq,
TypeFilter* filter = NULL);
// Given an Ident and optional frequency and type ,
// return a list of matching stations sorted by distance to the given position
static nav_list_type findByIdentAndFreq( const SGGeod & position,
const std::string& ident, const double freq = 0.0,
TypeFilter* filter = NULL);
};
// FGTACANList ---------------------------------------------------------------- // FGTACANList ----------------------------------------------------------------

View file

@ -40,117 +40,24 @@
#include <Airports/simple.hxx> #include <Airports/simple.hxx>
#include <Airports/xmlloader.hxx> #include <Airports/xmlloader.hxx>
#include <Main/fg_props.hxx> #include <Main/fg_props.hxx>
#include <Navaids/NavDataCache.hxx>
FGNavRecord::FGNavRecord(Type aTy, const std::string& aIdent, FGNavRecord::FGNavRecord(PositionedID aGuid, Type aTy, const std::string& aIdent,
const std::string& aName, const SGGeod& aPos, const std::string& aName, const SGGeod& aPos,
int aFreq, int aRange, double aMultiuse) : int aFreq, int aRange, double aMultiuse, PositionedID aRunway) :
FGPositioned(aTy, aIdent, aPos), FGPositioned(aGuid, aTy, aIdent, aPos),
freq(aFreq), freq(aFreq),
range(aRange), range(aRange),
multiuse(aMultiuse), multiuse(aMultiuse),
_name(aName), mName(aName),
mRunway(NULL), mRunway(aRunway),
serviceable(true) serviceable(true)
{ {
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_DEBUG, "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;
}
}
init(true); // init FGPositioned (now position is adjusted)
} }
void FGNavRecord::initAirportRelation() FGRunway* FGNavRecord::runway() const
{ {
if ((type() < ILS) || (type() > GS)) { return (FGRunway*) flightgear::NavDataCache::instance()->loadById(mRunway);
return; // not airport-located
}
mRunway = getRunwayFromName(_name);
if (!mRunway) {
return;
}
if (type() != GS) {
SGPropertyNode* ilsData = ilsDataForRunwayAndNavaid(mRunway, ident());
if (ilsData) {
processSceneryILS(ilsData);
}
}
// fudge elevation to the runway elevation if it's not specified
if (fabs(elevation()) < 0.01) {
mPosition.setElevationFt(mRunway->elevation());
}
if (type() == ILS || type() == LOC) {
mRunway->setILS(this);
}
// align localizers with their runway
if ((type() == ILS) || (type() == LOC)) {
if (!fgGetBool("/sim/navdb/localizers/auto-align", true)) {
return;
}
double threshold
= fgGetDouble( "/sim/navdb/localizers/auto-align-threshold-deg", 5.0 );
alignLocaliserWithRunway(threshold);
}
}
void FGNavRecord::processSceneryILS(SGPropertyNode* aILSNode)
{
double hdgDeg = aILSNode->getDoubleValue("hdg-deg"),
lon = aILSNode->getDoubleValue("lon"),
lat = aILSNode->getDoubleValue("lat"),
elevM = aILSNode->getDoubleValue("elev-m");
mPosition = SGGeod::fromDegM(lon, lat, elevM);
multiuse = hdgDeg;
}
void FGNavRecord::alignLocaliserWithRunway(double aThreshold)
{
// find the distance from the threshold to the localizer
double dist = SGGeodesy::distanceM(mPosition, mRunway->threshold());
// back project that distance along the runway center line
SGGeod newPos = mRunway->pointOnCenterline(dist);
double hdg_diff = get_multiuse() - mRunway->headingDeg();
SG_NORMALIZE_RANGE(hdg_diff, -180.0, 180.0);
if ( fabs(hdg_diff) <= aThreshold ) {
mPosition = SGGeod::fromGeodFt(newPos, mPosition.getElevationFt());
set_multiuse( mRunway->headingDeg() );
} else {
SG_LOG(SG_GENERAL, SG_DEBUG, "localizer:" << ident() << ", aligning with runway "
<< mRunway->ident() << " exceeded heading threshold");
}
} }
double FGNavRecord::localizerWidth() const double FGNavRecord::localizerWidth() const
@ -159,9 +66,10 @@ double FGNavRecord::localizerWidth() const
return 6.0; return 6.0;
} }
SGVec3d thresholdCart(SGVec3d::fromGeod(mRunway->threshold())); FGRunway* rwy = runway();
SGVec3d thresholdCart(SGVec3d::fromGeod(rwy->threshold()));
double axisLength = dist(cart(), thresholdCart); double axisLength = dist(cart(), thresholdCart);
double landingLength = dist(thresholdCart, SGVec3d::fromGeod(mRunway->end())); double landingLength = dist(thresholdCart, SGVec3d::fromGeod(rwy->end()));
// Reference: http://dcaa.slv.dk:8000/icaodocs/ // Reference: http://dcaa.slv.dk:8000/icaodocs/
// ICAO standard width at threshold is 210 m = 689 feet = approx 700 feet. // ICAO standard width at threshold is 210 m = 689 feet = approx 700 feet.

View file

@ -28,10 +28,10 @@
#include "positioned.hxx" #include "positioned.hxx"
#define FG_NAV_DEFAULT_RANGE 50 // nm const double FG_NAV_DEFAULT_RANGE = 50; // nm
#define FG_LOC_DEFAULT_RANGE 18 // nm const double FG_LOC_DEFAULT_RANGE = 18; // nm
#define FG_DME_DEFAULT_RANGE 50 // nm const double FG_DME_DEFAULT_RANGE = 50; // nm
#define FG_NAV_MAX_RANGE 300 // nm const double FG_NAV_MAX_RANGE = 300; // nm
// forward decls // forward decls
class FGRunway; class FGRunway;
@ -46,26 +46,18 @@ class FGNavRecord : public FGPositioned
// (degrees) or localizer heading // (degrees) or localizer heading
// (degrees) or dme bias (nm) // (degrees) or dme bias (nm)
std::string _name; // verbose name in nav database std::string mName; // verbose name in nav database
FGRunway* mRunway; // associated runway, if there is one PositionedID mRunway; // associated runway, if there is one
bool serviceable; // for failure modeling bool serviceable; // for failure modeling
/**
* Helper to init data when a navrecord is associated with an airport
*/
void initAirportRelation();
void alignLocaliserWithRunway(double aThreshold);
void readAirportSceneryData();
void processSceneryILS(SGPropertyNode* aILSNode); void processSceneryILS(SGPropertyNode* aILSNode);
public: public:
inline ~FGNavRecord(void) {} FGNavRecord(PositionedID aGuid, Type type, const std::string& ident,
const std::string& name,
FGNavRecord(Type type, const std::string& ident, const std::string& name, const SGGeod& aPos,
const SGGeod& aPos, int freq, int range, double multiuse,
int freq, int range, double multiuse); PositionedID aRunway);
inline double get_lon() const { return longitude(); } // degrees inline double get_lon() const { return longitude(); } // degrees
inline double get_lat() const { return latitude(); } // degrees inline double get_lat() const { return latitude(); } // degrees
@ -81,12 +73,12 @@ public:
inline const char *get_trans_ident() const { return get_ident(); } inline const char *get_trans_ident() const { return get_ident(); }
virtual const std::string& name() const virtual const std::string& name() const
{ return _name; } { return mName; }
/** /**
* Retrieve the runway this navaid is associated with (for ILS/LOC/GS) * Retrieve the runway this navaid is associated with (for ILS/LOC/GS)
*/ */
FGRunway* runway() const { return mRunway; } FGRunway* runway() const;
/** /**
* return the localizer width, in degrees * return the localizer width, in degrees

View file

@ -36,476 +36,15 @@
#include <osg/Math> // for osg::isNaN #include <osg/Math> // for osg::isNaN
#include <simgear/timing/timestamp.hxx> #include <simgear/timing/timestamp.hxx>
#include <simgear/props/props.hxx>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
#include <simgear/math/SGGeometry.hxx> #include <simgear/math/SGGeometry.hxx>
#include <simgear/sg_inlines.h> #include <simgear/sg_inlines.h>
#include <simgear/structure/commands.hxx>
#include "Airports/simple.hxx" #include "Navaids/PositionedOctree.hxx"
#include "Main/fg_props.hxx"
typedef std::multimap<std::string, FGPositioned*> NamedPositionedIndex; using std::string;
typedef std::pair<NamedPositionedIndex::const_iterator, NamedPositionedIndex::const_iterator> NamedIndexRange; using namespace flightgear;
using std::lower_bound;
using std::upper_bound;
static NamedPositionedIndex global_identIndex;
static NamedPositionedIndex global_nameIndex;
//////////////////////////////////////////////////////////////////////////////
namespace Octree
{
const double LEAF_SIZE = SG_NM_TO_METER * 8.0;
const double LEAF_SIZE_SQR = LEAF_SIZE * LEAF_SIZE;
/**
* Decorate an object with a double value, and use that value to order
* items, for the purpoises of the STL algorithms
*/
template <class T>
class Ordered
{
public:
Ordered(const T& v, double x) :
_order(x),
_inner(v)
{
}
Ordered(const Ordered<T>& a) :
_order(a._order),
_inner(a._inner)
{
}
Ordered<T>& operator=(const Ordered<T>& a)
{
_order = a._order;
_inner = a._inner;
return *this;
}
bool operator<(const Ordered<T>& other) const
{
return _order < other._order;
}
bool operator>(const Ordered<T>& other) const
{
return _order > other._order;
}
const T& get() const
{ return _inner; }
double order() const
{ return _order; }
private:
double _order;
T _inner;
};
class Node;
typedef Ordered<Node*> OrderedNode;
typedef std::greater<OrderedNode> FNPQCompare;
/**
* the priority queue is fundamental to our search algorithm. When searching,
* we know the front of the queue is the nearest unexpanded node (to the search
* location). The default STL pqueue returns the 'largest' item from top(), so
* to get the smallest, we need to replace the default Compare functor (less<>)
* with greater<>.
*/
typedef std::priority_queue<OrderedNode, std::vector<OrderedNode>, FNPQCompare> FindNearestPQueue;
typedef Ordered<FGPositioned*> OrderedPositioned;
typedef std::vector<OrderedPositioned> FindNearestResults;
Node* global_spatialOctree = NULL;
/**
* Octree node base class, tracks its bounding box and provides various
* queries relating to it
*/
class Node
{
public:
bool contains(const SGVec3d& aPos) const
{
return intersects(aPos, _box);
}
double distSqrToNearest(const SGVec3d& aPos) const
{
return distSqr(aPos, _box.getClosestPoint(aPos));
}
virtual void insert(FGPositioned* aP) = 0;
virtual void visit(const SGVec3d& aPos, double aCutoff,
FGPositioned::Filter* aFilter,
FindNearestResults& aResults, FindNearestPQueue&) = 0;
protected:
Node(const SGBoxd &aBox) :
_box(aBox)
{
}
const SGBoxd _box;
};
class Leaf : public Node
{
public:
Leaf(const SGBoxd& aBox) :
Node(aBox)
{
}
const FGPositioned::List& members() const
{ return _members; }
virtual void insert(FGPositioned* aP)
{
_members.push_back(aP);
}
virtual void visit(const SGVec3d& aPos, double aCutoff,
FGPositioned::Filter* aFilter,
FindNearestResults& aResults, FindNearestPQueue&)
{
int previousResultsSize = aResults.size();
int addedCount = 0;
for (unsigned int i=0; i<_members.size(); ++i) {
FGPositioned* p = _members[i];
double d2 = distSqr(aPos, p->cart());
if (d2 > aCutoff) {
continue;
}
if (aFilter) {
if (aFilter->hasTypeRange() && !aFilter->passType(p->type())) {
continue;
}
if (!aFilter->pass(p)) {
continue;
}
} // of have a filter
++addedCount;
aResults.push_back(OrderedPositioned(p, d2));
}
if (addedCount == 0) {
return;
}
// keep aResults sorted
// sort the new items, usually just one or two items
std::sort(aResults.begin() + previousResultsSize, aResults.end());
// merge the two sorted ranges together - in linear time
std::inplace_merge(aResults.begin(),
aResults.begin() + previousResultsSize, aResults.end());
}
private:
FGPositioned::List _members;
};
class Branch : public Node
{
public:
Branch(const SGBoxd& aBox) :
Node(aBox)
{
memset(children, 0, sizeof(Node*) * 8);
}
virtual void insert(FGPositioned* aP)
{
SGVec3d cart(aP->cart());
assert(contains(cart));
int childIndex = 0;
SGVec3d center(_box.getCenter());
// tests must match indices in SGbox::getCorner
if (cart.x() < center.x()) {
childIndex += 1;
}
if (cart.y() < center.y()) {
childIndex += 2;
}
if (cart.z() < center.z()) {
childIndex += 4;
}
Node* child = children[childIndex];
if (!child) { // lazy building of children
SGBoxd cb(boxForChild(childIndex));
double d2 = dot(cb.getSize(), cb.getSize());
if (d2 < LEAF_SIZE_SQR) {
child = new Leaf(cb);
} else {
child = new Branch(cb);
}
children[childIndex] = child;
}
child->insert(aP);
}
virtual void visit(const SGVec3d& aPos, double aCutoff,
FGPositioned::Filter*,
FindNearestResults&, FindNearestPQueue& aQ)
{
for (unsigned int i=0; i<8; ++i) {
if (!children[i]) {
continue;
}
double d2 = children[i]->distSqrToNearest(aPos);
if (d2 > aCutoff) {
continue; // exceeded cutoff
}
aQ.push(Ordered<Node*>(children[i], d2));
} // of child iteration
}
private:
/**
* Return the box for a child touching the specified corner
*/
SGBoxd boxForChild(unsigned int aCorner) const
{
SGBoxd r(_box.getCenter());
r.expandBy(_box.getCorner(aCorner));
return r;
}
Node* children[8];
};
void findNearestN(const SGVec3d& aPos, unsigned int aN, double aCutoffM, FGPositioned::Filter* aFilter, FGPositioned::List& aResults)
{
aResults.clear();
FindNearestPQueue pq;
FindNearestResults results;
pq.push(Ordered<Node*>(global_spatialOctree, 0));
double cut = aCutoffM * aCutoffM;
while (!pq.empty()) {
if (!results.empty()) {
// terminate the search if we have sufficent results, and we are
// sure no node still on the queue contains a closer match
double furthestResultOrder = results.back().order();
if ((results.size() >= aN) && (furthestResultOrder < pq.top().order())) {
break;
}
}
Node* nd = pq.top().get();
pq.pop();
nd->visit(aPos, cut, aFilter, results, pq);
} // of queue iteration
// depending on leaf population, we may have (slighty) more results
// than requested
unsigned int numResults = std::min((unsigned int) results.size(), aN);
// copy results out
aResults.resize(numResults);
for (unsigned int r=0; r<numResults; ++r) {
aResults[r] = results[r].get();
}
}
void findAllWithinRange(const SGVec3d& aPos, double aRangeM, FGPositioned::Filter* aFilter, FGPositioned::List& aResults)
{
aResults.clear();
FindNearestPQueue pq;
FindNearestResults results;
pq.push(Ordered<Node*>(global_spatialOctree, 0));
double rng = aRangeM * aRangeM;
while (!pq.empty()) {
Node* nd = pq.top().get();
pq.pop();
nd->visit(aPos, rng, aFilter, results, pq);
} // of queue iteration
unsigned int numResults = results.size();
// copy results out
aResults.resize(numResults);
for (unsigned int r=0; r<numResults; ++r) {
aResults[r] = results[r].get();
}
}
} // of namespace Octree
//////////////////////////////////////////////////////////////////////////////
static void
addToIndices(FGPositioned* aPos)
{
assert(aPos);
if (!aPos->ident().empty()) {
std::string u(boost::to_upper_copy(aPos->ident()));
global_identIndex.insert(global_identIndex.begin(),
std::make_pair(u, aPos));
}
if (!aPos->name().empty()) {
std::string u(boost::to_upper_copy(aPos->name()));
global_nameIndex.insert(global_nameIndex.begin(),
std::make_pair(u, aPos));
}
if (!Octree::global_spatialOctree) {
double RADIUS_EARTH_M = 7000 * 1000.0; // 7000km is plenty
SGVec3d earthExtent(RADIUS_EARTH_M, RADIUS_EARTH_M, RADIUS_EARTH_M);
Octree::global_spatialOctree = new Octree::Branch(SGBox<double>(-earthExtent, earthExtent));
}
Octree::global_spatialOctree->insert(aPos);
}
static void
removeFromIndices(FGPositioned* aPos)
{
assert(aPos);
if (!aPos->ident().empty()) {
std::string u(boost::to_upper_copy(aPos->ident()));
NamedPositionedIndex::iterator it = global_identIndex.find(u);
while (it != global_identIndex.end() && (it->first == u)) {
if (it->second == aPos) {
global_identIndex.erase(it);
break;
}
++it;
} // of multimap walk
}
if (!aPos->name().empty()) {
std::string u(boost::to_upper_copy(aPos->name()));
NamedPositionedIndex::iterator it = global_nameIndex.find(u);
while (it != global_nameIndex.end() && (it->first == u)) {
if (it->second == aPos) {
global_nameIndex.erase(it);
break;
}
++it;
} // of multimap walk
}
}
//////////////////////////////////////////////////////////////////////////////
class OrderByName
{
public:
bool operator()(FGPositioned* a, FGPositioned* b) const
{
return a->name() < b->name();
}
};
void findInIndex(NamedPositionedIndex& aIndex, const std::string& aFind, std::vector<FGPositioned*>& aResult)
{
NamedPositionedIndex::const_iterator it = aIndex.begin();
NamedPositionedIndex::const_iterator end = aIndex.end();
bool haveFilter = !aFind.empty();
for (; it != end; ++it) {
FGPositioned::Type ty = it->second->type();
if ((ty < FGPositioned::AIRPORT) || (ty > FGPositioned::SEAPORT)) {
continue;
}
if (haveFilter && it->first.find(aFind) == std::string::npos) {
continue;
}
aResult.push_back(it->second);
} // of index iteration
}
/**
* A special purpose helper (imported by FGAirport::searchNamesAndIdents) to
* implement the AirportList dialog. It's unfortunate that it needs to reside
* here, but for now it's least ugly solution.
*/
char** searchAirportNamesAndIdents(const std::string& aFilter)
{
// note this is a vector of raw pointers, not smart pointers, because it
// may get very large and smart-pointer-atomicity-locking then becomes a
// bottleneck for this case.
std::vector<FGPositioned*> matches;
if (!aFilter.empty()) {
std::string filter = boost::to_upper_copy(aFilter);
findInIndex(global_identIndex, filter, matches);
findInIndex(global_nameIndex, filter, matches);
} else {
findInIndex(global_identIndex, std::string(), matches);
}
// sort alphabetically on name
std::sort(matches.begin(), matches.end(), OrderByName());
// convert results to format comptible with puaList
unsigned int numMatches = matches.size();
char** result = new char*[numMatches + 1];
result[numMatches] = NULL; // end-of-list marker
// nasty code to avoid excessive string copying and allocations.
// We format results as follows (note whitespace!):
// ' name-of-airport-chars (ident)'
// so the total length is:
// 1 + strlen(name) + 4 + strlen(icao) + 1 + 1 (for the null)
// which gives a grand total of 7 + name-length + icao-length.
// note the ident can be three letters (non-ICAO local strip), four
// (default ICAO) or more (extended format ICAO)
for (unsigned int i=0; i<numMatches; ++i) {
int nameLength = matches[i]->name().size();
int icaoLength = matches[i]->ident().size();
char* entry = new char[7 + nameLength + icaoLength];
char* dst = entry;
*dst++ = ' ';
memcpy(dst, matches[i]->name().c_str(), nameLength);
dst += nameLength;
*dst++ = ' ';
*dst++ = ' ';
*dst++ = ' ';
*dst++ = '(';
memcpy(dst, matches[i]->ident().c_str(), icaoLength);
dst += icaoLength;
*dst++ = ')';
*dst++ = 0;
result[i] = entry;
}
return result;
}
static void validateSGGeod(const SGGeod& geod) static void validateSGGeod(const SGGeod& geod)
{ {
@ -516,96 +55,28 @@ static void validateSGGeod(const SGGeod& geod)
} }
} }
///////////////////////////////////////////////////////////////////////////////
bool
FGPositioned::Filter::hasTypeRange() const
{
assert(minType() <= maxType());
return (minType() != INVALID) && (maxType() != INVALID);
}
bool
FGPositioned::Filter::passType(Type aTy) const
{
assert(hasTypeRange());
return (minType() <= aTy) && (maxType() >= aTy);
}
static FGPositioned::List
findAll(const NamedPositionedIndex& aIndex,
const std::string& aName,
FGPositioned::Filter* aFilter,
bool aExact)
{
FGPositioned::List result;
if (aName.empty()) {
return result;
}
std::string name = boost::to_upper_copy(aName);
NamedPositionedIndex::const_iterator upperBound;
if (aExact) {
upperBound = aIndex.upper_bound(name);
} else {
std::string upperBoundId = name;
upperBoundId[upperBoundId.size()-1]++;
upperBound = aIndex.lower_bound(upperBoundId);
}
NamedPositionedIndex::const_iterator it = aIndex.lower_bound(name);
for (; it != upperBound; ++it) {
FGPositionedRef candidate = it->second;
if (aFilter) {
if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
continue;
}
if (!aFilter->pass(candidate)) {
continue;
}
}
result.push_back(candidate);
}
return result;
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
FGPositioned::FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos) : FGPositioned::FGPositioned(PositionedID aGuid, Type ty, const std::string& aIdent, const SGGeod& aPos) :
mGuid(aGuid),
mPosition(aPos), mPosition(aPos),
mCart(SGVec3d::fromGeod(mPosition)),
mType(ty), mType(ty),
mIdent(aIdent) mIdent(aIdent)
{ {
} }
void FGPositioned::init(bool aIndexed)
{
SGReferenced::get(this); // hold an owning ref, for the moment
mCart = SGVec3d::fromGeod(mPosition);
if (aIndexed) {
assert(mType != TAXIWAY && mType != PAVEMENT);
addToIndices(this);
}
}
FGPositioned::~FGPositioned() FGPositioned::~FGPositioned()
{ {
//std::cout << "destroying:" << mIdent << "/" << nameForType(mType) << std::endl; // std::cout << "destroying:" << mIdent << "/" << nameForType(mType) << std::endl;
removeFromIndices(this);
} }
FGPositioned* FGPositioned*
FGPositioned::createUserWaypoint(const std::string& aIdent, const SGGeod& aPos) FGPositioned::createUserWaypoint(const std::string& aIdent, const SGGeod& aPos)
{ {
FGPositioned* wpt = new FGPositioned(WAYPOINT, aIdent, aPos); PositionedID id = NavDataCache::instance()->createUserWaypoint(aIdent, aPos);
wpt->init(true); return NavDataCache::instance()->loadById(id);
return wpt;
} }
const SGVec3d& const SGVec3d&
@ -706,14 +177,22 @@ const char* FGPositioned::nameForType(Type aTy)
FGPositionedRef FGPositionedRef
FGPositioned::findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter) FGPositioned::findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter)
{ {
validateSGGeod(aPos); validateSGGeod(aPos);
return NavDataCache::instance()->findClosestWithIdent(aIdent, aPos, aFilter);
}
FGPositioned::List r(findAll(global_identIndex, aIdent, aFilter, true)); FGPositionedRef
if (r.empty()) { FGPositioned::findFirstWithIdent(const std::string& aIdent, Filter* aFilter)
return FGPositionedRef(); {
if (aIdent.empty()) {
return NULL;
}
List r = NavDataCache::instance()->findAllWithIdent(aIdent, aFilter, true);
if (r.empty()) {
return NULL;
} }
sortByRange(r, aPos);
return r.front(); return r.front();
} }
@ -731,13 +210,13 @@ FGPositioned::findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilt
FGPositioned::List FGPositioned::List
FGPositioned::findAllWithIdent(const std::string& aIdent, Filter* aFilter, bool aExact) FGPositioned::findAllWithIdent(const std::string& aIdent, Filter* aFilter, bool aExact)
{ {
return findAll(global_identIndex, aIdent, aFilter, aExact); return NavDataCache::instance()->findAllWithIdent(aIdent, aFilter, aExact);
} }
FGPositioned::List FGPositioned::List
FGPositioned::findAllWithName(const std::string& aName, Filter* aFilter, bool aExact) FGPositioned::findAllWithName(const std::string& aName, Filter* aFilter, bool aExact)
{ {
return findAll(global_nameIndex, aName, aFilter, aExact); return NavDataCache::instance()->findAllWithName(aName, aFilter, aExact);
} }
FGPositionedRef FGPositionedRef
@ -763,54 +242,7 @@ FGPositioned::findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm
Octree::findNearestN(SGVec3d::fromGeod(aPos), aN, aCutoffNm * SG_NM_TO_METER, aFilter, result); Octree::findNearestN(SGVec3d::fromGeod(aPos), aN, aCutoffNm * SG_NM_TO_METER, aFilter, result);
return result; return result;
} }
FGPositionedRef
FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter)
{
if (aId.empty()) {
return NULL;
}
std::string id(boost::to_upper_copy(aId));
// It is essential to bound our search, to avoid iterating all the way to the end of the database.
// Do this by generating a second ID with the final character incremented by 1.
// e.g., if the partial ID is "KI", we wish to search "KIxxx" but not "KJ".
std::string upperBoundId = id;
upperBoundId[upperBoundId.size()-1]++;
NamedPositionedIndex::const_iterator upperBound = global_identIndex.lower_bound(upperBoundId);
NamedIndexRange range = global_identIndex.equal_range(id);
while (range.first != upperBound) {
for (; range.first != range.second; ++range.first) {
FGPositionedRef candidate = range.first->second;
if (aCur == candidate) {
aCur = NULL; // found our start point, next match will pass
continue;
}
if (aFilter) {
if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
continue;
}
if (!aFilter->pass(candidate)) {
continue;
}
}
if (!aCur) {
return candidate;
}
}
// Unable to match the filter with this range - try the next range.
range = global_identIndex.equal_range(range.second->first);
}
return NULL; // Reached the end of the valid sequence with no match.
}
void void
FGPositioned::sortByRange(List& aResult, const SGGeod& aPos) FGPositioned::sortByRange(List& aResult, const SGGeod& aPos)
{ {
@ -835,7 +267,15 @@ FGPositioned::sortByRange(List& aResult, const SGGeod& aPos)
} }
} }
FGPositioned::TypeFilter::TypeFilter(Type aTy) void FGPositioned::modifyPosition(const SGGeod& newPos)
{
const_cast<SGGeod&>(mPosition) = newPos;
const_cast<SGVec3d&>(mCart) = SGVec3d::fromGeod(newPos);
}
FGPositioned::TypeFilter::TypeFilter(Type aTy) :
mMinType(aTy),
mMaxType(aTy)
{ {
addType(aTy); addType(aTy);
} }
@ -844,10 +284,11 @@ void FGPositioned::TypeFilter::addType(Type aTy)
{ {
if (aTy == INVALID) { if (aTy == INVALID) {
return; return;
} }
types.push_back(aTy); types.push_back(aTy);
mMinType = std::min(mMinType, aTy);
mMaxType = std::max(mMaxType, aTy);
} }
bool bool

View file

@ -23,15 +23,19 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <stdint.h>
#include <simgear/structure/SGSharedPtr.hxx> #include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/SGMath.hxx> #include <simgear/math/SGMath.hxx>
class FGPositioned; class FGPositioned;
class SGPropertyNode;
typedef SGSharedPtr<FGPositioned> FGPositionedRef; typedef SGSharedPtr<FGPositioned> FGPositionedRef;
typedef int64_t PositionedID;
typedef std::vector<PositionedID> PositionedIDVec;
namespace flightgear { class NavDataCache; }
class FGPositioned : public SGReferenced class FGPositioned : public SGReferenced
{ {
public: public:
@ -47,17 +51,24 @@ public:
PARK_STAND, PARK_STAND,
WAYPOINT, WAYPOINT,
FIX, FIX,
VOR,
NDB, NDB,
VOR,
ILS, ILS,
LOC, LOC,
GS, GS,
OM, OM,
MM, MM,
IM, IM,
/// important that DME & TACAN are adjacent to keep the TacanFilter
/// efficient - DMEs are proxies for TACAN/VORTAC stations
DME, DME,
TACAN, TACAN,
MOBILE_TACAN,
OBSTACLE, OBSTACLE,
/// an actual airport tower - not a radio comms facility!
/// some airports have multiple towers, eg EHAM, although our data source
/// doesn't necessarily include them
TOWER,
FREQ_GROUND, FREQ_GROUND,
FREQ_TOWER, FREQ_TOWER,
FREQ_ATIS, FREQ_ATIS,
@ -89,6 +100,9 @@ public:
const SGGeod& geod() const const SGGeod& geod() const
{ return mPosition; } { return mPosition; }
PositionedID guid() const
{ return mGuid; }
/** /**
* The cartesian position associated with this object * The cartesian position associated with this object
@ -125,15 +139,6 @@ public:
virtual Type maxType() const virtual Type maxType() const
{ return INVALID; } { return INVALID; }
/**
* Test if this filter has a non-empty type range
*/
bool hasTypeRange() const;
/**
* Assuming hasTypeRange is true, test if a given type passes the range
*/
bool passType(Type aTy) const;
bool operator()(FGPositioned* aPos) const bool operator()(FGPositioned* aPos) const
{ return pass(aPos); } { return pass(aPos); }
@ -144,25 +149,25 @@ public:
public: public:
TypeFilter(Type aTy); TypeFilter(Type aTy);
virtual bool pass(FGPositioned* aPos) const; virtual bool pass(FGPositioned* aPos) const;
virtual Type minType() const
{ return mMinType; }
virtual Type maxType() const
{ return mMaxType; }
void addType(Type aTy); void addType(Type aTy);
private: private:
std::vector<Type> types; std::vector<Type> types;
Type mMinType, mMaxType;
}; };
static List findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilter = NULL); static List findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilter = NULL);
static FGPositionedRef findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter = NULL); static FGPositionedRef findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter = NULL);
/** static FGPositionedRef findFirstWithIdent(const std::string& aIdent, Filter* aFilter = NULL);
* Find the next item with the specified partial ID, after the 'current' item
* Note this function is not hyper-efficient, particular where the partial id
* spans a large number of candidates.
*
* @param aCur - Current item, or NULL to retrieve the first item with partial id
* @param aId - the (partial) id to lookup
*/
static FGPositionedRef findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter = NULL);
/** /**
* Find all items with the specified ident * Find all items with the specified ident
* @param aFilter - optional filter on items * @param aFilter - optional filter on items
@ -214,16 +219,15 @@ public:
static FGPositioned* createUserWaypoint(const std::string& aIdent, const SGGeod& aPos); static FGPositioned* createUserWaypoint(const std::string& aIdent, const SGGeod& aPos);
protected: protected:
friend class flightgear::NavDataCache;
FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos); FGPositioned(PositionedID aGuid, Type ty, const std::string& aIdent, const SGGeod& aPos);
void modifyPosition(const SGGeod& newPos);
void init(bool aIndexed); const PositionedID mGuid;
const SGGeod mPosition;
// can't be const right now, navrecord at least needs to fix up the position const SGVec3d mCart;
// after navaids are parsed
SGGeod mPosition;
SGVec3d mCart; // once mPosition is const, this can be const too
const Type mType; const Type mType;
const std::string mIdent; const std::string mIdent;
}; };

138243
src/Navaids/sqlite3.c Normal file

File diff suppressed because it is too large Load diff

7055
src/Navaids/sqlite3.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -669,7 +669,7 @@ static const char* procedureGhostGetMember(naContext c, void* g, naRef field, na
else if (!strcmp(fieldName, "radio")) *out = procedureRadioType(c, proc->type()); else if (!strcmp(fieldName, "radio")) *out = procedureRadioType(c, proc->type());
else if (!strcmp(fieldName, "runways")) { else if (!strcmp(fieldName, "runways")) {
*out = naNewVector(c); *out = naNewVector(c);
BOOST_FOREACH(FGRunwayPtr rwy, proc->runways()) { BOOST_FOREACH(FGRunwayRef rwy, proc->runways()) {
naVec_append(*out, stringToNasal(c, rwy->ident())); naVec_append(*out, stringToNasal(c, rwy->ident()));
} }
} else if (!strcmp(fieldName, "transitions")) { } else if (!strcmp(fieldName, "transitions")) {
@ -1416,7 +1416,8 @@ static naRef f_navinfo(naContext c, naRef me, int argc, naRef* args)
return naNil(); return naNil();
} }
navlist = globals->get_navlist()->findByIdentAndFreq( pos, id, 0.0, type ); FGNavList::TypeFilter filter(type);
navlist = FGNavList::findByIdentAndFreq( pos, id, 0.0, &filter );
naRef reply = naNewVector(c); naRef reply = naNewVector(c);
for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) { for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
@ -1470,7 +1471,8 @@ static naRef f_findNavaidByFrequency(naContext c, naRef me, int argc, naRef* arg
type = FGPositioned::typeFromName(naStr_data(args[argOffset])); type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
} }
nav_list_type navs = globals->get_navlist()->findAllByFreq(freqMhz, pos, type); FGNavList::TypeFilter filter(type);
nav_list_type navs = FGNavList::findAllByFreq(freqMhz, pos, &filter);
if (navs.empty()) { if (navs.empty()) {
return naNil(); return naNil();
} }
@ -1495,7 +1497,9 @@ static naRef f_findNavaidsByFrequency(naContext c, naRef me, int argc, naRef* ar
} }
naRef r = naNewVector(c); naRef r = naNewVector(c);
nav_list_type navs = globals->get_navlist()->findAllByFreq(freqMhz, pos, type);
FGNavList::TypeFilter filter(type);
nav_list_type navs = FGNavList::findAllByFreq(freqMhz, pos, &filter);
BOOST_FOREACH(nav_rec_ptr a, navs) { BOOST_FOREACH(nav_rec_ptr a, navs) {
naVec_append(r, ghostForNavaid(c, a.ptr())); naVec_append(r, ghostForNavaid(c, a.ptr()));
@ -1520,8 +1524,9 @@ static naRef f_findNavaidsByIdent(naContext c, naRef me, int argc, naRef* args)
type = FGPositioned::typeFromName(naStr_data(args[argOffset])); type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
} }
FGNavList::TypeFilter filter(type);
naRef r = naNewVector(c); naRef r = naNewVector(c);
nav_list_type navs = globals->get_navlist()->findByIdentAndFreq(pos, ident, 0.0, type); nav_list_type navs = FGNavList::findByIdentAndFreq(pos, ident, 0.0, &filter);
BOOST_FOREACH(nav_rec_ptr a, navs) { BOOST_FOREACH(nav_rec_ptr a, navs) {
naVec_append(r, ghostForNavaid(c, a.ptr())); naVec_append(r, ghostForNavaid(c, a.ptr()));