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,34 +1,27 @@
#include "CommStation.hxx"
#include <map>
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
#include <Navaids/NavDataCache.hxx>
namespace flightgear {
CommStation::CommStation(const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq) :
FGPositioned(t, name, pos),
CommStation::CommStation(PositionedID aGuid, const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq) :
FGPositioned(aGuid, t, name, pos),
mRangeNM(range),
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;
}
FGAirport* CommStation::airport() const
{
return (FGAirport*) NavDataCache::instance()->loadById(mAirport);
}
double CommStation::freqMHz() const
{
return mFreqKhz / 100.0;
@ -37,23 +30,7 @@ double CommStation::freqMHz() const
CommStation*
CommStation::findByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt)
{
FrequencyMapRange range = static_frequencies.equal_range(freqKhz);
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();
return (CommStation*) NavDataCache::instance()->findCommByFreq(freqKhz, pos, filt).ptr();
}
} // of namespace flightgear

View file

@ -11,10 +11,10 @@ namespace flightgear
class CommStation : public FGPositioned
{
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);
FGAirport* airport() const { return mAirport; }
void setAirport(PositionedID apt);
FGAirport* airport() const;
int rangeNm() const
{ return mRangeNM; }
@ -28,7 +28,7 @@ public:
private:
int mRangeNM;
int mFreqKhz;
FGAirport* mAirport;
PositionedID mAirport;
};
} // of namespace flightgear

View file

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

View file

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

View file

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

View file

@ -59,7 +59,7 @@ public:
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 addBezierNode(const SGGeod &aPos, const SGGeod &aCtrlPt, bool aClose = false);

View file

@ -46,20 +46,17 @@ using std::string;
* 12 - lakebed
*/
FGRunwayBase::FGRunwayBase(Type aTy, const string& aIdent,
FGRunwayBase::FGRunwayBase(PositionedID aGuid, Type aTy, const string& aIdent,
const SGGeod& aGeod,
const double heading, const double length,
const double width,
const int surface_code,
bool index) :
FGPositioned(aTy, aIdent, aGeod)
const int surface_code) :
FGPositioned(aGuid, aTy, aIdent, aGeod)
{
_heading = heading;
_length = length;
_width = width;
_surface_code = surface_code;
init(index);
}
SGGeod FGRunwayBase::pointOnCenterline(double aOffset) const
@ -100,11 +97,12 @@ bool FGRunwayBase::isHardSurface() const
return ((_surface_code == 1) || (_surface_code == 2));
}
FGTaxiway::FGTaxiway(const string& aIdent,
FGTaxiway::FGTaxiway(PositionedID aGuid,
const string& aIdent,
const SGGeod& aGeod,
const double heading, const double length,
const double width,
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
{
public:
FGRunwayBase(Type aTy, const std::string& aIdent,
FGRunwayBase(PositionedID aGuid, Type aTy, const std::string& aIdent,
const SGGeod& aGeod,
const double heading, const double length,
const double width,
const int surface_code,
bool index);
const int surface_code);
/**
* Retrieve a position on the extended centerline. Positive values
@ -100,7 +99,8 @@ protected:
class FGTaxiway : public FGRunwayBase
{
public:
FGTaxiway(const std::string& aIdent,
FGTaxiway(PositionedID aGuid,
const std::string& aIdent,
const SGGeod& aGeod,
const double heading, const double length,
const double width,

View file

@ -40,33 +40,12 @@
#include <Airports/simple.hxx>
#include <Navaids/procedure.hxx>
#include <Navaids/navrecord.hxx>
#include <Navaids/NavDataCache.hxx>
using std::string;
static std::string cleanRunwayNo(const std::string& aRwyNo)
{
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,
FGRunway::FGRunway(PositionedID aGuid,
PositionedID aAirport, const string& aIdent,
const SGGeod& aGeod,
const double heading, const double length,
const double width,
@ -74,13 +53,14 @@ FGRunway::FGRunway(FGAirport* aAirport, const string& aIdent,
const double stopway,
const int surface_code,
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),
_isReciprocal(reciprocal),
_reciprocal(NULL),
_reciprocal(0),
_displ_thresh(displ_thresh),
_stopway(stopway),
_ils(NULL)
_ils(0)
{
}
@ -144,39 +124,37 @@ SGGeod FGRunway::threshold() const
return pointOnCenterline(_displ_thresh * SG_FEET_TO_METER);
}
void FGRunway::processThreshold(SGPropertyNode* aThreshold)
void FGRunway::setReciprocalRunway(PositionedID other)
{
assert(ident() == aThreshold->getStringValue("rwy"));
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;
assert(_reciprocal==0);
_reciprocal = other;
}
void FGRunway::setReciprocalRunway(FGRunway* other)
FGAirport* FGRunway::airport() const
{
assert(_reciprocal==NULL);
assert((other->_reciprocal == NULL) || (other->_reciprocal == this));
return (FGAirport*) flightgear::NavDataCache::instance()->loadById(_airport);
}
_reciprocal = other;
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
{
FGAirport* apt = airport();
std::vector<flightgear::SID*> result;
for (unsigned int i=0; i<_airport->numSIDs(); ++i) {
flightgear::SID* s = _airport->getSIDByIndex(i);
for (unsigned int i=0; i<apt->numSIDs(); ++i) {
flightgear::SID* s = apt->getSIDByIndex(i);
if (s->isForRunway(this)) {
result.push_back(s);
}
@ -187,9 +165,10 @@ std::vector<flightgear::SID*> FGRunway::getSIDs() const
std::vector<flightgear::STAR*> FGRunway::getSTARs() const
{
FGAirport* apt = airport();
std::vector<flightgear::STAR*> result;
for (unsigned int i=0; i<_airport->numSTARs(); ++i) {
flightgear::STAR* s = _airport->getSTARByIndex(i);
for (unsigned int i=0; i<apt->numSTARs(); ++i) {
flightgear::STAR* s = apt->getSTARByIndex(i);
if (s->isForRunway(this)) {
result.push_back(s);
}
@ -200,9 +179,10 @@ std::vector<flightgear::STAR*> FGRunway::getSTARs() const
std::vector<flightgear::Approach*> FGRunway::getApproaches() const
{
FGAirport* apt = airport();
std::vector<flightgear::Approach*> result;
for (unsigned int i=0; i<_airport->numApproaches(); ++i) {
flightgear::Approach* s = _airport->getApproachByIndex(i);
for (unsigned int i=0; i<apt->numApproaches(); ++i) {
flightgear::Approach* s = apt->getApproachByIndex(i);
if (s->runway() == this) {
result.push_back(s);
}

View file

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

View file

@ -31,6 +31,7 @@
#include "simple.hxx"
#include <cassert>
#include <boost/foreach.hpp>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
@ -48,30 +49,28 @@
#include <Navaids/procedure.hxx>
#include <Navaids/waypoint.hxx>
#include <ATC/CommStation.hxx>
#include <Navaids/NavDataCache.hxx>
using std::vector;
using std::pair;
using namespace flightgear;
// magic import of a helper which uses FGPositioned internals
extern char** searchAirportNamesAndIdents(const std::string& aFilter);
/***************************************************************************
* 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) :
FGPositioned(aType, id, location),
_tower_location(tower_location),
FGPositioned(aGuid, aType, id, location),
_name(name),
_has_metar(has_metar),
_dynamics(0),
mTowerDataLoaded(false),
mRunwaysLoaded(false),
mTaxiwaysLoaded(true)
{
init(true); // init FGPositioned
}
@ -132,53 +131,30 @@ FGRunway* FGAirport::getRunwayByIndex(unsigned int aIndex) const
loadRunways();
assert(aIndex >= 0 && aIndex < mRunways.size());
return mRunways[aIndex];
return (FGRunway*) flightgear::NavDataCache::instance()->loadById(mRunways[aIndex]);
}
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
{
Runway_iterator it = getIteratorForRunwayIdent(aIdent);
if (it == mRunways.end()) {
PositionedID id = flightgear::NavDataCache::instance()->airportItemWithIdent(guid(), FGPositioned::RUNWAY, aIdent);
if (id == 0) {
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");
}
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
{
loadRunways();
Runway_iterator it = mRunways.begin();
FGRunway* result = NULL;
double currentBestQuality = 0.0;
@ -188,17 +164,17 @@ FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const
double surfaceWeight = param->getDoubleValue("surface-weight", 10);
double deviationWeight = param->getDoubleValue("deviation-weight", 1);
for (; it != mRunways.end(); ++it) {
double good = (*it)->score(lengthWeight, widthWeight, surfaceWeight);
double dev = aHeading - (*it)->headingDeg();
BOOST_FOREACH(PositionedID id, mRunways) {
FGRunway* rwy = (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
double good = rwy->score(lengthWeight, widthWeight, surfaceWeight);
double dev = aHeading - rwy->headingDeg();
SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
double bad = fabs(deviationWeight * dev) + 1e-20;
double quality = good / bad;
if (quality > currentBestQuality) {
currentBestQuality = quality;
result = *it;
result = rwy;
}
}
@ -209,19 +185,20 @@ FGRunway* FGAirport::findBestRunwayForPos(const SGGeod& aPos) const
{
loadRunways();
Runway_iterator it = mRunways.begin();
FGRunway* result = NULL;
double currentLowestDev = 180.0;
for (; it != mRunways.end(); ++it) {
double inboundCourse = SGGeodesy::courseDeg(aPos, (*it)->end());
double dev = inboundCourse - (*it)->headingDeg();
BOOST_FOREACH(PositionedID id, mRunways) {
FGRunway* rwy = (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
double inboundCourse = SGGeodesy::courseDeg(aPos, rwy->end());
double dev = inboundCourse - rwy->headingDeg();
SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
dev = fabs(dev);
if (dev < currentLowestDev) { // new best match
currentLowestDev = dev;
result = *it;
result = rwy;
}
} // of runway iteration
@ -233,9 +210,9 @@ bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const
{
loadRunways();
unsigned int numRunways(mRunways.size());
for (unsigned int r=0; r<numRunways; ++r) {
FGRunway* rwy = mRunways[r];
BOOST_FOREACH(PositionedID id, mRunways) {
FGRunway* rwy = (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
if (rwy->isReciprocal()) {
continue; // we only care about lengths, so don't do work twice
}
@ -258,7 +235,7 @@ FGTaxiway* FGAirport::getTaxiwayByIndex(unsigned int aIndex) const
{
loadTaxiways();
assert(aIndex >= 0 && aIndex < mTaxiways.size());
return mTaxiways[aIndex];
return (FGTaxiway*) flightgear::NavDataCache::instance()->loadById(mTaxiways[aIndex]);
}
unsigned int FGAirport::numPavements() const
@ -271,21 +248,7 @@ FGPavement* FGAirport::getPavementByIndex(unsigned int aIndex) const
{
loadTaxiways();
assert(aIndex >= 0 && aIndex < mPavements.size());
return 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);
return (FGPavement*) flightgear::NavDataCache::instance()->loadById(mPavements[aIndex]);
}
FGRunway* FGAirport::getActiveRunwayForUsage() const
@ -341,9 +304,8 @@ bool FGAirport::HardSurfaceFilter::passAirport(FGAirport* aApt) const
FGAirport* FGAirport::findByIdent(const std::string& aIdent)
{
FGPositionedRef r;
PortsFilter filter;
r = FGPositioned::findNextWithPartialId(r, aIdent, &filter);
FGPositionedRef r = FGPositioned::findFirstWithIdent(aIdent, &filter);
if (!r) {
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;
PortsFilter filter;
r = FGPositioned::findNextWithPartialId(r, aIdent, &filter);
r = FGPositioned::findFirstWithIdent(aIdent, &filter);
if (!r) {
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)
{
// we delegate all the work to a horrible helper in FGPositioned, which can
// access the (private) index data.
return searchAirportNamesAndIdents(aFilter);
return NavDataCache::instance()->searchAirportNamesAndIdents(aFilter);
}
// find basic airport location info from airport database
@ -384,8 +344,10 @@ void FGAirport::loadRunways() const
return; // already loaded, great
}
mRunwaysLoaded = true;
loadSceneryDefinitions();
mRunwaysLoaded = true;
mRunways = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::RUNWAY);
}
void FGAirport::loadTaxiways() const
@ -393,6 +355,9 @@ void FGAirport::loadTaxiways() const
if (mTaxiwaysLoaded) {
return; // already loaded, great
}
mTaxiwaysLoaded = true;
mTaxiways = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::TAXIWAY);
}
void FGAirport::loadProcedures() const
@ -414,19 +379,22 @@ void FGAirport::loadProcedures() const
void FGAirport::loadSceneryDefinitions() const
{
NavDataCache* cache = NavDataCache::instance();
SGPath path;
SGPropertyNode_ptr rootNode = new SGPropertyNode;
if (XMLLoader::findAirportData(ident(), "threshold", path)) {
readProperties(path.str(), rootNode);
const_cast<FGAirport*>(this)->readThresholdData(rootNode);
if (!XMLLoader::findAirportData(ident(), "threshold", path)) {
return; // no XML threshold data
}
// repeat for the tower data
rootNode = new SGPropertyNode;
if (XMLLoader::findAirportData(ident(), "twr", path)) {
readProperties(path.str(), rootNode);
const_cast<FGAirport*>(this)->readTowerData(rootNode);
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)->readThresholdData(rootNode);
cache->stampCacheFile(path);
}
void FGAirport::readThresholdData(SGPropertyNode* aRoot)
@ -447,15 +415,64 @@ void FGAirport::readThresholdData(SGPropertyNode* aRoot)
void FGAirport::processThreshold(SGPropertyNode* aThreshold)
{
// first, let's identify the current runway
string id(aThreshold->getStringValue("rwy"));
if (!hasRunwayWithIdent(id)) {
string rwyIdent(aThreshold->getStringValue("rwy"));
NavDataCache* cache = NavDataCache::instance();
PositionedID id = cache->airportItemWithIdent(guid(), FGPositioned::RUNWAY, rwyIdent);
if (id == 0) {
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;
}
FGRunway* rwy = getRunwayByIdent(id);
rwy->processThreshold(aThreshold);
double lon = aThreshold->getDoubleValue("lon"),
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)
@ -468,8 +485,74 @@ void FGAirport::readTowerData(SGPropertyNode* aRoot)
// scenery for a precise terrain elevation, we use the field elevation
// (this is also what the apt.dat code does)
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)
@ -559,23 +642,30 @@ Approach* FGAirport::findApproachWithIdent(const std::string& aIdent) const
return NULL;
}
void FGAirport::setCommStations(CommStationList& comms)
CommStationList
FGAirport::commStations() const
{
mCommStations.swap(comms);
for (unsigned int c=0; c<mCommStations.size(); ++c) {
mCommStations[c]->setAirport(this);
NavDataCache* cache = NavDataCache::instance();
CommStationList result;
BOOST_FOREACH(PositionedID pos, cache->airportItemsOfType(guid(),
FGPositioned::FREQ_GROUND,
FGPositioned::FREQ_UNICOM))
{
result.push_back((CommStation*) cache->loadById(pos));
}
return result;
}
CommStationList
FGAirport::commStationsOfType(FGPositioned::Type aTy) const
{
NavDataCache* cache = NavDataCache::instance();
CommStationList result;
for (unsigned int c=0; c<mCommStations.size(); ++c) {
if (mCommStations[c]->type() == aTy) {
result.push_back(mCommStations[c]);
}
BOOST_FOREACH(PositionedID pos, cache->airportItemsOfType(guid(), aTy)) {
result.push_back((CommStation*) cache->loadById(pos));
}
return result;
}

View file

@ -41,10 +41,6 @@ class FGTaxiway;
class FGPavement;
class SGPropertyNode;
typedef SGSharedPtr<FGRunway> FGRunwayPtr;
typedef SGSharedPtr<FGTaxiway> FGTaxiwayPtr;
typedef SGSharedPtr<FGPavement> FGPavementPtr;
namespace flightgear {
class SID;
class STAR;
@ -66,7 +62,7 @@ namespace flightgear {
class FGAirport : public FGPositioned
{
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);
~FGAirport();
@ -87,7 +83,14 @@ public:
virtual const std::string& name() const
{ 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; }
@ -123,10 +126,6 @@ public:
unsigned int numPavements() 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
{
public:
@ -217,19 +216,10 @@ public:
*/
static char** searchNamesAndIdents(const std::string& aFilter);
void setCommStations(flightgear::CommStationList& comms);
flightgear::CommStationList commStationsOfType(FGPositioned::Type aTy) const;
const flightgear::CommStationList& commStations() const
{ return mCommStations; }
flightgear::CommStationList commStations() const;
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
FGAirport operator=(FGAirport &other);
FGAirport(const FGAirport&);
@ -245,12 +235,15 @@ private:
void readThresholdData(SGPropertyNode* aRoot);
void processThreshold(SGPropertyNode* aThreshold);
void readILSData(SGPropertyNode* aRoot);
void validateTowerData() const;
/**
* Helper to parse property data loaded from an ICAO.twr.xml filke
*/
void readTowerData(SGPropertyNode* aRoot);
SGGeod _tower_location;
std::string _name;
bool _has_metar;
FGAirportDynamics *_dynamics;
@ -259,19 +252,19 @@ private:
void loadTaxiways() const;
void loadProcedures() const;
mutable bool mTowerDataLoaded;
mutable bool mRunwaysLoaded;
mutable bool mTaxiwaysLoaded;
mutable bool mProceduresLoaded;
bool mILSDataLoaded;
std::vector<FGRunwayPtr> mRunways;
std::vector<FGTaxiwayPtr> mTaxiways;
std::vector<FGPavementPtr> mPavements;
mutable PositionedIDVec mRunways;
mutable PositionedIDVec mTaxiways;
PositionedIDVec mPavements;
std::vector<flightgear::SID*> mSIDs;
std::vector<flightgear::STAR*> mSTARs;
std::vector<flightgear::Approach*> mApproaches;
flightgear::CommStationList mCommStations;
};
// find basic airport location info from airport database

View file

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

View file

@ -937,11 +937,11 @@ public:
}
virtual FGPositioned::Type minType() const {
return _fixes ? FGPositioned::FIX : FGPositioned::VOR;
return _fixes ? FGPositioned::FIX : FGPositioned::NDB;
}
virtual FGPositioned::Type maxType() const {
return _navaids ? FGPositioned::NDB : FGPositioned::FIX;
return _navaids ? FGPositioned::VOR : FGPositioned::FIX;
}
private:
@ -1069,7 +1069,9 @@ void MapWidget::drawNavRadio(SGPropertyNode_ptr radio)
// identify the tuned station - unfortunately we don't get lat/lon directly,
// need to do the frequency search again
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"))) {
// mismatch between navradio selection logic and ours!
return;
@ -1111,7 +1113,7 @@ void MapWidget::drawNavRadio(SGPropertyNode_ptr radio)
void MapWidget::drawTunedLocalizer(SGPropertyNode_ptr radio)
{
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"))) {
// mismatch between navradio selection logic and ours!
return;

View file

@ -1089,7 +1089,7 @@ void NavDisplay::processNavRadios()
FGNavRecord* NavDisplay::processNavRadio(const SGPropertyNode_ptr& radio)
{
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"))) {
// station was not found
return NULL;

View file

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

View file

@ -60,15 +60,11 @@ private:
void set_bearing (double delta_time_sec, double bearing);
void search (double frequency, double longitude_rad,
double latitude_rad, double altitude_m);
void search (double frequency, const SGGeod& pos);
std::string _name;
unsigned int _num;
SGPropertyNode_ptr _longitude_node;
SGPropertyNode_ptr _latitude_node;
SGPropertyNode_ptr _altitude_node;
SGPropertyNode_ptr _heading_node;
SGPropertyNode_ptr _serviceable_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
{
public:
virtual bool pass(const FGPositioned* aPos) const {
virtual bool pass(FGPositioned* aPos) const
{
switch (aPos->type()) {
case FGPositioned::AIRPORT:
// how about heliports and seaports?
@ -1371,8 +1372,13 @@ public:
GPSWaypoint* DCLGPS::FindFirstById(const string& id) const
{
DCLGPSFilter filter;
FGPositionedRef result = FGPositioned::findNextWithPartialId(NULL, id, &filter);
return GPSWaypoint::createFromPositioned(result);
FGPositioned::List matches = FGPositioned::findAllWithIdent(id, &filter, false);
if (matches.empty()) {
return NULL;
}
FGPositioned::sortByRange(matches, SGGeod::fromRad(_lon, _lat));
return GPSWaypoint::createFromPositioned(matches.front());
}
GPSWaypoint* DCLGPS::FindFirstByExactId(const string& id) const
@ -1388,15 +1394,14 @@ FGPositioned* DCLGPS::FindTypedFirstById(const string& id, FGPositioned::Type ty
multi = false;
FGPositioned::TypeFilter filter(ty);
if (exact) {
FGPositioned::List matches =
FGPositioned::findAllWithIdent(id, &filter);
FGPositioned::sortByRange(matches, SGGeod::fromRad(_lon, _lat));
multi = (matches.size() > 1);
return matches.empty() ? NULL : matches.front().ptr();
FGPositioned::findAllWithIdent(id, &filter, exact);
if (matches.empty()) {
return NULL;
}
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)

View file

@ -40,6 +40,36 @@ adjust_range (double transmitter_elevation_ft, double aircraft_altitude_ft,
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 )
: _last_distance_nm(0),
@ -127,17 +157,9 @@ DME::update (double delta_time_sec)
if (_time_before_search_sec < 0) {
_time_before_search_sec = 1.0;
if( fgGetBool( "/sim/realism/dme-fallback-to-loc", true ) ) {
if( NULL == (_navrecord = globals->get_loclist()->findByFreq( frequency_mhz,
globals->get_aircraft_position())) ) {
_navrecord = globals->get_dmelist()->findByFreq( frequency_mhz,
globals->get_aircraft_position());
}
} else {
_navrecord = globals->get_dmelist()->findByFreq( frequency_mhz,
globals->get_aircraft_position());
}
SGGeod pos(globals->get_aircraft_position());
DMEFilter filter;
_navrecord = FGNavList::findByFreq(frequency_mhz, pos, &filter);
}
// 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 ) {
char sfreq[128];
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* nav = globals->get_navlist()->findByFreq(aFreqMHz, aPos);
if (nav) {
return nav;
}
return globals->get_loclist()->findByFreq(aFreqMHz, aPos);
return FGNavList::findByFreq(aFreqMHz, aPos, FGNavList::navFilter());
}
// Update current nav/adf radio stations based on current position
@ -907,7 +902,9 @@ void FGNavRadio::search()
// search glideslope station
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))
{
_nav_search = true; // search NAV on next iteration

View file

@ -148,7 +148,7 @@ public:
protected:
virtual double computeSignalQuality_norm( const SGGeod & aircraftPosition );
virtual FGNavList * getNavaidList() = 0;
virtual FGNavList::TypeFilter* getNavaidFilter() = 0;
// 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 )
{
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 );
_ident = "";
return;
@ -322,7 +323,7 @@ public:
virtual double getRange_nm(const SGGeod & aircraftPosition);
protected:
virtual double computeSignalQuality_norm( const SGGeod & aircraftPosition );
virtual FGNavList * getNavaidList();
virtual FGNavList::TypeFilter* getNavaidFilter();
private:
double _totalTime;
@ -392,9 +393,10 @@ double VOR::getRange_nm( const SGGeod & aircraftPosition )
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 )
@ -467,7 +469,7 @@ public:
protected:
virtual double computeSignalQuality_norm( const SGGeod & aircraftPosition );
virtual FGNavList * getNavaidList();
virtual FGNavList::TypeFilter* getNavaidFilter();
private:
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 )
@ -617,8 +619,7 @@ public:
virtual double getRange_nm(const SGGeod & aircraftPosition);
protected:
virtual FGNavList * getNavaidList();
virtual FGNavList::TypeFilter* getNavaidFilter();
private:
class ServiceVolume {
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)

View file

@ -58,8 +58,6 @@ TACAN::TACAN ( SGPropertyNode *node ) :
_transmitter_pos(SGGeod::fromDeg(0, 0)),
_transmitter_range_nm(0),
_transmitter_bias(0.0),
_mobile_lat(0.0),
_mobile_lon(0.0),
_listener_active(0)
{
}
@ -116,9 +114,6 @@ TACAN::init ()
SGPropertyNode *mnode = fgGetNode("/ai/models/multiplayer", _num, false);
_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);
_yaw_node = fgGetNode("/orientation/side-slip-deg", true);
_electrical_node = fgGetNode("/systems/electrical/outputs/tacan", true);
@ -146,17 +141,11 @@ TACAN::update (double delta_time_sec)
return;
}
// Get the 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;
SGGeod pos(globals->get_aircraft_position());
// On timeout, scan again
_time_before_search_sec -= delta_time_sec;
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
@ -165,15 +154,9 @@ TACAN::update (double delta_time_sec)
double mobile_bearing = 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_valid " << _mobile_valid);
geo_inverse_wgs_84(altitude_m,
latitude_deg,
longitude_deg,
_mobile_lat,
_mobile_lon,
geo_inverse_wgs_84(pos, _mobilePos,
&mobile_bearing, &mobile_az2, &mobile_distance);
@ -182,7 +165,6 @@ TACAN::update (double delta_time_sec)
double bearing = 0;
double distance = 0;
SGGeod pos = SGGeod::fromDegM(longitude_deg, latitude_deg, altitude_m);
geo_inverse_wgs_84(pos, _transmitter_pos,
&bearing, &az2, &distance);
@ -193,7 +175,7 @@ TACAN::update (double delta_time_sec)
SG_LOG( SG_INSTR, SG_DEBUG, "distance_m " << distance);
bearing = mobile_bearing;
distance = mobile_distance;
_transmitter_pos.setElevationFt(_mobile_elevation_ft);
_transmitter_pos.setElevationFt(_mobilePos.getElevationFt());
_transmitter_range_nm = _mobile_range_nm;
_transmitter_bias = _mobile_bias;
_transmitter_name = _mobile_name;
@ -231,7 +213,7 @@ TACAN::update (double delta_time_sec)
double rotation = 0;
double range_nm = adjust_range(_transmitter_pos.getElevationFt(),
altitude_m * SG_METER_TO_FEET,
pos.getElevationFt(),
_transmitter_range_nm);
if (distance_nm <= range_nm) {
@ -283,8 +265,7 @@ TACAN::update (double delta_time_sec)
} // end function update
void
TACAN::search (double frequency_mhz, double longitude_rad,
double latitude_rad, double altitude_m)
TACAN::search (double frequency_mhz,const SGGeod& pos)
{
int number, i;
_mobile_valid = false;
@ -295,8 +276,7 @@ TACAN::search (double frequency_mhz, double longitude_rad,
_time_before_search_sec = 1.0;
//try any carriers first
FGNavRecord *mobile_tacan
= globals->get_carrierlist()->findStationByFreq( frequency_mhz );
FGNavRecord *mobile_tacan = FGNavList::findByFreq( frequency_mhz, FGNavList::carrierFilter() );
bool freq_valid = (mobile_tacan != NULL);
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 );
if ( loc1 != string::npos && str2 != "" ) {
SG_LOG( SG_INSTR, SG_DEBUG, " string found" );
_mobile_lat = carrier[i]->getDoubleValue("position/latitude-deg");
_mobile_lon = carrier[i]->getDoubleValue("position/longitude-deg");
_mobile_elevation_ft = mobile_tacan->get_elev_ft();
_mobilePos = SGGeod::fromDegFt(
carrier[i]->getDoubleValue("position/longitude-deg"),
carrier[i]->getDoubleValue("position/latitude-deg"),
mobile_tacan->get_elev_ft());
_mobile_range_nm = mobile_tacan->get_range();
_mobile_bias = mobile_tacan->get_multiuse();
_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 );
if ( loc1 != string::npos && str4 != "" ) {
SG_LOG( SG_INSTR, SG_DEBUG, " string found" );
_mobile_lat = tanker[i]->getDoubleValue("position/latitude-deg");
_mobile_lon = tanker[i]->getDoubleValue("position/longitude-deg");
_mobile_elevation_ft = tanker[i]->getDoubleValue("position/altitude-ft");
_mobilePos = SGGeod::fromDegFt(
tanker[i]->getDoubleValue("position/longitude-deg"),
tanker[i]->getDoubleValue("position/latitude-deg"),
tanker[i]->getDoubleValue("position/altitude-ft"));
_mobile_range_nm = mobile_tacan->get_range();
_mobile_bias = mobile_tacan->get_multiuse();
_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 );
if ( loc1 != string::npos && str6 != "" ) {
SG_LOG( SG_INSTR, SG_DEBUG, " string found" );
_mobile_lat = mp_tanker[i]->getDoubleValue("position/latitude-deg");
_mobile_lon = mp_tanker[i]->getDoubleValue("position/longitude-deg");
_mobile_elevation_ft = mp_tanker[i]->getDoubleValue("position/altitude-ft");
_mobilePos = SGGeod::fromDegFt(
mp_tanker[i]->getDoubleValue("position/longitude-deg"),
mp_tanker[i]->getDoubleValue("position/latitude-deg"),
mp_tanker[i]->getDoubleValue("position/altitude-ft"));
_mobile_range_nm = mobile_tacan->get_range();
_mobile_bias = mobile_tacan->get_multiuse();
_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 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);
break;
} else {
@ -420,8 +405,7 @@ TACAN::search (double frequency_mhz, double longitude_rad,
}
// try the TACAN/VORTAC list next
FGNavRecord *tacan = globals->get_tacanlist()->findByFreq( frequency_mhz,
SGGeod::fromRadM(longitude_rad, latitude_rad, altitude_m));
FGNavRecord *tacan = FGNavList::findByFreq( frequency_mhz, pos, FGNavList::tacanFilter() );
_transmitter_valid = (tacan != NULL);

View file

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

View file

@ -65,6 +65,11 @@ source_group("Main\\Headers" FILES ${HEADERS})
source_group("Main\\Sources" FILES ${SOURCES})
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)
#message(STATUS "fg libs ${FG_LIBS}")
#message(STATUS "OSG libs ${OPENSCENEGRAPH_LIBRARIES}")

View file

@ -63,7 +63,6 @@
#include <Aircraft/controls.hxx>
#include <Aircraft/replay.hxx>
#include <Airports/apt_loader.hxx>
#include <Airports/runways.hxx>
#include <Airports/simple.hxx>
#include <Airports/dynamics.hxx>
@ -90,9 +89,6 @@
#include <AIModel/AIManager.hxx>
#include <Navaids/navdb.hxx>
#include <Navaids/navlist.hxx>
#include <Navaids/fix.hxx>
#include <Navaids/fixlist.hxx>
#include <Navaids/airways.hxx>
#include <Scenery/scenery.hxx>
#include <Scenery/tilemgr.hxx>
#include <Scripting/NasalSys.hxx>
@ -107,6 +103,7 @@
#include <Environment/environment_mgr.hxx>
#include <Viewer/renderer.hxx>
#include <Viewer/viewmgr.hxx>
#include <Navaids/NavDataCache.hxx>
#include "fg_init.hxx"
#include "fg_io.hxx"
@ -454,6 +451,366 @@ bool fgInitConfig ( int argc, char **argv )
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
* well as simple airport db list)
@ -463,43 +820,21 @@ fgInitNav ()
{
SG_LOG(SG_GENERAL, SG_INFO, "Loading Airport Database ...");
SGPath aptdb( globals->get_fg_root() );
aptdb.append( "Airports/apt.dat" );
flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
if (cache->isRebuildRequired()) {
SGTimeStamp st;
st.stamp();
cache->rebuild();
SGPath p_metar( globals->get_fg_root() );
p_metar.append( "Airports/metar.dat" );
fgAirportDBLoad( aptdb.str(), p_metar.str() );
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, "rebuilding NavDataCache took:" << st.elapsedMSec());
}
SG_LOG(SG_GENERAL, SG_INFO, " Fixes");
SGPath p_fix( globals->get_fg_root() );
p_fix.append( "Navaids/fix.dat" );
FGFixList fixlist;
fixlist.init( p_fix ); // adds fixes to the DB in positioned.cxx
FGTACANList *channellist = new FGTACANList;
globals->set_channellist( channellist );
SG_LOG(SG_GENERAL, SG_INFO, " Airways");
flightgear::Airway::load();
SGPath path(globals->get_fg_root());
path.append( "Navaids/TACAN_freq.dat" );
flightgear::loadTacan(path, channellist);
return true;
}

View file

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

View file

@ -60,6 +60,7 @@
#include <Viewer/WindowSystemAdapter.hxx>
#include <Viewer/splash.hxx>
#include <Viewer/renderer.hxx>
#include <Navaids/NavDataCache.hxx>
#include "fg_commands.hxx"
#include "fg_io.hxx"
@ -438,5 +439,9 @@ int fgMainInit( int argc, char **argv ) {
delete globals;
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;
}

View file

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

View file

@ -14,6 +14,9 @@ set(SOURCES
waypoint.cxx
LevelDXML.cxx
FlightPlan.cxx
NavDataCache.cxx
sqlite3.c
PositionedOctree.cxx
)
set(HEADERS
@ -30,6 +33,9 @@ set(HEADERS
waypoint.hxx
LevelDXML.hxx
FlightPlan.hxx
NavDataCache.hxx
sqlite3.h
PositionedOctree.hxx
)
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/sg_path.hxx>
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>
#include <Main/globals.hxx>
#include <Navaids/positioned.hxx>
#include <Navaids/waypoint.hxx>
#include <Navaids/NavDataCache.hxx>
using std::make_pair;
using std::string;
using std::set;
using std::vector;
#define DEBUG_AWY_SEARCH 1
//#define DEBUG_AWY_SEARCH 1
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
{
public:
AStarOpenNode(FGPositionedRef aNode, double aLegDist,
Airway* aAirway,
int aAirway,
FGPositionedRef aDest, AStarOpenNode* aPrev) :
node(aNode),
airway(aAirway),
@ -98,7 +74,7 @@ public:
}
FGPositionedRef node;
Airway* airway;
int airway;
SGSharedPtr<AStarOpenNode> previous;
double distanceFromStart; // aka 'g(x)'
double directDistanceToDestination; // aka 'h(x)'
@ -117,11 +93,24 @@ typedef SGSharedPtr<AStarOpenNode> AStarOpenNodeRef;
Airway::Network* Airway::lowLevel()
{
static Network* static_lowLevel = NULL;
if (!static_lowLevel) {
static_lowLevel = new Network;
static_lowLevel->_networkID = 1;
}
return static_lowLevel;
}
Airway::Network* Airway::highLevel()
{
static Network* static_highLevel = NULL;
if (!static_highLevel) {
static_highLevel = new Network;
static_highLevel->_networkID = 2;
}
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;
double latStart, lonStart, latEnd, lonEnd;
int type, base, top;
@ -168,28 +151,22 @@ void Airway::load()
// type = 1; low-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)),
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);
} // 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);
if (it == _airways.end()) {
Airway* awy = new Airway(aName, aTop, aBase);
it = _airways.insert(it, make_pair(aName, awy));
return NavDataCache::instance()->findAirway(_networkID, aName);
}
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 SGGeod& aEndPos, const std::string& aEndIdent)
{
@ -208,17 +185,21 @@ void Airway::Network::addEdge(Airway* aWay, const SGGeod& aStartPos,
return;
}
AdjacentWaypoint* edge = new AdjacentWaypoint(start, end, aWay);
_graph.insert(make_pair(start, edge));
_graph.insert(make_pair(end, edge));
NavDataCache::instance()->insertEdge(_networkID, aWay, start->guid(), end->guid());
}
//////////////////////////////////////////////////////////////////////////////
bool Airway::Network::inNetwork(const FGPositioned* aPos) const
bool Airway::Network::inNetwork(PositionedID posID) const
{
FGPositioned* pos = const_cast<FGPositioned*>(aPos);
return (_graph.find(pos) != _graph.end());
NetworkMembershipDict::iterator it = _inNetworkCache.find(posID);
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,
@ -278,7 +259,7 @@ public:
virtual bool pass(FGPositioned* aPos) const
{
return _net->inNetwork(aPos);
return _net->inNetwork(aPos->guid());
}
virtual FGPositioned::Type minType() const
@ -319,7 +300,7 @@ static void buildWaypoints(AStarOpenNodeRef aNode, WayptVec& aRoute)
// run over the route, creating waypoints
for (n = aNode; n; n=n->previous) {
aRoute[--count] = new NavaidWaypoint(n->node, n->airway);
aRoute[--count] = new NavaidWaypoint(n->node, NULL);
}
}
@ -350,15 +331,13 @@ public:
bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest,
WayptVec& aRoute)
{
typedef set<FGPositioned*> ClosedNodeSet;
typedef std::pair<AdjacencyMap::iterator,AdjacencyMap::iterator> AdjacencyMapRange;
typedef set<PositionedID> ClosedNodeSet;
OpenNodeHeap openNodes;
ClosedNodeSet closedNodes;
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
while (!openNodes.empty()) {
@ -366,9 +345,11 @@ bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest,
AStarOpenNodeRef x = openNodes.back();
FGPositioned* xp = x->node;
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
// node with lower f(x) value.
@ -378,35 +359,40 @@ bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest,
}
// adjacent (neighbour) iteration
AdjacencyMapRange r(_graph.equal_range(xp));
for (; r.first != r.second; ++r.first) {
AdjacentWaypoint* adj(r.first->second);
FGPositioned* yp = adj->other(xp);
if (closedNodes.count(yp)) {
NavDataCache* cache = NavDataCache::instance();
BOOST_FOREACH(AirwayEdge other, cache->airwayEdgesFrom(_networkID, xp->guid())) {
if (closedNodes.count(other.second)) {
continue; // closed, ignore
}
FGPositioned* yp = cache->loadById(other.second);
double edgeDistanceM = SGGeodesy::distanceM(xp->geod(), yp->geod());
AStarOpenNodeRef y = findInOpen(openNodes, yp);
if (y) { // already open
double g = x->distanceFromStart + adj->distanceM();
double g = x->distanceFromStart + edgeDistanceM;
if (g > y->distanceFromStart) {
// worse path, ignore
//SG_LOG(SG_GENERAL, SG_INFO, "\tabandoning " << yp->ident() <<
// " path is worse: g(y)" << y->distanceFromStart << ", g'=" << g);
#ifdef DEBUG_AWY_SEARCH
SG_LOG(SG_GENERAL, SG_INFO, "\tabandoning " << yp->ident() <<
" path is worse: g(y)" << y->distanceFromStart << ", g'=" << g);
#endif
continue;
}
// we need to update y. Unfortunately this means rebuilding the heap,
// 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->distanceFromStart = g;
y->airway = (Airway*) adj->airway;
y->airway = other.first;
std::make_heap(openNodes.begin(), openNodes.end(), ordering);
} else { // not open, insert a new node for y into the heap
y = new AStarOpenNode(yp, adj->distanceM(),
(Airway*) adj->airway, aDest, x);
//SG_LOG(SG_GENERAL, SG_INFO, "\ty=" << yp->ident() << ", f(y)=" << y->totalCost());
y = new AStarOpenNode(yp, edgeDistanceM, other.first, aDest, x);
#ifdef DEBUG_AWY_SEARCH
SG_LOG(SG_GENERAL, SG_INFO, "\ty=" << yp->ident() << ", f(y)=" << y->totalCost());
#endif
openNodes.push_back(y);
std::push_heap(openNodes.begin(), openNodes.end(), ordering);
}
@ -417,38 +403,4 @@ bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest,
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

View file

@ -24,8 +24,9 @@
#include <vector>
#include <Navaids/route.hxx>
#include <Navaids/positioned.hxx>
class FGPositioned;
class SGPath;
typedef SGSharedPtr<FGPositioned> FGPositionedRef;
namespace flightgear {
@ -41,7 +42,7 @@ public:
virtual std::string ident() const
{ return _ident; }
static void load();
static void load(const SGPath& path);
/**
* Track a network of airways
@ -54,7 +55,6 @@ public:
friend class InAirwayFilter;
/**
* Principal routing algorithm. Attempts to find the best route beween
* two points. If either point is part of the airway network (e.g, a SID
@ -65,26 +65,18 @@ public:
*/
bool route(WayptRef aFrom, WayptRef aTo, WayptVec& aPath);
private:
void addEdge(Airway* aWay, const SGGeod& aStartPos,
void addEdge(int aWay, const SGGeod& aStartPos,
const std::string& aStartIdent,
const SGGeod& aEndPos, const std::string& aEndIdent);
Airway* 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;
int findAirway(const std::string& aName, double aTop, double aBase);
bool search2(FGPositionedRef aStart, FGPositionedRef aDest, WayptVec& aRoute);
/**
* 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
@ -103,7 +95,16 @@ public:
/**
* Overloaded version working with a raw SGGeod
*/
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,16 +116,10 @@ private:
friend class Network;
static Network* static_highLevel;
static Network* static_lowLevel;
std::string _ident;
double _topAltitudeFt;
double _bottomAltitudeFt;
// high-level vs low-level flag
// ... ?
WayptVec _elements;
};

View file

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

View file

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

View file

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

View file

@ -24,51 +24,13 @@
#include "config.h"
#endif
#include <simgear/structure/exception.hxx>
#include <simgear/debug/logstream.hxx>
#include <Navaids/markerbeacon.hxx>
#include <Airports/runways.hxx>
#include <Navaids/navdb.hxx>
using std::string;
FGPositioned::Type
FGMarkerBeaconRecord::mapType(int aTy)
{
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),
FGMarkerBeaconRecord::FGMarkerBeaconRecord(PositionedID aGuid, Type aTy,
PositionedID aRunway, const SGGeod& aPos) :
FGPositioned(aGuid, aTy, string(), aPos),
_runway(aRunway)
{
init(true); // init FGPositioned
}

View file

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

View file

@ -24,10 +24,9 @@
# include "config.h"
#endif
#include "navdb.hxx"
#include <simgear/compiler.h>
#include <string>
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/misc/strutils.hxx>
@ -35,24 +34,21 @@
#include <simgear/structure/exception.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/sg_inlines.h>
#include "navrecord.hxx"
#include "navlist.hxx"
#include "navdb.hxx"
#include <Main/globals.hxx>
#include <Navaids/markerbeacon.hxx>
#include <Airports/simple.hxx>
#include <Airports/runways.hxx>
#include <Airports/xmlloader.hxx>
#include <Main/fg_props.hxx>
#include <Navaids/NavDataCache.hxx>
using std::string;
using std::vector;
typedef std::map<FGAirport*, SGPropertyNode_ptr> AirportPropertyMap;
static AirportPropertyMap static_airportIlsData;
static FGPositioned::Type
mapRobinTypeToFGPType(int aTy)
{
@ -63,6 +59,9 @@ mapRobinTypeToFGPType(int aTy)
case 4: return FGPositioned::ILS;
case 5: return FGPositioned::LOC;
case 6: return FGPositioned::GS;
case 7: return FGPositioned::OM;
case 8: return FGPositioned::MM;
case 9: return FGPositioned::IM;
case 12:
case 13: return FGPositioned::DME;
case 99: return FGPositioned::INVALID; // end-of-file code
@ -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;
aStream >> rawType;
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;
@ -88,15 +147,52 @@ static FGNavRecord* createNavFromStream(std::istream& aStream)
SGGeod pos(SGGeod::fromDegFt(lon, lat, elev_ft));
name = simgear::strutils::strip(name);
if ((rawType >= 7) && (rawType <= 9)) {
// marker beacons use a different run-time class now
FGMarkerBeaconRecord::create(rawType, name, pos);
return NULL; // not a nav-record, but that's okay
// the type can be forced by our caller, but normally we use th value
// supplied in the .dat file
if (type == FGPositioned::INVALID) {
type = mapRobinTypeToFGPType(rawType);
}
if (type == FGPositioned::INVALID) {
return 0;
}
FGPositioned::Type type = mapRobinTypeToFGPType(rawType);
if (type == FGPositioned::INVALID) {
return NULL;
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
@ -105,116 +201,67 @@ static FGNavRecord* createNavFromStream(std::istream& aStream)
freq *= 100;
}
return new FGNavRecord(type, ident, name, pos,
freq, range, multiuse);
PositionedID r = cache->insertNavaid(type, ident, name, pos, freq, range, multiuse,
arp.first, arp.second);
if (isLoc) {
cache->setRunwayILS(arp.second, r);
}
return r;
}
// load and initialize the navigational databases
bool fgNavDBInit( FGNavList *navlist, FGNavList *loclist, FGNavList *gslist,
FGNavList *dmelist,
FGNavList *tacanlist, FGNavList *carrierlist,
FGTACANList *channellist)
bool navDBInit(const SGPath& path)
{
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() );
if ( !in.is_open() ) {
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
in >> skipeol;
in >> skipeol;
while (!in.eof()) {
FGNavRecord *r = createNavFromStream(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");
}
readNavFromStream(in);
in >> skipcomment;
} // 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" );
file = path.str();
bool loadCarrierNav(const SGPath& path)
{
SG_LOG( SG_GENERAL, SG_INFO, "opening file: " << path.str() );
sg_gzifstream incarrier( path.str() );
if ( !incarrier.is_open() ) {
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() ) {
FGNavRecord *r = createNavFromStream(incarrier);
if (!r) {
continue;
}
carrierlist->add (r);
// force the type to be MOBILE_TACAN
readNavFromStream(incarrier, FGPositioned::MOBILE_TACAN);
} // end while
// end loading the carrier navaids file
// load the channel/freqency file
string channel, freq;
path="";
path = globals->get_fg_root();
path.append( "Navaids/TACAN_freq.dat" );
return true;
}
bool loadTacan(const SGPath& path, FGTACANList *channellist)
{
SG_LOG( SG_GENERAL, SG_INFO, "opening file: " << path.str() );
sg_gzifstream inchannel( path.str() );
if ( !inchannel.is_open() ) {
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
exit(-1);
return false;
}
// skip first line
@ -223,85 +270,10 @@ bool fgNavDBInit( FGNavList *navlist, FGNavList *loclist, FGNavList *gslist,
FGTACANRecord *r = new FGTACANRecord;
inchannel >> (*r);
channellist->add ( r );
//cout << "channel = " << r->get_channel() ;
//cout << " freq = " << r->get_freq() << endl;
} // 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;
}
SGPropertyNode* ilsDataForRunwayAndNavaid(FGRunway* aRunway, const std::string& aNavIdent)
{
if (!aRunway) {
return NULL;
}
FGAirport* apt = aRunway->airport();
// 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]);
}
} // of namespace flightgear

View file

@ -26,16 +26,23 @@
#include <simgear/compiler.h>
#include <string>
class FGNavList;
// forward decls
class FGTACANList;
class SGPath;
class SGPropertyNode;
class FGRunway;
namespace flightgear
{
// load and initialize the navigational databases
bool fgNavDBInit( FGNavList *navlist, FGNavList *loclist, FGNavList *gslist,
FGNavList *dmelist,
FGNavList *tacanlist, FGNavList *carrierlist,
FGTACANList *channellist );
bool navDBInit(const SGPath& path);
bool loadCarrierNav(const SGPath& path);
bool loadTacan(const SGPath& path, FGTACANList *channellist);
/**
* 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);
/**
* 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);
} // of namespace flightgear
#endif // _FG_NAVDB_HXX

View file

@ -25,6 +25,7 @@
# include <config.h>
#endif
#include <cassert>
#include <boost/foreach.hpp>
#include <algorithm>
@ -35,6 +36,7 @@
#include "navlist.hxx"
#include <Airports/runways.hxx>
#include <Navaids/NavDataCache.hxx>
using std::string;
@ -56,114 +58,10 @@ private:
};
} // of anonymous namespace
// FGNavList ------------------------------------------------------------------
FGNavList::TypeFilter::TypeFilter(const FGPositioned::Type type)
{
if (type == FGPositioned::INVALID) {
_mintype = FGPositioned::VOR;
_maxtype = FGPositioned::GS;
} else {
_mintype = _maxtype = type;
}
}
FGNavList::FGNavList( void )
{
}
FGNavList::~FGNavList( void )
{
nav_list_type navlist = navaids.begin()->second;
navaids.erase( navaids.begin(), navaids.end() );
}
// load the navaids and build the map
bool FGNavList::init()
{
// No need to delete the original navaid structures
// since we're using an SGSharedPointer
nav_list_type navlist = navaids.begin()->second;
navaids.erase( navaids.begin(), navaids.end() );
return true;
}
// add an entry to the lists
bool FGNavList::add( FGNavRecord *n )
{
navaids[n->get_freq()].push_back(n);
return true;
}
FGNavRecord *FGNavList::findByFreq( double freq, const SGGeod& position)
{
const nav_list_type& stations = navaids[(int)(freq*100.0 + 0.5)];
SG_LOG( SG_INSTR, SG_DEBUG, "findbyFreq " << freq << " size " << stations.size() );
return findNavFromList( position, stations );
}
nav_list_type FGNavList::findAllByFreq( double freq, const SGGeod& position, const FGPositioned::Type type)
{
nav_list_type stations;
TypeFilter filter(type);
BOOST_FOREACH(nav_rec_ptr nav, navaids[(int)(freq*100.0 + 0.5)]) {
if (filter.pass(nav.ptr())) {
stations.push_back(nav);
}
}
NavRecordDistanceSortPredicate sortPredicate( position );
std::sort( stations.begin(), stations.end(), sortPredicate );
return stations;
}
// Given an Ident and optional frequency, return the first matching
// 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;
cur = FGPositioned::findNextWithPartialId(cur, ident, &filter);
int f = (int)(freq*100.0 + 0.5);
while (cur) {
FGNavRecord* nav = static_cast<FGNavRecord*>(cur.ptr());
if ( f <= 0.0 || nav->get_freq() == f) {
reply.push_back( nav );
}
cur = FGPositioned::findNextWithPartialId(cur, ident, &filter);
}
return reply;
}
// 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 FGNavList::findByIdentAndFreq( const SGGeod & position,
const std::string& ident, const double freq, const FGPositioned::Type type )
{
nav_list_type reply = findByIdentAndFreq( ident, freq, type );
NavRecordDistanceSortPredicate sortPredicate( position );
std::sort( reply.begin(), reply.end(), sortPredicate );
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)
bool navidUsable(FGNavRecord* aNav, const SGGeod &aircraft)
{
FGRunway* r(aNav->runway());
if (!r || !r->reciprocalRunway()) {
@ -188,48 +86,175 @@ static bool navidUsable(FGNavRecord* aNav, const SGGeod &aircraft)
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 )
} // of anonymous namespace
// FGNavList ------------------------------------------------------------------
FGNavList::TypeFilter::TypeFilter(const FGPositioned::Type type)
{
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;
if (type == FGPositioned::INVALID) {
_mintype = FGPositioned::NDB;
_maxtype = FGPositioned::GS;
} else {
_mintype = _maxtype = type;
}
}
min_dist = d2;
nav = station;
}
return nav;
}
// Given a frequency, return the first matching station.
FGNavRecord *FGNavList::findStationByFreq( double freq )
FGNavList::TypeFilter::TypeFilter(const FGPositioned::Type minType,
const FGPositioned::Type maxType) :
_mintype(minType),
_maxtype(maxType)
{
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];
}
/**
* 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
{
public:
TacanFilter() :
TypeFilter(FGPositioned::DME, FGPositioned::TACAN)
{
}
virtual bool pass(FGPositioned* pos) const
{
if (pos->type() == FGPositioned::TACAN) {
return true;
}
assert(pos->type() == FGPositioned::DME);
string::size_type loc1 = pos->name().find( "TACAN" );
string::size_type loc2 = pos->name().find( "VORTAC" );
return (loc1 != string::npos) || (loc2 != string::npos);
}
};
FGNavList::TypeFilter* FGNavList::locFilter()
{
static TypeFilter tf(FGPositioned::ILS, FGPositioned::LOC);
return &tf;
}
FGNavList::TypeFilter* FGNavList::ndbFilter()
{
static TypeFilter tf(FGPositioned::NDB);
return &tf;
}
FGNavList::TypeFilter* FGNavList::navFilter()
{
static TypeFilter tf(FGPositioned::VOR, FGPositioned::LOC);
return &tf;
}
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));
}
return stations;
}
nav_list_type FGNavList::findByIdentAndFreq(const string& ident, const double freq,
TypeFilter* filter)
{
nav_list_type reply;
int f = (int)(freq*100.0 + 0.5);
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) {
reply.push_back( nav );
}
}
return reply;
}
// Given an Ident and optional frequency and type ,
// return a list of matching stations sorted by distance to the given position
nav_list_type FGNavList::findByIdentAndFreq( const SGGeod & position,
const std::string& ident, const double freq,
TypeFilter* filter)
{
nav_list_type reply = findByIdentAndFreq( ident, freq, filter );
NavRecordDistanceSortPredicate sortPredicate( position );
std::sort( reply.begin(), reply.end(), sortPredicate );
return reply;
}
// FGTACANList ----------------------------------------------------------------

View file

@ -42,62 +42,18 @@ class SGGeod;
typedef SGSharedPtr<FGNavRecord> nav_rec_ptr;
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:
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
{
public:
TypeFilter(const FGPositioned::Type type);
TypeFilter(const FGPositioned::Type minType,
const FGPositioned::Type maxType);
virtual FGPositioned::Type minType() const {
return _mintype;
}
@ -105,15 +61,64 @@ public:
virtual FGPositioned::Type maxType() const {
return _maxtype;
}
private:
protected:
FGPositioned::Type _mintype;
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 ----------------------------------------------------------------

View file

@ -40,117 +40,24 @@
#include <Airports/simple.hxx>
#include <Airports/xmlloader.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,
int aFreq, int aRange, double aMultiuse) :
FGPositioned(aTy, aIdent, aPos),
int aFreq, int aRange, double aMultiuse, PositionedID aRunway) :
FGPositioned(aGuid, aTy, aIdent, aPos),
freq(aFreq),
range(aRange),
multiuse(aMultiuse),
_name(aName),
mRunway(NULL),
mName(aName),
mRunway(aRunway),
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; // 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");
}
return (FGRunway*) flightgear::NavDataCache::instance()->loadById(mRunway);
}
double FGNavRecord::localizerWidth() const
@ -159,9 +66,10 @@ double FGNavRecord::localizerWidth() const
return 6.0;
}
SGVec3d thresholdCart(SGVec3d::fromGeod(mRunway->threshold()));
FGRunway* rwy = runway();
SGVec3d thresholdCart(SGVec3d::fromGeod(rwy->threshold()));
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/
// ICAO standard width at threshold is 210 m = 689 feet = approx 700 feet.

View file

@ -28,10 +28,10 @@
#include "positioned.hxx"
#define FG_NAV_DEFAULT_RANGE 50 // nm
#define FG_LOC_DEFAULT_RANGE 18 // nm
#define FG_DME_DEFAULT_RANGE 50 // nm
#define FG_NAV_MAX_RANGE 300 // nm
const double FG_NAV_DEFAULT_RANGE = 50; // nm
const double FG_LOC_DEFAULT_RANGE = 18; // nm
const double FG_DME_DEFAULT_RANGE = 50; // nm
const double FG_NAV_MAX_RANGE = 300; // nm
// forward decls
class FGRunway;
@ -46,26 +46,18 @@ class FGNavRecord : public FGPositioned
// (degrees) or localizer heading
// (degrees) or dme bias (nm)
std::string _name; // verbose name in nav database
FGRunway* mRunway; // associated runway, if there is one
std::string mName; // verbose name in nav database
PositionedID mRunway; // associated runway, if there is one
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);
public:
inline ~FGNavRecord(void) {}
FGNavRecord(Type type, const std::string& ident, const std::string& name,
FGNavRecord(PositionedID aGuid, Type type, const std::string& ident,
const std::string& name,
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_lat() const { return latitude(); } // degrees
@ -81,12 +73,12 @@ public:
inline const char *get_trans_ident() const { return get_ident(); }
virtual const std::string& name() const
{ return _name; }
{ return mName; }
/**
* 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

View file

@ -36,476 +36,15 @@
#include <osg/Math> // for osg::isNaN
#include <simgear/timing/timestamp.hxx>
#include <simgear/props/props.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/math/SGGeometry.hxx>
#include <simgear/sg_inlines.h>
#include <simgear/structure/commands.hxx>
#include "Airports/simple.hxx"
#include "Main/fg_props.hxx"
#include "Navaids/PositionedOctree.hxx"
typedef std::multimap<std::string, FGPositioned*> NamedPositionedIndex;
typedef std::pair<NamedPositionedIndex::const_iterator, NamedPositionedIndex::const_iterator> NamedIndexRange;
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;
}
using std::string;
using namespace flightgear;
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),
mCart(SGVec3d::fromGeod(mPosition)),
mType(ty),
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()
{
// std::cout << "destroying:" << mIdent << "/" << nameForType(mType) << std::endl;
removeFromIndices(this);
}
FGPositioned*
FGPositioned::createUserWaypoint(const std::string& aIdent, const SGGeod& aPos)
{
FGPositioned* wpt = new FGPositioned(WAYPOINT, aIdent, aPos);
wpt->init(true);
return wpt;
PositionedID id = NavDataCache::instance()->createUserWaypoint(aIdent, aPos);
return NavDataCache::instance()->loadById(id);
}
const SGVec3d&
@ -707,13 +178,21 @@ FGPositionedRef
FGPositioned::findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter)
{
validateSGGeod(aPos);
FGPositioned::List r(findAll(global_identIndex, aIdent, aFilter, true));
if (r.empty()) {
return FGPositionedRef();
return NavDataCache::instance()->findClosestWithIdent(aIdent, aPos, aFilter);
}
FGPositionedRef
FGPositioned::findFirstWithIdent(const std::string& aIdent, Filter* aFilter)
{
if (aIdent.empty()) {
return NULL;
}
List r = NavDataCache::instance()->findAllWithIdent(aIdent, aFilter, true);
if (r.empty()) {
return NULL;
}
sortByRange(r, aPos);
return r.front();
}
@ -731,13 +210,13 @@ FGPositioned::findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilt
FGPositioned::List
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::findAllWithName(const std::string& aName, Filter* aFilter, bool aExact)
{
return findAll(global_nameIndex, aName, aFilter, aExact);
return NavDataCache::instance()->findAllWithName(aName, aFilter, aExact);
}
FGPositionedRef
@ -764,53 +243,6 @@ FGPositioned::findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm
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
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);
}
@ -844,10 +284,11 @@ void FGPositioned::TypeFilter::addType(Type aTy)
{
if (aTy == INVALID) {
return;
}
types.push_back(aTy);
mMinType = std::min(mMinType, aTy);
mMaxType = std::max(mMaxType, aTy);
}
bool

View file

@ -23,15 +23,19 @@
#include <string>
#include <vector>
#include <stdint.h>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/SGMath.hxx>
class FGPositioned;
class SGPropertyNode;
typedef SGSharedPtr<FGPositioned> FGPositionedRef;
typedef int64_t PositionedID;
typedef std::vector<PositionedID> PositionedIDVec;
namespace flightgear { class NavDataCache; }
class FGPositioned : public SGReferenced
{
public:
@ -47,17 +51,24 @@ public:
PARK_STAND,
WAYPOINT,
FIX,
VOR,
NDB,
VOR,
ILS,
LOC,
GS,
OM,
MM,
IM,
/// important that DME & TACAN are adjacent to keep the TacanFilter
/// efficient - DMEs are proxies for TACAN/VORTAC stations
DME,
TACAN,
MOBILE_TACAN,
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_TOWER,
FREQ_ATIS,
@ -90,6 +101,9 @@ public:
const SGGeod& geod() const
{ return mPosition; }
PositionedID guid() const
{ return mGuid; }
/**
* The cartesian position associated with this object
*/
@ -125,15 +139,6 @@ public:
virtual Type maxType() const
{ 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
{ return pass(aPos); }
@ -144,24 +149,24 @@ public:
public:
TypeFilter(Type aTy);
virtual bool pass(FGPositioned* aPos) const;
virtual Type minType() const
{ return mMinType; }
virtual Type maxType() const
{ return mMaxType; }
void addType(Type aTy);
private:
std::vector<Type> types;
Type mMinType, mMaxType;
};
static List findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilter = NULL);
static FGPositionedRef findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, 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);
static FGPositionedRef findFirstWithIdent(const std::string& aIdent, Filter* aFilter = NULL);
/**
* Find all items with the specified ident
@ -214,16 +219,15 @@ public:
static FGPositioned* createUserWaypoint(const std::string& aIdent, const SGGeod& aPos);
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 init(bool aIndexed);
void modifyPosition(const SGGeod& newPos);
// can't be const right now, navrecord at least needs to fix up the position
// after navaids are parsed
SGGeod mPosition;
SGVec3d mCart; // once mPosition is const, this can be const too
const PositionedID mGuid;
const SGGeod mPosition;
const SGVec3d mCart;
const Type mType;
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, "runways")) {
*out = naNewVector(c);
BOOST_FOREACH(FGRunwayPtr rwy, proc->runways()) {
BOOST_FOREACH(FGRunwayRef rwy, proc->runways()) {
naVec_append(*out, stringToNasal(c, rwy->ident()));
}
} else if (!strcmp(fieldName, "transitions")) {
@ -1416,7 +1416,8 @@ static naRef f_navinfo(naContext c, naRef me, int argc, naRef* args)
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);
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]));
}
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()) {
return naNil();
}
@ -1495,7 +1497,9 @@ static naRef f_findNavaidsByFrequency(naContext c, naRef me, int argc, naRef* ar
}
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) {
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]));
}
FGNavList::TypeFilter filter(type);
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) {
naVec_append(r, ghostForNavaid(c, a.ptr()));