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:
parent
48c26079e1
commit
9b900e9430
54 changed files with 149152 additions and 1903 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -37,9 +37,6 @@
|
|||
#include <list>
|
||||
|
||||
class Block;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::list;
|
||||
|
||||
#include "gnnode.hxx"
|
||||
#include "parking.hxx"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -42,10 +42,11 @@ void
|
|||
AirportList::destroy_list()
|
||||
{
|
||||
for (char **c = _content; *c; c++) {
|
||||
delete *c;
|
||||
free(*c);
|
||||
*c = 0;
|
||||
}
|
||||
delete [] _content;
|
||||
|
||||
free(_content);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
1616
src/Navaids/NavDataCache.cxx
Normal file
File diff suppressed because it is too large
Load diff
186
src/Navaids/NavDataCache.hxx
Normal file
186
src/Navaids/NavDataCache.hxx
Normal 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
|
||||
|
286
src/Navaids/PositionedOctree.cxx
Normal file
286
src/Navaids/PositionedOctree.cxx
Normal 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
|
224
src/Navaids/PositionedOctree.hxx
Normal file
224
src/Navaids/PositionedOctree.hxx
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ----------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -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 ----------------------------------------------------------------
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
138243
src/Navaids/sqlite3.c
Normal file
File diff suppressed because it is too large
Load diff
7055
src/Navaids/sqlite3.h
Normal file
7055
src/Navaids/sqlite3.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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()));
|
||||
|
|
Loading…
Reference in a new issue