Core airway and flight-plan enhancements
Add better airway support, fix various issues with VIA, and add parsing / generation of ICAO route strings. Also fix the serialisation of flight-plans with airway enroute segments, so these can be restored correctly.
This commit is contained in:
parent
5c9771fb00
commit
69dbfc0a27
18 changed files with 1315 additions and 323 deletions
|
@ -1,7 +1,7 @@
|
||||||
#ifndef FG_NAVCACHE_SCHEMA_HXX
|
#ifndef FG_NAVCACHE_SCHEMA_HXX
|
||||||
#define FG_NAVCACHE_SCHEMA_HXX
|
#define FG_NAVCACHE_SCHEMA_HXX
|
||||||
|
|
||||||
const int SCHEMA_VERSION = 17;
|
const int SCHEMA_VERSION = 19;
|
||||||
|
|
||||||
#define SCHEMA_SQL \
|
#define SCHEMA_SQL \
|
||||||
"CREATE TABLE properties (key VARCHAR, value VARCHAR);" \
|
"CREATE TABLE properties (key VARCHAR, value VARCHAR);" \
|
||||||
|
@ -31,7 +31,8 @@ const int SCHEMA_VERSION = 17;
|
||||||
"CREATE INDEX airway_ident ON airway(ident);" \
|
"CREATE INDEX airway_ident ON airway(ident);" \
|
||||||
\
|
\
|
||||||
"CREATE TABLE airway_edge (network INT,airway INT64,a INT64,b INT64);" \
|
"CREATE TABLE airway_edge (network INT,airway INT64,a INT64,b INT64);" \
|
||||||
"CREATE INDEX airway_edge_from ON airway_edge(a);"
|
"CREATE INDEX airway_edge_from ON airway_edge(a);" \
|
||||||
|
"CREATE INDEX airway_edge_to ON airway_edge(b);"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,25 @@ using std::vector;
|
||||||
using std::endl;
|
using std::endl;
|
||||||
using std::fstream;
|
using std::fstream;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const string_list static_icaoFlightRulesCode = {
|
||||||
|
"V",
|
||||||
|
"I",
|
||||||
|
"Y",
|
||||||
|
"Z"
|
||||||
|
};
|
||||||
|
|
||||||
|
const string_list static_icaoFlightTypeCode = {
|
||||||
|
"S",
|
||||||
|
"N",
|
||||||
|
"G",
|
||||||
|
"M",
|
||||||
|
"X"
|
||||||
|
};
|
||||||
|
|
||||||
|
} // of anonymous namespace
|
||||||
|
|
||||||
namespace flightgear {
|
namespace flightgear {
|
||||||
|
|
||||||
typedef std::vector<FlightPlan::DelegateFactory*> FPDelegateFactoryVec;
|
typedef std::vector<FlightPlan::DelegateFactory*> FPDelegateFactoryVec;
|
||||||
|
@ -65,15 +84,16 @@ FlightPlan::FlightPlan() :
|
||||||
_currentIndex(-1),
|
_currentIndex(-1),
|
||||||
_followLegTrackToFix(true),
|
_followLegTrackToFix(true),
|
||||||
_aircraftCategory(ICAO_AIRCRAFT_CATEGORY_C),
|
_aircraftCategory(ICAO_AIRCRAFT_CATEGORY_C),
|
||||||
_departureRunway(NULL),
|
_departureRunway(nullptr),
|
||||||
_destinationRunway(NULL),
|
_destinationRunway(nullptr),
|
||||||
_sid(NULL),
|
_sid(nullptr),
|
||||||
_star(NULL),
|
_star(nullptr),
|
||||||
_approach(NULL),
|
_approach(nullptr),
|
||||||
_totalDistance(0.0)
|
_totalDistance(0.0)
|
||||||
{
|
{
|
||||||
_departureChanged = _arrivalChanged = _waypointsChanged = _currentWaypointChanged = false;
|
_departureChanged = _arrivalChanged = _waypointsChanged = _currentWaypointChanged = false;
|
||||||
|
_cruiseDataChanged = false;
|
||||||
|
|
||||||
for (auto factory : static_delegateFactories) {
|
for (auto factory : static_delegateFactories) {
|
||||||
Delegate* d = factory->createFlightPlanDelegate(this);
|
Delegate* d = factory->createFlightPlanDelegate(this);
|
||||||
if (d) { // factory might not always create a delegate
|
if (d) { // factory might not always create a delegate
|
||||||
|
@ -145,7 +165,7 @@ string FlightPlan::ident() const
|
||||||
FlightPlan::Leg* FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex)
|
FlightPlan::Leg* FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex)
|
||||||
{
|
{
|
||||||
if (!aWpt) {
|
if (!aWpt) {
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
WayptVec wps;
|
WayptVec wps;
|
||||||
|
@ -234,7 +254,8 @@ void FlightPlan::clear()
|
||||||
_currentWaypointChanged = true;
|
_currentWaypointChanged = true;
|
||||||
_arrivalChanged = true;
|
_arrivalChanged = true;
|
||||||
_departureChanged = true;
|
_departureChanged = true;
|
||||||
|
_cruiseDataChanged = true;
|
||||||
|
|
||||||
_currentIndex = -1;
|
_currentIndex = -1;
|
||||||
for (Leg* l : _legs) {
|
for (Leg* l : _legs) {
|
||||||
delete l;
|
delete l;
|
||||||
|
@ -382,14 +403,14 @@ int FlightPlan::findWayptIndex(const FGPositionedRef aPos) const
|
||||||
FlightPlan::Leg* FlightPlan::currentLeg() const
|
FlightPlan::Leg* FlightPlan::currentLeg() const
|
||||||
{
|
{
|
||||||
if ((_currentIndex < 0) || (_currentIndex >= numLegs()))
|
if ((_currentIndex < 0) || (_currentIndex >= numLegs()))
|
||||||
return NULL;
|
return nullptr;
|
||||||
return legAtIndex(_currentIndex);
|
return legAtIndex(_currentIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
FlightPlan::Leg* FlightPlan::previousLeg() const
|
FlightPlan::Leg* FlightPlan::previousLeg() const
|
||||||
{
|
{
|
||||||
if (_currentIndex <= 0) {
|
if (_currentIndex <= 0) {
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return legAtIndex(_currentIndex - 1);
|
return legAtIndex(_currentIndex - 1);
|
||||||
|
@ -398,7 +419,7 @@ FlightPlan::Leg* FlightPlan::previousLeg() const
|
||||||
FlightPlan::Leg* FlightPlan::nextLeg() const
|
FlightPlan::Leg* FlightPlan::nextLeg() const
|
||||||
{
|
{
|
||||||
if ((_currentIndex < 0) || ((_currentIndex + 1) >= numLegs())) {
|
if ((_currentIndex < 0) || ((_currentIndex + 1) >= numLegs())) {
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return legAtIndex(_currentIndex + 1);
|
return legAtIndex(_currentIndex + 1);
|
||||||
|
@ -481,14 +502,14 @@ void FlightPlan::setSID(SID* sid, const std::string& transition)
|
||||||
void FlightPlan::setSID(Transition* trans)
|
void FlightPlan::setSID(Transition* trans)
|
||||||
{
|
{
|
||||||
if (!trans) {
|
if (!trans) {
|
||||||
setSID((SID*) NULL);
|
setSID(static_cast<SID*>(nullptr));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trans->parent()->type() != PROCEDURE_SID)
|
if (trans->parent()->type() != PROCEDURE_SID)
|
||||||
throw sg_exception("FlightPlan::setSID: transition does not belong to a SID");
|
throw sg_exception("FlightPlan::setSID: transition does not belong to a SID");
|
||||||
|
|
||||||
setSID((SID*) trans->parent(), trans->ident());
|
setSID(static_cast<SID*>(trans->parent()), trans->ident());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlightPlan::clearSID()
|
void FlightPlan::clearSID()
|
||||||
|
@ -503,7 +524,7 @@ void FlightPlan::clearSID()
|
||||||
Transition* FlightPlan::sidTransition() const
|
Transition* FlightPlan::sidTransition() const
|
||||||
{
|
{
|
||||||
if (!_sid || _sidTransition.empty()) {
|
if (!_sid || _sidTransition.empty()) {
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _sid->findTransitionByName(_sidTransition);
|
return _sid->findTransitionByName(_sidTransition);
|
||||||
|
@ -552,6 +573,19 @@ void FlightPlan::clearDestination()
|
||||||
unlockDelegates();
|
unlockDelegates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FGAirportRef FlightPlan::alternate() const
|
||||||
|
{
|
||||||
|
return _alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightPlan::setAlternate(FGAirportRef alt)
|
||||||
|
{
|
||||||
|
lockDelegates();
|
||||||
|
_alternate = alt;
|
||||||
|
_arrivalChanged = true;
|
||||||
|
unlockDelegates();
|
||||||
|
}
|
||||||
|
|
||||||
void FlightPlan::setSTAR(STAR* star, const std::string& transition)
|
void FlightPlan::setSTAR(STAR* star, const std::string& transition)
|
||||||
{
|
{
|
||||||
if (_star == star) {
|
if (_star == star) {
|
||||||
|
@ -587,11 +621,31 @@ void FlightPlan::clearSTAR()
|
||||||
_starTransition.clear();
|
_starTransition.clear();
|
||||||
unlockDelegates();
|
unlockDelegates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlightPlan::setEstimatedDurationMinutes(int mins)
|
||||||
|
{
|
||||||
|
_estimatedDuration = mins;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightPlan::computeDurationMinutes()
|
||||||
|
{
|
||||||
|
if ((_cruiseAirspeedMach < 0.01) && (_cruiseAirspeedKnots < 10)) {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, "can't compute duration, no cruise speed set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_cruiseAltitudeFt < 100) && (_cruiseFlightLevel < 10)) {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, "can't compute duration, no cruise altitude set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Transition* FlightPlan::starTransition() const
|
Transition* FlightPlan::starTransition() const
|
||||||
{
|
{
|
||||||
if (!_star || _starTransition.empty()) {
|
if (!_star || _starTransition.empty()) {
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _star->findTransitionByName(_starTransition);
|
return _star->findTransitionByName(_starTransition);
|
||||||
|
@ -618,48 +672,25 @@ void FlightPlan::setApproach(flightgear::Approach *app)
|
||||||
}
|
}
|
||||||
unlockDelegates();
|
unlockDelegates();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlightPlan::save(const SGPath& path)
|
bool FlightPlan::save(std::ostream& stream) const
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
SGPropertyNode_ptr d(new SGPropertyNode);
|
||||||
|
saveToProperties(d);
|
||||||
|
writeProperties(stream, d, true);
|
||||||
|
return true;
|
||||||
|
} catch (sg_exception& e) {
|
||||||
|
SG_LOG(SG_NAVAID, SG_ALERT, "Failed to save flight-plan " << e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlightPlan::save(const SGPath& path) const
|
||||||
{
|
{
|
||||||
SG_LOG(SG_NAVAID, SG_INFO, "Saving route to " << path);
|
|
||||||
try {
|
try {
|
||||||
SGPropertyNode_ptr d(new SGPropertyNode);
|
SGPropertyNode_ptr d(new SGPropertyNode);
|
||||||
d->setIntValue("version", 2);
|
saveToProperties(d);
|
||||||
|
|
||||||
if (_departure) {
|
|
||||||
d->setStringValue("departure/airport", _departure->ident());
|
|
||||||
if (_sid) {
|
|
||||||
d->setStringValue("departure/sid", _sid->ident());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_departureRunway) {
|
|
||||||
d->setStringValue("departure/runway", _departureRunway->ident());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_destination) {
|
|
||||||
d->setStringValue("destination/airport", _destination->ident());
|
|
||||||
if (_star) {
|
|
||||||
d->setStringValue("destination/star", _star->ident());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_approach) {
|
|
||||||
d->setStringValue("destination/approach", _approach->ident());
|
|
||||||
}
|
|
||||||
|
|
||||||
//d->setStringValue("destination/transition", destination->getStringValue("transition"));
|
|
||||||
|
|
||||||
if (_destinationRunway) {
|
|
||||||
d->setStringValue("destination/runway", _destinationRunway->ident());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// route nodes
|
|
||||||
SGPropertyNode* routeNode = d->getChild("route", 0, true);
|
|
||||||
for (unsigned int i=0; i<_legs.size(); ++i) {
|
|
||||||
Waypt* wpt = _legs[i]->waypoint();
|
|
||||||
wpt->saveAsNode(routeNode->getChild("wp", i, true));
|
|
||||||
} // of waypoint iteration
|
|
||||||
writeProperties(path, d, true /* write-all */);
|
writeProperties(path, d, true /* write-all */);
|
||||||
return true;
|
return true;
|
||||||
} catch (sg_exception& e) {
|
} catch (sg_exception& e) {
|
||||||
|
@ -668,6 +699,83 @@ bool FlightPlan::save(const SGPath& path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlightPlan::saveToProperties(SGPropertyNode* d) const
|
||||||
|
{
|
||||||
|
d->setIntValue("version", 2);
|
||||||
|
|
||||||
|
// general data
|
||||||
|
d->setStringValue("flight-rules", static_icaoFlightRulesCode[static_cast<int>(_flightRules)]);
|
||||||
|
d->setStringValue("flight-type", static_icaoFlightTypeCode[static_cast<int>(_flightType)]);
|
||||||
|
if (!_callsign.empty()) {
|
||||||
|
d->setStringValue("callsign", _callsign);
|
||||||
|
}
|
||||||
|
if (!_remarks.empty()) {
|
||||||
|
d->setStringValue("remarks", _remarks);
|
||||||
|
}
|
||||||
|
if (!_aircraftType.empty()) {
|
||||||
|
d->setStringValue("aircraft-type", _aircraftType);
|
||||||
|
}
|
||||||
|
d->setIntValue("estimated-duration-minutes", _estimatedDuration);
|
||||||
|
|
||||||
|
if (_departure) {
|
||||||
|
d->setStringValue("departure/airport", _departure->ident());
|
||||||
|
if (_sid) {
|
||||||
|
if (!_sidTransition.empty()) {
|
||||||
|
d->setStringValue("departure/sid", _sidTransition);
|
||||||
|
} else {
|
||||||
|
d->setStringValue("departure/sid", _sid->ident());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_departureRunway) {
|
||||||
|
d->setStringValue("departure/runway", _departureRunway->ident());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_destination) {
|
||||||
|
d->setStringValue("destination/airport", _destination->ident());
|
||||||
|
if (_star) {
|
||||||
|
if (!_starTransition.empty()) {
|
||||||
|
d->setStringValue("destination/star", _starTransition);
|
||||||
|
} else {
|
||||||
|
d->setStringValue("destination/star", _star->ident());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_approach) {
|
||||||
|
d->setStringValue("destination/approach", _approach->ident());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_destinationRunway) {
|
||||||
|
d->setStringValue("destination/runway", _destinationRunway->ident());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_alternate) {
|
||||||
|
d->setStringValue("alternate", _alternate->ident());
|
||||||
|
}
|
||||||
|
|
||||||
|
// cruise data
|
||||||
|
if (_cruiseFlightLevel > 0) {
|
||||||
|
d->setIntValue("cruise/flight-level", _cruiseFlightLevel);
|
||||||
|
} else if (_cruiseAltitudeFt > 0) {
|
||||||
|
d->setIntValue("cruise/altitude-ft", _cruiseAltitudeFt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cruiseAirspeedMach > 0.0) {
|
||||||
|
d->setDoubleValue("cruise/mach", _cruiseAirspeedMach);
|
||||||
|
} else if (_cruiseAirspeedKnots > 0) {
|
||||||
|
d->setIntValue("cruise/knots", _cruiseAirspeedKnots);
|
||||||
|
}
|
||||||
|
|
||||||
|
// route nodes
|
||||||
|
SGPropertyNode* routeNode = d->getChild("route", 0, true);
|
||||||
|
for (unsigned int i=0; i<_legs.size(); ++i) {
|
||||||
|
Waypt* wpt = _legs[i]->waypoint();
|
||||||
|
wpt->saveAsNode(routeNode->getChild("wp", i, true));
|
||||||
|
} // of waypoint iteration
|
||||||
|
}
|
||||||
|
|
||||||
bool FlightPlan::load(const SGPath& path)
|
bool FlightPlan::load(const SGPath& path)
|
||||||
{
|
{
|
||||||
if (!path.exists())
|
if (!path.exists())
|
||||||
|
@ -700,12 +808,51 @@ bool FlightPlan::load(const SGPath& path)
|
||||||
// mark data as unchanged since this is a clean plan
|
// mark data as unchanged since this is a clean plan
|
||||||
_arrivalChanged = false;
|
_arrivalChanged = false;
|
||||||
_departureChanged = false;
|
_departureChanged = false;
|
||||||
|
_cruiseDataChanged = true;
|
||||||
_waypointsChanged = true;
|
_waypointsChanged = true;
|
||||||
unlockDelegates();
|
unlockDelegates();
|
||||||
|
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FlightPlan::load(std::istream &stream)
|
||||||
|
{
|
||||||
|
SGPropertyNode_ptr routeData(new SGPropertyNode);
|
||||||
|
try {
|
||||||
|
readProperties(stream, routeData);
|
||||||
|
} catch (sg_exception& e) {
|
||||||
|
SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin()
|
||||||
|
<< "'. " << e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!routeData.valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool Status = false;
|
||||||
|
lockDelegates();
|
||||||
|
try {
|
||||||
|
int version = routeData->getIntValue("version", 1);
|
||||||
|
if (version == 2) {
|
||||||
|
loadVersion2XMLRoute(routeData);
|
||||||
|
Status = true;
|
||||||
|
} else {
|
||||||
|
throw sg_io_exception("unsupported XML route version");
|
||||||
|
}
|
||||||
|
} catch (sg_exception& e) {
|
||||||
|
SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin()
|
||||||
|
<< "'. " << e.getMessage());
|
||||||
|
Status = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_arrivalChanged = false;
|
||||||
|
_departureChanged = false;
|
||||||
|
_cruiseDataChanged = true;
|
||||||
|
_waypointsChanged = true;
|
||||||
|
unlockDelegates();
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
/** XML loader for GPX file format */
|
/** XML loader for GPX file format */
|
||||||
class GpxXmlVisitor : public XMLVisitor
|
class GpxXmlVisitor : public XMLVisitor
|
||||||
|
@ -835,6 +982,23 @@ bool FlightPlan::loadXmlFormat(const SGPath& path)
|
||||||
|
|
||||||
void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData)
|
void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData)
|
||||||
{
|
{
|
||||||
|
// general info
|
||||||
|
|
||||||
|
const auto rules = routeData->getStringValue("flight-rules", "V");
|
||||||
|
auto it = std::find(static_icaoFlightRulesCode.begin(),
|
||||||
|
static_icaoFlightRulesCode.end(), rules);
|
||||||
|
_flightRules = static_cast<ICAOFlightRules>(std::distance(static_icaoFlightRulesCode.begin(), it));
|
||||||
|
|
||||||
|
const auto type = routeData->getStringValue("flight-type", "X");
|
||||||
|
auto it2 = std::find(static_icaoFlightTypeCode.begin(),
|
||||||
|
static_icaoFlightTypeCode.end(), type);
|
||||||
|
_flightType = static_cast<ICAOFlightType>(std::distance(static_icaoFlightTypeCode.begin(), it2));
|
||||||
|
|
||||||
|
_callsign = routeData->getStringValue("callsign");
|
||||||
|
_remarks = routeData->getStringValue("remarks");
|
||||||
|
_aircraftType = routeData->getStringValue("aircraft-type");
|
||||||
|
_estimatedDuration = routeData->getIntValue("estimated-duration-minutes");
|
||||||
|
|
||||||
// departure nodes
|
// departure nodes
|
||||||
SGPropertyNode* dep = routeData->getChild("departure");
|
SGPropertyNode* dep = routeData->getChild("departure");
|
||||||
if (dep) {
|
if (dep) {
|
||||||
|
@ -847,9 +1011,14 @@ void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dep->hasChild("sid")) {
|
if (dep->hasChild("sid")) {
|
||||||
setSID(_departure->findSIDWithIdent(dep->getStringValue("sid")));
|
const auto sid = dep->getStringValue("sid");
|
||||||
|
auto trans = _departure->selectSIDByTransition(sid);
|
||||||
|
if (trans) {
|
||||||
|
setSID(trans);
|
||||||
|
} else {
|
||||||
|
setSID(_departure->findSIDWithIdent(sid));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// departure->setStringValue("transition", dep->getStringValue("transition"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -864,31 +1033,41 @@ void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dst->hasChild("star")) {
|
if (dst->hasChild("star")) {
|
||||||
setSTAR(_destination->findSTARWithIdent(dst->getStringValue("star")));
|
const auto star = dst->getStringValue("star");
|
||||||
|
auto trans = _destination->selectSTARByTransition(star);
|
||||||
|
if (trans) {
|
||||||
|
setSTAR(trans);
|
||||||
|
} else {
|
||||||
|
setSTAR(_destination->findSTARWithIdent(star));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dst->hasChild("approach")) {
|
if (dst->hasChild("approach")) {
|
||||||
setApproach(_destination->findApproachWithIdent(dst->getStringValue("approach")));
|
setApproach(_destination->findApproachWithIdent(dst->getStringValue("approach")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// destination->setStringValue("transition", dst->getStringValue("transition"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// alternate
|
// alternate
|
||||||
SGPropertyNode* alt = routeData->getChild("alternate");
|
if (routeData->hasChild("alternate")) {
|
||||||
if (alt) {
|
setAlternate((FGAirport*) fgFindAirportID(routeData->getStringValue("alternate")));
|
||||||
//alternate->setStringValue(alt->getStringValue("airport"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// cruise
|
// cruise
|
||||||
SGPropertyNode* crs = routeData->getChild("cruise");
|
SGPropertyNode* crs = routeData->getChild("cruise");
|
||||||
if (crs) {
|
if (crs) {
|
||||||
// cruise->setDoubleValue("speed-kts", crs->getDoubleValue("speed-kts"));
|
if (crs->hasChild("flight-level")) {
|
||||||
// cruise->setDoubleValue("mach", crs->getDoubleValue("mach"));
|
_cruiseFlightLevel = crs->getIntValue("flight-level");
|
||||||
// cruise->setDoubleValue("altitude-ft", crs->getDoubleValue("altitude-ft"));
|
} else if (crs->hasChild("altitude-ft")) {
|
||||||
|
_cruiseAltitudeFt = crs->getIntValue("altitude-ft");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crs->hasChild("mach")) {
|
||||||
|
_cruiseAirspeedMach = crs->getDoubleValue("mach");
|
||||||
|
} else if (crs->hasChild("knots")) {
|
||||||
|
_cruiseAirspeedKnots = crs->getIntValue("knots");
|
||||||
|
}
|
||||||
} // of cruise data loading
|
} // of cruise data loading
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData)
|
void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData)
|
||||||
|
@ -901,7 +1080,7 @@ void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData)
|
||||||
if (routeNode.valid()) {
|
if (routeNode.valid()) {
|
||||||
for (int i=0; i<routeNode->nChildren(); ++i) {
|
for (int i=0; i<routeNode->nChildren(); ++i) {
|
||||||
SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i);
|
SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i);
|
||||||
Leg* l = new Leg(this, Waypt::createFromProperties(NULL, wpNode));
|
Leg* l = new Leg(this, Waypt::createFromProperties(this, wpNode));
|
||||||
_legs.push_back(l);
|
_legs.push_back(l);
|
||||||
} // of route iteration
|
} // of route iteration
|
||||||
}
|
}
|
||||||
|
@ -937,7 +1116,7 @@ WayptRef FlightPlan::parseVersion1XMLWaypt(SGPropertyNode* aWP)
|
||||||
if (aWP->hasChild("longitude-deg")) {
|
if (aWP->hasChild("longitude-deg")) {
|
||||||
// explicit longitude/latitude
|
// explicit longitude/latitude
|
||||||
w = new BasicWaypt(SGGeod::fromDeg(aWP->getDoubleValue("longitude-deg"),
|
w = new BasicWaypt(SGGeod::fromDeg(aWP->getDoubleValue("longitude-deg"),
|
||||||
aWP->getDoubleValue("latitude-deg")), ident, NULL);
|
aWP->getDoubleValue("latitude-deg")), ident, this);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
string nid = aWP->getStringValue("navid", ident.c_str());
|
string nid = aWP->getStringValue("navid", ident.c_str());
|
||||||
|
@ -961,7 +1140,7 @@ WayptRef FlightPlan::parseVersion1XMLWaypt(SGPropertyNode* aWP)
|
||||||
SGGeodesy::direct(pos, radialDeg, offsetNm * SG_NM_TO_METER, pos, az2);
|
SGGeodesy::direct(pos, radialDeg, offsetNm * SG_NM_TO_METER, pos, az2);
|
||||||
}
|
}
|
||||||
|
|
||||||
w = new BasicWaypt(pos, ident, NULL);
|
w = new BasicWaypt(pos, ident, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
double altFt = aWP->getDoubleValue("altitude-ft", -9999.9);
|
double altFt = aWP->getDoubleValue("altitude-ft", -9999.9);
|
||||||
|
@ -1031,7 +1210,7 @@ WayptRef intersectionFromString(FGPositionedRef p1,
|
||||||
FGPositionedRef p2 = FGPositioned::findClosestWithIdent(pieces[2], basePosition);
|
FGPositionedRef p2 = FGPositioned::findClosestWithIdent(pieces[2], basePosition);
|
||||||
if (!p2) {
|
if (!p2) {
|
||||||
SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces[2]);
|
SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces[2]);
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
double r1 = atof(pieces[1].c_str()),
|
double r1 = atof(pieces[1].c_str()),
|
||||||
|
@ -1048,7 +1227,7 @@ WayptRef intersectionFromString(FGPositionedRef p1,
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string name = p1->ident() + "-" + p2->ident();
|
std::string name = p1->ident() + "-" + p2->ident();
|
||||||
return new BasicWaypt(intersection, name, NULL);
|
return new BasicWaypt(intersection, name, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
WayptRef wayptFromLonLatString(const std::string& target)
|
WayptRef wayptFromLonLatString(const std::string& target)
|
||||||
|
@ -1077,25 +1256,24 @@ WayptRef viaFromString(const SGGeod& basePosition, const std::string& target)
|
||||||
string_list pieces(simgear::strutils::split(target, "/"));
|
string_list pieces(simgear::strutils::split(target, "/"));
|
||||||
if ((pieces.size() != 4) || (pieces[2] != "TO")) {
|
if ((pieces.size() != 4) || (pieces[2] != "TO")) {
|
||||||
SG_LOG( SG_NAVAID, SG_WARN, "Malformed VIA specification string:" << target);
|
SG_LOG( SG_NAVAID, SG_WARN, "Malformed VIA specification string:" << target);
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// airway ident is pieces[1]
|
|
||||||
Airway* airway = Airway::findByIdent(pieces[1]);
|
|
||||||
if (airway == NULL) {
|
|
||||||
SG_LOG( SG_NAVAID, SG_WARN, "Unknown airway:" << pieces[1]);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TO navaid is pieces[3]
|
// TO navaid is pieces[3]
|
||||||
FGPositionedRef nav = FGPositioned::findClosestWithIdent(pieces[3], basePosition, NULL);
|
FGPositionedRef nav = FGPositioned::findClosestWithIdent(pieces[3], basePosition, nullptr);
|
||||||
if (!nav || !airway->containsNavaid(nav)) {
|
if (!nav) {
|
||||||
SG_LOG( SG_NAVAID, SG_WARN, "TO navaid:" << pieces[3] << " unknown or not on airway");
|
SG_LOG( SG_NAVAID, SG_WARN, "TO navaid:" << pieces[3] << " unknown");
|
||||||
return NULL;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// airway ident is pieces[1]
|
||||||
|
Airway* airway = Airway::findByIdentAndNavaid(pieces[1], nav);
|
||||||
|
if (airway == nullptr) {
|
||||||
|
SG_LOG( SG_NAVAID, SG_WARN, "Unknown airway:" << pieces[1]);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Via* via = new Via(NULL, pieces[1], nav);
|
return new Via(nullptr, pieces[1], nav);
|
||||||
return via;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // of anonymous namespace
|
} // of anonymous namespace
|
||||||
|
@ -1138,31 +1316,31 @@ WayptRef FlightPlan::waypointFromString(const string& tgt )
|
||||||
FGPositionedRef p = FGPositioned::findClosestWithIdent(pieces.front(), basePosition);
|
FGPositionedRef p = FGPositioned::findClosestWithIdent(pieces.front(), basePosition);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front());
|
SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front());
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pieces.size() == 1) {
|
if (pieces.size() == 1) {
|
||||||
wpt = new NavaidWaypoint(p, NULL);
|
wpt = new NavaidWaypoint(p, this);
|
||||||
} else if (pieces.size() == 3) {
|
} else if (pieces.size() == 3) {
|
||||||
// navaid/radial/distance-nm notation
|
// navaid/radial/distance-nm notation
|
||||||
double radial = atof(pieces[1].c_str()),
|
double radial = atof(pieces[1].c_str()),
|
||||||
distanceNm = atof(pieces[2].c_str());
|
distanceNm = atof(pieces[2].c_str());
|
||||||
radial += magvar;
|
radial += magvar;
|
||||||
wpt = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm);
|
wpt = new OffsetNavaidWaypoint(p, this, radial, distanceNm);
|
||||||
} else if (pieces.size() == 2) {
|
} else if (pieces.size() == 2) {
|
||||||
FGAirport* apt = dynamic_cast<FGAirport*>(p.ptr());
|
FGAirport* apt = dynamic_cast<FGAirport*>(p.ptr());
|
||||||
if (!apt) {
|
if (!apt) {
|
||||||
SG_LOG(SG_NAVAID, SG_INFO, "Waypoint is not an airport:" << pieces.front());
|
SG_LOG(SG_NAVAID, SG_INFO, "Waypoint is not an airport:" << pieces.front());
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!apt->hasRunwayWithIdent(pieces[1])) {
|
if (!apt->hasRunwayWithIdent(pieces[1])) {
|
||||||
SG_LOG(SG_NAVAID, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]);
|
SG_LOG(SG_NAVAID, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]);
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
FGRunway* runway = apt->getRunwayByIdent(pieces[1]);
|
FGRunway* runway = apt->getRunwayByIdent(pieces[1]);
|
||||||
wpt = new NavaidWaypoint(runway, NULL);
|
wpt = new NavaidWaypoint(runway, this);
|
||||||
} else if (pieces.size() == 4) {
|
} else if (pieces.size() == 4) {
|
||||||
wpt = intersectionFromString(p, basePosition, magvar, pieces);
|
wpt = intersectionFromString(p, basePosition, magvar, pieces);
|
||||||
}
|
}
|
||||||
|
@ -1170,7 +1348,7 @@ WayptRef FlightPlan::waypointFromString(const string& tgt )
|
||||||
|
|
||||||
if (!wpt) {
|
if (!wpt) {
|
||||||
SG_LOG(SG_NAVAID, SG_INFO, "Unable to parse waypoint:" << target);
|
SG_LOG(SG_NAVAID, SG_INFO, "Unable to parse waypoint:" << target);
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (altSetting != RESTRICT_NONE) {
|
if (altSetting != RESTRICT_NONE) {
|
||||||
|
@ -1185,7 +1363,7 @@ void FlightPlan::activate()
|
||||||
FGRouteMgr* routeManager = globals->get_subsystem<FGRouteMgr>();
|
FGRouteMgr* routeManager = globals->get_subsystem<FGRouteMgr>();
|
||||||
if (routeManager) {
|
if (routeManager) {
|
||||||
if (routeManager->flightPlan() != this) {
|
if (routeManager->flightPlan() != this) {
|
||||||
SG_LOG(SG_NAVAID, SG_INFO, "setting new flight-plan on route-manager");
|
SG_LOG(SG_NAVAID, SG_DEBUG, "setting new flight-plan on route-manager");
|
||||||
routeManager->setFlightPlan(this);
|
routeManager->setFlightPlan(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1208,7 +1386,7 @@ void FlightPlan::activate()
|
||||||
_legs.erase(it);
|
_legs.erase(it);
|
||||||
delete l;
|
delete l;
|
||||||
|
|
||||||
// create new lefs and insert
|
// create new legs and insert
|
||||||
it = _legs.begin();
|
it = _legs.begin();
|
||||||
it += i;
|
it += i;
|
||||||
|
|
||||||
|
@ -1233,14 +1411,11 @@ void FlightPlan::activate()
|
||||||
|
|
||||||
FlightPlan::Leg::Leg(FlightPlan* owner, WayptRef wpt) :
|
FlightPlan::Leg::Leg(FlightPlan* owner, WayptRef wpt) :
|
||||||
_parent(owner),
|
_parent(owner),
|
||||||
_speedRestrict(RESTRICT_NONE),
|
|
||||||
_altRestrict(RESTRICT_NONE),
|
|
||||||
_waypt(wpt)
|
_waypt(wpt)
|
||||||
{
|
{
|
||||||
if (!wpt.valid()) {
|
if (!wpt.valid()) {
|
||||||
throw sg_exception("can't create FlightPlan::Leg without underlying waypoint");
|
throw sg_exception("can't create FlightPlan::Leg without underlying waypoint");
|
||||||
}
|
}
|
||||||
_speed = _altitudeFt = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FlightPlan::Leg* FlightPlan::Leg::cloneFor(FlightPlan* owner) const
|
FlightPlan::Leg* FlightPlan::Leg::cloneFor(FlightPlan* owner) const
|
||||||
|
@ -1409,6 +1584,13 @@ void FlightPlan::unlockDelegates()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_cruiseDataChanged) {
|
||||||
|
_cruiseDataChanged = false;
|
||||||
|
for (auto d : _delegates) {
|
||||||
|
d->cruiseChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_waypointsChanged) {
|
if (_waypointsChanged) {
|
||||||
_waypointsChanged = false;
|
_waypointsChanged = false;
|
||||||
rebuildLegData();
|
rebuildLegData();
|
||||||
|
@ -1508,5 +1690,283 @@ void FlightPlan::setIcaoAircraftCategory(const std::string& cat)
|
||||||
_aircraftCategory = cat[0];
|
_aircraftCategory = cat[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlightPlan::setIcaoAircraftType(const string &ty)
|
||||||
|
{
|
||||||
|
_aircraftType = ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlightPlan::parseICAOLatLon(const std::string& s, SGGeod& p)
|
||||||
|
{
|
||||||
|
if (s.size() == 7) {
|
||||||
|
double latDegrees = std::stoi(s);
|
||||||
|
double lonDegrees = std::stoi(s.substr(3, 3));
|
||||||
|
if (s[2] == 'S') latDegrees = -latDegrees;
|
||||||
|
if (s[6] == 'W') lonDegrees = -lonDegrees;
|
||||||
|
p = SGGeod::fromDeg(lonDegrees, latDegrees);
|
||||||
|
return true;
|
||||||
|
} else if (s.size() == 11) {
|
||||||
|
// problem here is we have minutes, not decimal degrees
|
||||||
|
double latDegrees = std::stoi(s.substr(0, 2));
|
||||||
|
double latMinutes = std::stoi(s.substr(2, 2));
|
||||||
|
latDegrees += (latMinutes / 60.0);
|
||||||
|
double lonDegrees = std::stoi(s.substr(5, 3));
|
||||||
|
double lonMinutes = std::stoi(s.substr(8, 2));
|
||||||
|
lonDegrees += (lonMinutes / 60.0);
|
||||||
|
|
||||||
|
if (s[4] == 'S') latDegrees = -latDegrees;
|
||||||
|
if (s[10] == 'W') lonDegrees = -lonDegrees;
|
||||||
|
p = SGGeod::fromDeg(lonDegrees, latDegrees);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlightPlan::parseICAORouteString(const std::string& routeData)
|
||||||
|
{
|
||||||
|
auto tokens = simgear::strutils::split(routeData);
|
||||||
|
if (tokens.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string tk;
|
||||||
|
std::string nextToken;
|
||||||
|
|
||||||
|
FGAirportRef firstICAO = FGAirport::findByIdent(tokens.front());
|
||||||
|
unsigned int i = 0;
|
||||||
|
|
||||||
|
if (firstICAO) {
|
||||||
|
// route string starts with an airport, let's see if it matches
|
||||||
|
// our departure airport
|
||||||
|
if (!_departure) {
|
||||||
|
setDeparture(firstICAO);
|
||||||
|
} else if (_departure != firstICAO) {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, "ICAO route begins with an airprot which is not the departure airport:" << tokens.front());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++i; // either way, skip the ICAO token now
|
||||||
|
}
|
||||||
|
|
||||||
|
WayptVec enroute;
|
||||||
|
SGGeod currentPos;
|
||||||
|
if (_departure)
|
||||||
|
currentPos = _departure->geod();
|
||||||
|
|
||||||
|
for (; i < tokens.size(); ++i) {
|
||||||
|
tk = tokens.at(i);
|
||||||
|
// update current position for next search
|
||||||
|
if (!enroute.empty()) {
|
||||||
|
currentPos = enroute.back()->position();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isdigit(tk.front())) {
|
||||||
|
// might be a lat lon (but some airway idents also start with a digit...)
|
||||||
|
SGGeod geod;
|
||||||
|
bool ok = parseICAOLatLon(tk, geod);
|
||||||
|
if (ok) {
|
||||||
|
enroute.push_back(new BasicWaypt(geod, tk, this));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextToken = (i < (tokens.size() - 1)) ? tokens.at(i+1) : std::string();
|
||||||
|
|
||||||
|
if (tk == "DCT") {
|
||||||
|
if (nextToken.empty()) {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, "ICAO route DIRECT segment missing waypoint");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FGPositionedRef wpt = FGPositioned::findClosestWithIdent(nextToken, currentPos);
|
||||||
|
if (!wpt) {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, "ICAO route waypoint not found:" << nextToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
enroute.push_back(new NavaidWaypoint(wpt, this));
|
||||||
|
++i;
|
||||||
|
} else if (tk == "STAR") {
|
||||||
|
// look for a STAR based on the preceeding transition point
|
||||||
|
auto starTrans = _destination->selectSTARByEnrouteTransition(enroute.back()->source());
|
||||||
|
if (!starTrans) {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, "ICAO route couldn't find STAR transitioning from " <<
|
||||||
|
enroute.back()->source()->ident());
|
||||||
|
} else {
|
||||||
|
setSTAR(starTrans);
|
||||||
|
}
|
||||||
|
} else if (tk == "SID") {
|
||||||
|
// select a SID based on the next point
|
||||||
|
FGPositionedRef wpt = FGPositioned::findClosestWithIdent(nextToken, currentPos);
|
||||||
|
auto sidTrans = _departure->selectSIDByEnrouteTransition(wpt);
|
||||||
|
if (!sidTrans) {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, "ICAO route couldn't find SID transitioning to " << nextToken);
|
||||||
|
} else {
|
||||||
|
setSID(sidTrans);
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
if (nextToken.empty()) {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, "ICAO route airway segment missing transition:" << tk);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FGPositionedRef nav = FGPositioned::findClosestWithIdent(nextToken, currentPos);
|
||||||
|
if (!nav) {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, "ICAO route waypoint not found:" << nextToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Airway* way = Airway::findByIdentAndNavaid(tk, nav);
|
||||||
|
if (way) {
|
||||||
|
enroute.push_back(new Via(this, tk, nav));
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, "ICAO route unknown airway:" << tk);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // of token iteration
|
||||||
|
|
||||||
|
lockDelegates();
|
||||||
|
_waypointsChanged = true;
|
||||||
|
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_INFO, "adding waypoints from string");
|
||||||
|
// rebuild legs from waypoints we created
|
||||||
|
for (auto l : _legs) {
|
||||||
|
delete l;
|
||||||
|
}
|
||||||
|
_legs.clear();
|
||||||
|
insertWayptsAtIndex(enroute, 0);
|
||||||
|
|
||||||
|
unlockDelegates();
|
||||||
|
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_INFO, "legs now:" << numLegs());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FlightPlan::asICAORouteString() const
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
if (!_sidTransition.empty())
|
||||||
|
result += _sidTransition + " ";
|
||||||
|
|
||||||
|
std::string currentAirwayIdent;
|
||||||
|
for (auto l : _legs) {
|
||||||
|
const auto wpt = l->waypoint();
|
||||||
|
if (wpt->flag(WPT_GENERATED)) {
|
||||||
|
if (wpt->flag(WPT_VIA)) {
|
||||||
|
AirwayRef awy = static_cast<Airway*>(wpt->owner());
|
||||||
|
// SG_LOG(SG_AUTOPILOT, SG_INFO, "via " << awy->ident() << " -> " << wpt->icaoDescription());
|
||||||
|
assert(awy);
|
||||||
|
if (currentAirwayIdent != awy->ident()) {
|
||||||
|
if (!currentAirwayIdent.empty()) {
|
||||||
|
result += currentAirwayIdent + " " + wpt->icaoDescription() + " ";
|
||||||
|
}
|
||||||
|
currentAirwayIdent = awy->ident();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (!currentAirwayIdent.empty()) {
|
||||||
|
// completed an airway segment, ensure we insert the last piece
|
||||||
|
result += currentAirwayIdent + " ";
|
||||||
|
currentAirwayIdent.clear();
|
||||||
|
} else if (wpt->type() == "navaid") {
|
||||||
|
// technically we need DCT unless both ends of the leg are
|
||||||
|
// defined geographically
|
||||||
|
result += "DCT ";
|
||||||
|
}
|
||||||
|
result += wpt->icaoDescription() + " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_starTransition.empty())
|
||||||
|
result += _starTransition;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightPlan::setFlightRules(ICAOFlightRules rules)
|
||||||
|
{
|
||||||
|
_flightRules = rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
ICAOFlightRules FlightPlan::flightRules() const
|
||||||
|
{
|
||||||
|
return _flightRules;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightPlan::setCallsign(const std::string& callsign)
|
||||||
|
{
|
||||||
|
_callsign = callsign;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightPlan::setRemarks(const std::string& remarks)
|
||||||
|
{
|
||||||
|
_remarks = remarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightPlan::setFlightType(ICAOFlightType type)
|
||||||
|
{
|
||||||
|
_flightType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
ICAOFlightType FlightPlan::flightType() const
|
||||||
|
{
|
||||||
|
return _flightType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightPlan::setCruiseSpeedKnots(int kts)
|
||||||
|
{
|
||||||
|
lockDelegates();
|
||||||
|
_cruiseAirspeedKnots = kts;
|
||||||
|
_cruiseAirspeedMach = 0.0;
|
||||||
|
_cruiseDataChanged = true;
|
||||||
|
unlockDelegates();
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlightPlan::cruiseSpeedKnots() const
|
||||||
|
{
|
||||||
|
return _cruiseAirspeedKnots;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightPlan::setCruiseSpeedMach(double mach)
|
||||||
|
{
|
||||||
|
lockDelegates();
|
||||||
|
_cruiseDataChanged = true;
|
||||||
|
_cruiseAirspeedMach = mach;
|
||||||
|
_cruiseAirspeedKnots = 0;
|
||||||
|
unlockDelegates();
|
||||||
|
}
|
||||||
|
|
||||||
|
double FlightPlan::cruiseSpeedMach() const
|
||||||
|
{
|
||||||
|
return _cruiseAirspeedMach;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightPlan::setCruiseFlightLevel(int flightLevel)
|
||||||
|
{
|
||||||
|
lockDelegates();
|
||||||
|
_cruiseDataChanged = true;
|
||||||
|
_cruiseFlightLevel = flightLevel;
|
||||||
|
_cruiseAltitudeFt = 0;
|
||||||
|
unlockDelegates();
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlightPlan::cruiseFlightLevel() const
|
||||||
|
{
|
||||||
|
return _cruiseFlightLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightPlan::setCruiseAltitudeFt(int altFt)
|
||||||
|
{
|
||||||
|
lockDelegates();
|
||||||
|
_cruiseDataChanged = true;
|
||||||
|
_cruiseAltitudeFt = altFt;
|
||||||
|
_cruiseFlightLevel = 0;
|
||||||
|
unlockDelegates();
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlightPlan::cruiseAltitudeFt() const
|
||||||
|
{
|
||||||
|
return _cruiseAltitudeFt;
|
||||||
|
}
|
||||||
|
|
||||||
} // of namespace flightgear
|
} // of namespace flightgear
|
||||||
|
|
|
@ -35,6 +35,23 @@ class FlightPlan;
|
||||||
|
|
||||||
typedef SGSharedPtr<FlightPlan> FlightPlanRef;
|
typedef SGSharedPtr<FlightPlan> FlightPlanRef;
|
||||||
|
|
||||||
|
enum class ICAOFlightRules
|
||||||
|
{
|
||||||
|
VFR = 0,
|
||||||
|
IFR,
|
||||||
|
IFR_VFR, // type Y
|
||||||
|
VFR_IFR // type Z
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ICAOFlightType
|
||||||
|
{
|
||||||
|
Scheduled = 0,
|
||||||
|
NonScheduled,
|
||||||
|
GeneralAviation,
|
||||||
|
Military,
|
||||||
|
Other // type X
|
||||||
|
};
|
||||||
|
|
||||||
class FlightPlan : public RouteBase
|
class FlightPlan : public RouteBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -53,7 +70,12 @@ public:
|
||||||
std::string icaoAircraftCategory() const;
|
std::string icaoAircraftCategory() const;
|
||||||
void setIcaoAircraftCategory(const std::string& cat);
|
void setIcaoAircraftCategory(const std::string& cat);
|
||||||
|
|
||||||
FlightPlan* clone(const std::string& newIdent = std::string()) const;
|
std::string icaoAircraftType() const
|
||||||
|
{ return _aircraftType; }
|
||||||
|
|
||||||
|
void setIcaoAircraftType(const std::string& ty);
|
||||||
|
|
||||||
|
FlightPlan* clone(const std::string& newIdent = std::string()) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* flight-plan leg encapsulation
|
* flight-plan leg encapsulation
|
||||||
|
@ -62,7 +84,7 @@ public:
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FlightPlan* owner() const
|
FlightPlan* owner() const
|
||||||
{ return _parent; }
|
{ return const_cast<FlightPlan*>(_parent); }
|
||||||
|
|
||||||
Waypt* waypoint() const
|
Waypt* waypoint() const
|
||||||
{ return _waypt; }
|
{ return _waypt; }
|
||||||
|
@ -87,6 +109,7 @@ public:
|
||||||
double courseDeg() const;
|
double courseDeg() const;
|
||||||
double distanceNm() const;
|
double distanceNm() const;
|
||||||
double distanceAlongRoute() const;
|
double distanceAlongRoute() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class FlightPlan;
|
friend class FlightPlan;
|
||||||
|
|
||||||
|
@ -94,16 +117,18 @@ public:
|
||||||
|
|
||||||
Leg* cloneFor(FlightPlan* owner) const;
|
Leg* cloneFor(FlightPlan* owner) const;
|
||||||
|
|
||||||
FlightPlan* _parent;
|
const FlightPlan* _parent;
|
||||||
RouteRestriction _speedRestrict, _altRestrict;
|
RouteRestriction _speedRestrict = RESTRICT_NONE,
|
||||||
int _speed;
|
_altRestrict = RESTRICT_NONE;
|
||||||
int _altitudeFt;
|
int _speed = 0;
|
||||||
|
int _altitudeFt = 0;
|
||||||
|
|
||||||
WayptRef _waypt;
|
WayptRef _waypt;
|
||||||
/// length of this leg following the flown path
|
/// length of this leg following the flown path
|
||||||
mutable double _pathDistance;
|
mutable double _pathDistance = -1.0;
|
||||||
mutable double _courseDeg;
|
mutable double _courseDeg = -1.0;
|
||||||
/// total distance of this leg from departure point
|
/// total distance of this leg from departure point
|
||||||
mutable double _distanceAlongPath;
|
mutable double _distanceAlongPath = 11.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Delegate
|
class Delegate
|
||||||
|
@ -114,6 +139,7 @@ public:
|
||||||
virtual void departureChanged() { }
|
virtual void departureChanged() { }
|
||||||
virtual void arrivalChanged() { }
|
virtual void arrivalChanged() { }
|
||||||
virtual void waypointsChanged() { }
|
virtual void waypointsChanged() { }
|
||||||
|
virtual void cruiseChanged() { }
|
||||||
virtual void cleared() { }
|
virtual void cleared() { }
|
||||||
virtual void activated() { }
|
virtual void activated() { }
|
||||||
virtual void currentWaypointChanged() { }
|
virtual void currentWaypointChanged() { }
|
||||||
|
@ -150,7 +176,7 @@ public:
|
||||||
Leg* previousLeg() const;
|
Leg* previousLeg() const;
|
||||||
|
|
||||||
int numLegs() const
|
int numLegs() const
|
||||||
{ return _legs.size(); }
|
{ return static_cast<int>(_legs.size()); }
|
||||||
|
|
||||||
Leg* legAtIndex(int index) const;
|
Leg* legAtIndex(int index) const;
|
||||||
int findLegIndex(const Leg* l) const;
|
int findLegIndex(const Leg* l) const;
|
||||||
|
@ -159,7 +185,10 @@ public:
|
||||||
int findWayptIndex(const FGPositionedRef aPos) const;
|
int findWayptIndex(const FGPositionedRef aPos) const;
|
||||||
|
|
||||||
bool load(const SGPath& p);
|
bool load(const SGPath& p);
|
||||||
bool save(const SGPath& p);
|
bool save(const SGPath& p) const;
|
||||||
|
|
||||||
|
bool save(std::ostream& stream) const;
|
||||||
|
bool load(std::istream& stream);
|
||||||
|
|
||||||
FGAirportRef departureAirport() const
|
FGAirportRef departureAirport() const
|
||||||
{ return _departure; }
|
{ return _departure; }
|
||||||
|
@ -197,6 +226,9 @@ public:
|
||||||
|
|
||||||
void clearDestination();
|
void clearDestination();
|
||||||
|
|
||||||
|
FGAirportRef alternate() const;
|
||||||
|
void setAlternate(FGAirportRef alt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* note setting an approach will implicitly update the destination
|
* note setting an approach will implicitly update the destination
|
||||||
* airport and runway to match
|
* airport and runway to match
|
||||||
|
@ -217,6 +249,17 @@ public:
|
||||||
double totalDistanceNm() const
|
double totalDistanceNm() const
|
||||||
{ return _totalDistance; }
|
{ return _totalDistance; }
|
||||||
|
|
||||||
|
int estimatedDurationMinutes() const
|
||||||
|
{ return _estimatedDuration; }
|
||||||
|
|
||||||
|
void setEstimatedDurationMinutes(int minutes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief computeDurationMinutes - use performance data and cruise data
|
||||||
|
* to estimate enroute time
|
||||||
|
*/
|
||||||
|
void computeDurationMinutes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* given a waypoint index, and an offset in NM, find the geodetic
|
* given a waypoint index, and an offset in NM, find the geodetic
|
||||||
* position on the route path. I.e the point 10nm before or after
|
* position on the route path. I.e the point 10nm before or after
|
||||||
|
@ -233,6 +276,45 @@ public:
|
||||||
*/
|
*/
|
||||||
WayptRef waypointFromString(const std::string& target);
|
WayptRef waypointFromString(const std::string& target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* attempt to replace the route waypoints (and potentially the SID and
|
||||||
|
* STAR) based on an ICAO standard route string, i.e item 15.
|
||||||
|
* Returns true if the rotue was parsed successfully (and this flight
|
||||||
|
* plan modified accordingly) or false if the string could not be
|
||||||
|
* parsed.
|
||||||
|
*/
|
||||||
|
bool parseICAORouteString(const std::string& routeData);
|
||||||
|
|
||||||
|
std::string asICAORouteString() const;
|
||||||
|
|
||||||
|
// ICAO flight-plan data
|
||||||
|
void setFlightRules(ICAOFlightRules rules);
|
||||||
|
ICAOFlightRules flightRules() const;
|
||||||
|
|
||||||
|
void setFlightType(ICAOFlightType type);
|
||||||
|
ICAOFlightType flightType() const;
|
||||||
|
|
||||||
|
void setCallsign(const std::string& callsign);
|
||||||
|
std::string callsign() const
|
||||||
|
{ return _callsign; }
|
||||||
|
|
||||||
|
void setRemarks(const std::string& remarks);
|
||||||
|
std::string remarks() const
|
||||||
|
{ return _remarks; }
|
||||||
|
|
||||||
|
// cruise data
|
||||||
|
void setCruiseSpeedKnots(int kts);
|
||||||
|
int cruiseSpeedKnots() const;
|
||||||
|
|
||||||
|
void setCruiseSpeedMach(double mach);
|
||||||
|
double cruiseSpeedMach() const;
|
||||||
|
|
||||||
|
void setCruiseFlightLevel(int flightLevel);
|
||||||
|
int cruiseFlightLevel() const;
|
||||||
|
|
||||||
|
void setCruiseAltitudeFt(int altFt);
|
||||||
|
int cruiseAltitudeFt() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* abstract interface for creating delegates automatically when a
|
* abstract interface for creating delegates automatically when a
|
||||||
* flight-plan is created or loaded
|
* flight-plan is created or loaded
|
||||||
|
@ -258,8 +340,11 @@ private:
|
||||||
bool _arrivalChanged,
|
bool _arrivalChanged,
|
||||||
_departureChanged,
|
_departureChanged,
|
||||||
_waypointsChanged,
|
_waypointsChanged,
|
||||||
_currentWaypointChanged;
|
_currentWaypointChanged,
|
||||||
|
_cruiseDataChanged;
|
||||||
|
|
||||||
|
void saveToProperties(SGPropertyNode* d) const;
|
||||||
|
|
||||||
bool loadXmlFormat(const SGPath& path);
|
bool loadXmlFormat(const SGPath& path);
|
||||||
bool loadGpxFormat(const SGPath& path);
|
bool loadGpxFormat(const SGPath& path);
|
||||||
bool loadPlainTextFormat(const SGPath& path);
|
bool loadPlainTextFormat(const SGPath& path);
|
||||||
|
@ -270,13 +355,26 @@ private:
|
||||||
WayptRef parseVersion1XMLWaypt(SGPropertyNode* aWP);
|
WayptRef parseVersion1XMLWaypt(SGPropertyNode* aWP);
|
||||||
|
|
||||||
double magvarDegAt(const SGGeod& pos) const;
|
double magvarDegAt(const SGGeod& pos) const;
|
||||||
|
bool parseICAOLatLon(const std::string &s, SGGeod &p);
|
||||||
|
|
||||||
std::string _ident;
|
std::string _ident;
|
||||||
|
std::string _callsign;
|
||||||
|
std::string _remarks;
|
||||||
|
std::string _aircraftType;
|
||||||
|
|
||||||
int _currentIndex;
|
int _currentIndex;
|
||||||
bool _followLegTrackToFix;
|
bool _followLegTrackToFix;
|
||||||
char _aircraftCategory;
|
char _aircraftCategory;
|
||||||
|
ICAOFlightType _flightType = ICAOFlightType::Other;
|
||||||
|
ICAOFlightRules _flightRules = ICAOFlightRules::VFR;
|
||||||
|
int _cruiseAirspeedKnots = 0;
|
||||||
|
double _cruiseAirspeedMach = 0.0;
|
||||||
|
int _cruiseFlightLevel = 0;
|
||||||
|
int _cruiseAltitudeFt = 0;
|
||||||
|
int _estimatedDuration = 0;
|
||||||
|
|
||||||
FGAirportRef _departure, _destination;
|
FGAirportRef _departure, _destination;
|
||||||
|
FGAirportRef _alternate;
|
||||||
FGRunway* _departureRunway, *_destinationRunway;
|
FGRunway* _departureRunway, *_destinationRunway;
|
||||||
SGSharedPtr<SID> _sid;
|
SGSharedPtr<SID> _sid;
|
||||||
SGSharedPtr<STAR> _star;
|
SGSharedPtr<STAR> _star;
|
||||||
|
@ -289,7 +387,7 @@ private:
|
||||||
typedef std::vector<Leg*> LegVec;
|
typedef std::vector<Leg*> LegVec;
|
||||||
LegVec _legs;
|
LegVec _legs;
|
||||||
|
|
||||||
std::vector<Delegate*> _delegates;
|
std::vector<Delegate*> _delegates;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // of namespace flightgear
|
} // of namespace flightgear
|
||||||
|
|
|
@ -19,9 +19,7 @@
|
||||||
// along with this program; if not, write to the Free Software
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#include "config.h"
|
||||||
# include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "NavDataCache.hxx"
|
#include "NavDataCache.hxx"
|
||||||
|
|
||||||
|
@ -31,9 +29,6 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <stdint.h> // for int64_t
|
#include <stdint.h> // for int64_t
|
||||||
#include <sstream> // for std::ostringstream
|
#include <sstream> // for std::ostringstream
|
||||||
// boost
|
|
||||||
#include <boost/foreach.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef SYSTEM_SQLITE
|
#ifdef SYSTEM_SQLITE
|
||||||
// the standard sqlite3.h doesn't give a way to set SQLITE_UINT64_TYPE,
|
// the standard sqlite3.h doesn't give a way to set SQLITE_UINT64_TYPE,
|
||||||
|
@ -79,6 +74,7 @@
|
||||||
#include <Airports/gnnode.hxx>
|
#include <Airports/gnnode.hxx>
|
||||||
#include "CacheSchema.h"
|
#include "CacheSchema.h"
|
||||||
#include <GUI/MessageBox.hxx>
|
#include <GUI/MessageBox.hxx>
|
||||||
|
#include <Navaids/airways.hxx>
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
|
@ -241,7 +237,7 @@ class NavDataCache::NavDataCachePrivate
|
||||||
public:
|
public:
|
||||||
NavDataCachePrivate(const SGPath& p, NavDataCache* o) :
|
NavDataCachePrivate(const SGPath& p, NavDataCache* o) :
|
||||||
outer(o),
|
outer(o),
|
||||||
db(NULL),
|
db(nullptr),
|
||||||
path(p),
|
path(p),
|
||||||
readOnly(false),
|
readOnly(false),
|
||||||
cacheHits(0),
|
cacheHits(0),
|
||||||
|
@ -323,7 +319,7 @@ public:
|
||||||
|
|
||||||
void close()
|
void close()
|
||||||
{
|
{
|
||||||
BOOST_FOREACH(sqlite3_stmt_ptr stmt, prepared) {
|
for (sqlite3_stmt_ptr stmt : prepared) {
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
prepared.clear();
|
prepared.clear();
|
||||||
|
@ -487,7 +483,7 @@ public:
|
||||||
void initTables()
|
void initTables()
|
||||||
{
|
{
|
||||||
string_list commands = simgear::strutils::split(SCHEMA_SQL, ";");
|
string_list commands = simgear::strutils::split(SCHEMA_SQL, ";");
|
||||||
BOOST_FOREACH(std::string sql, commands) {
|
for (std::string sql : commands) {
|
||||||
if (sql.empty()) {
|
if (sql.empty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -614,17 +610,19 @@ public:
|
||||||
sqlite3_bind_int(findILS, 5, FGPositioned::LOC);
|
sqlite3_bind_int(findILS, 5, FGPositioned::LOC);
|
||||||
|
|
||||||
// airways
|
// airways
|
||||||
findAirway = prepare("SELECT rowid FROM airway WHERE network=?1 AND ident=?2");
|
findAirwayNet = prepare("SELECT rowid FROM airway WHERE network=?1 AND ident=?2");
|
||||||
|
findAirway = prepare("SELECT rowid FROM airway WHERE ident=?1");
|
||||||
|
loadAirway = prepare("SELECT ident, network FROM airway WHERE rowid=?1");
|
||||||
insertAirway = prepare("INSERT INTO airway (ident, network) "
|
insertAirway = prepare("INSERT INTO airway (ident, network) "
|
||||||
"VALUES (?1, ?2)");
|
"VALUES (?1, ?2)");
|
||||||
|
|
||||||
insertAirwayEdge = prepare("INSERT INTO airway_edge (network, airway, a, b) "
|
insertAirwayEdge = prepare("INSERT INTO airway_edge (network, airway, a, b) "
|
||||||
"VALUES (?1, ?2, ?3, ?4)");
|
"VALUES (?1, ?2, ?3, ?4)");
|
||||||
|
|
||||||
isPosInAirway = prepare("SELECT rowid FROM airway_edge WHERE network=?1 AND a=?2");
|
isPosInAirway = prepare("SELECT rowid FROM airway_edge WHERE network=?1 AND (a=?2 OR b=?2)");
|
||||||
|
|
||||||
airwayEdgesFrom = prepare("SELECT airway, b FROM airway_edge WHERE network=?1 AND a=?2");
|
airwayEdgesFrom = prepare("SELECT airway, b FROM airway_edge WHERE network=?1 AND a=?2");
|
||||||
|
airwayEdgesTo = prepare("SELECT airway, a FROM airway_edge WHERE network=?1 AND b=?2");
|
||||||
airwayEdges = prepare("SELECT a, b FROM airway_edge WHERE airway=?1");
|
airwayEdges = prepare("SELECT a, b FROM airway_edge WHERE airway=?1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -836,7 +834,7 @@ public:
|
||||||
|
|
||||||
void flushDeferredOctreeUpdates()
|
void flushDeferredOctreeUpdates()
|
||||||
{
|
{
|
||||||
BOOST_FOREACH(Octree::Branch* nd, deferredOctreeUpdates) {
|
for (Octree::Branch* nd : deferredOctreeUpdates) {
|
||||||
sqlite3_bind_int64(updateOctreeChildren, 1, nd->guid());
|
sqlite3_bind_int64(updateOctreeChildren, 1, nd->guid());
|
||||||
sqlite3_bind_int(updateOctreeChildren, 2, nd->childMask());
|
sqlite3_bind_int(updateOctreeChildren, 2, nd->childMask());
|
||||||
execUpdate(updateOctreeChildren);
|
execUpdate(updateOctreeChildren);
|
||||||
|
@ -902,9 +900,10 @@ public:
|
||||||
sqlite3_stmt_ptr runwayLengthFtQuery;
|
sqlite3_stmt_ptr runwayLengthFtQuery;
|
||||||
|
|
||||||
// airways
|
// airways
|
||||||
sqlite3_stmt_ptr findAirway, insertAirwayEdge,
|
sqlite3_stmt_ptr findAirway, findAirwayNet, insertAirwayEdge,
|
||||||
isPosInAirway, airwayEdgesFrom,
|
isPosInAirway, airwayEdgesFrom, airwayEdgesTo,
|
||||||
insertAirway, airwayEdges;
|
insertAirway, airwayEdges;
|
||||||
|
sqlite3_stmt_ptr loadAirway;
|
||||||
|
|
||||||
// since there's many permutations of ident/name queries, we create
|
// since there's many permutations of ident/name queries, we create
|
||||||
// them programtically, but cache the exact query by its raw SQL once
|
// them programtically, but cache the exact query by its raw SQL once
|
||||||
|
@ -1071,13 +1070,12 @@ void NavDataCache::NavDataCachePrivate::findDatFiles(
|
||||||
const PathList files = simgear::Dir(datFilesDir).children(
|
const PathList files = simgear::Dir(datFilesDir).children(
|
||||||
simgear::Dir::TYPE_FILE | simgear::Dir::NO_DOT_OR_DOTDOT);
|
simgear::Dir::TYPE_FILE | simgear::Dir::NO_DOT_OR_DOTDOT);
|
||||||
|
|
||||||
for (PathList::const_iterator filesIt = files.begin();
|
for (auto f : files) {
|
||||||
filesIt != files.end(); filesIt++) {
|
const std::string name = f.file();
|
||||||
const std::string name = filesIt->file();
|
|
||||||
if (simgear::strutils::ends_with(name, ".dat") ||
|
if (simgear::strutils::ends_with(name, ".dat") ||
|
||||||
simgear::strutils::ends_with(name, ".dat.gz")) {
|
simgear::strutils::ends_with(name, ".dat.gz")) {
|
||||||
result.paths.push_back(*filesIt);
|
result.paths.push_back(f);
|
||||||
result.totalSize += filesIt->sizeInBytes();
|
result.totalSize += f.sizeInBytes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1484,7 +1482,7 @@ void NavDataCache::doRebuild()
|
||||||
stampCacheFile(d->carrierDatPath);
|
stampCacheFile(d->carrierDatPath);
|
||||||
|
|
||||||
st.stamp();
|
st.stamp();
|
||||||
Airway::load(d->airwayDatPath);
|
Airway::loadAWYDat(d->airwayDatPath);
|
||||||
stampCacheFile(d->airwayDatPath);
|
stampCacheFile(d->airwayDatPath);
|
||||||
SG_LOG(SG_NAVCACHE, SG_INFO, "awy.dat load took:" << st.elapsedMSec());
|
SG_LOG(SG_NAVCACHE, SG_INFO, "awy.dat load took:" << st.elapsedMSec());
|
||||||
|
|
||||||
|
@ -1591,7 +1589,7 @@ void NavDataCache::writeStringListProperty(const string& key, const string_list&
|
||||||
sqlite_bind_stdstring(d->clearProperty, 1, key);
|
sqlite_bind_stdstring(d->clearProperty, 1, key);
|
||||||
d->execUpdate(d->clearProperty);
|
d->execUpdate(d->clearProperty);
|
||||||
|
|
||||||
BOOST_FOREACH(string value, values) {
|
for (string value : values) {
|
||||||
sqlite_bind_stdstring(d->writePropertyMulti, 1, key);
|
sqlite_bind_stdstring(d->writePropertyMulti, 1, key);
|
||||||
sqlite_bind_stdstring(d->writePropertyMulti, 2, value);
|
sqlite_bind_stdstring(d->writePropertyMulti, 2, value);
|
||||||
d->execInsert(d->writePropertyMulti);
|
d->execInsert(d->writePropertyMulti);
|
||||||
|
@ -2223,37 +2221,50 @@ NavDataCache::findILS(PositionedID airport, const string& aRunway, const string&
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int NavDataCache::findAirway(int network, const string& aName)
|
int NavDataCache::findAirway(int network, const string& aName, bool create)
|
||||||
{
|
{
|
||||||
sqlite3_bind_int(d->findAirway, 1, network);
|
sqlite3_bind_int(d->findAirwayNet, 1, network);
|
||||||
sqlite_bind_stdstring(d->findAirway, 2, aName);
|
sqlite_bind_stdstring(d->findAirwayNet, 2, aName);
|
||||||
|
|
||||||
|
int airway = 0;
|
||||||
|
if (d->execSelect(d->findAirwayNet)) {
|
||||||
|
// already exists
|
||||||
|
airway = sqlite3_column_int(d->findAirwayNet, 0);
|
||||||
|
} else if (create) {
|
||||||
|
d->reset(d->insertAirway);
|
||||||
|
sqlite_bind_stdstring(d->insertAirway, 1, aName);
|
||||||
|
sqlite3_bind_int(d->insertAirway, 2, network);
|
||||||
|
airway = d->execInsert(d->insertAirway);
|
||||||
|
} else {
|
||||||
|
// doesn't exist but don't create
|
||||||
|
}
|
||||||
|
|
||||||
|
d->reset(d->findAirwayNet);
|
||||||
|
return airway;
|
||||||
|
}
|
||||||
|
|
||||||
|
int NavDataCache::findAirway(const string& aName)
|
||||||
|
{
|
||||||
|
sqlite_bind_stdstring(d->findAirway, 1, aName);
|
||||||
|
|
||||||
int airway = 0;
|
int airway = 0;
|
||||||
if (d->execSelect(d->findAirway)) {
|
if (d->execSelect(d->findAirway)) {
|
||||||
// already exists
|
// already exists
|
||||||
airway = sqlite3_column_int(d->findAirway, 0);
|
airway = sqlite3_column_int(d->findAirway, 0);
|
||||||
} else {
|
|
||||||
sqlite_bind_stdstring(d->insertAirway, 1, aName);
|
|
||||||
sqlite3_bind_int(d->insertAirway, 2, network);
|
|
||||||
airway = d->execInsert(d->insertAirway);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d->reset(d->findAirway);
|
d->reset(d->findAirway);
|
||||||
return airway;
|
return airway;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void NavDataCache::insertEdge(int network, int airwayID, PositionedID from, PositionedID to)
|
void NavDataCache::insertEdge(int network, int airwayID, PositionedID from, PositionedID to)
|
||||||
{
|
{
|
||||||
// assume all edges are bidirectional for the moment
|
|
||||||
for (int i=0; i<2; ++i) {
|
|
||||||
sqlite3_bind_int(d->insertAirwayEdge, 1, network);
|
sqlite3_bind_int(d->insertAirwayEdge, 1, network);
|
||||||
sqlite3_bind_int(d->insertAirwayEdge, 2, airwayID);
|
sqlite3_bind_int(d->insertAirwayEdge, 2, airwayID);
|
||||||
sqlite3_bind_int64(d->insertAirwayEdge, 3, from);
|
sqlite3_bind_int64(d->insertAirwayEdge, 3, from);
|
||||||
sqlite3_bind_int64(d->insertAirwayEdge, 4, to);
|
sqlite3_bind_int64(d->insertAirwayEdge, 4, to);
|
||||||
d->execInsert(d->insertAirwayEdge);
|
d->execInsert(d->insertAirwayEdge);
|
||||||
|
|
||||||
std::swap(from, to);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NavDataCache::isInAirwayNetwork(int network, PositionedID pos)
|
bool NavDataCache::isInAirwayNetwork(int network, PositionedID pos)
|
||||||
|
@ -2280,11 +2291,41 @@ AirwayEdgeVec NavDataCache::airwayEdgesFrom(int network, PositionedID pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
d->reset(d->airwayEdgesFrom);
|
d->reset(d->airwayEdgesFrom);
|
||||||
|
|
||||||
|
// find bidirectional / backwsards edges
|
||||||
|
// at present all edges are bidirectional
|
||||||
|
sqlite3_bind_int(d->airwayEdgesTo, 1, network);
|
||||||
|
sqlite3_bind_int64(d->airwayEdgesTo, 2, pos);
|
||||||
|
|
||||||
|
while (d->stepSelect(d->airwayEdgesTo)) {
|
||||||
|
result.push_back(AirwayEdge(
|
||||||
|
sqlite3_column_int(d->airwayEdgesTo, 0),
|
||||||
|
sqlite3_column_int64(d->airwayEdgesTo, 1)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
d->reset(d->airwayEdgesTo);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AirwayRef NavDataCache::loadAirway(int airwayID)
|
||||||
|
{
|
||||||
|
sqlite3_bind_int(d->loadAirway, 1, airwayID);
|
||||||
|
bool ok = d->execSelect(d->loadAirway);
|
||||||
|
AirwayRef result;
|
||||||
|
if (ok) {
|
||||||
|
string ident = (char*) sqlite3_column_text(d->loadAirway, 0);
|
||||||
|
Airway::Level network = static_cast<Airway::Level>(sqlite3_column_int(d->loadAirway, 1));
|
||||||
|
result = new Airway(ident, network, airwayID, 0, 0);
|
||||||
|
}
|
||||||
|
d->reset(d->loadAirway);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
PositionedIDVec NavDataCache::airwayWaypts(int id)
|
PositionedIDVec NavDataCache::airwayWaypts(int id)
|
||||||
{
|
{
|
||||||
|
d->reset(d->airwayEdges);
|
||||||
sqlite3_bind_int(d->airwayEdges, 1, id);
|
sqlite3_bind_int(d->airwayEdges, 1, id);
|
||||||
|
|
||||||
typedef std::pair<PositionedID, PositionedID> Edge;
|
typedef std::pair<PositionedID, PositionedID> Edge;
|
||||||
|
@ -2301,55 +2342,97 @@ PositionedIDVec NavDataCache::airwayWaypts(int id)
|
||||||
|
|
||||||
d->reset(d->airwayEdges);
|
d->reset(d->airwayEdges);
|
||||||
if (rawEdges.empty()) {
|
if (rawEdges.empty()) {
|
||||||
return PositionedIDVec();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// linearize
|
// linearize
|
||||||
PositionedIDDeque linearAirway;
|
PositionedIDVec result;
|
||||||
PositionedID firstId = rawEdges.front().first,
|
|
||||||
lastId = rawEdges.front().second;
|
|
||||||
std::set<PositionedID> seen;
|
|
||||||
|
|
||||||
// first edge is trivial
|
|
||||||
linearAirway.push_back(firstId);
|
|
||||||
linearAirway.push_back(lastId);
|
|
||||||
seen.insert(firstId);
|
|
||||||
seen.insert(lastId);
|
|
||||||
rawEdges.pop_front();
|
|
||||||
|
|
||||||
while (!rawEdges.empty()) {
|
while (!rawEdges.empty()) {
|
||||||
Edge e = rawEdges.front();
|
bool didAddEdge = false;
|
||||||
|
std::set<PositionedID> seen;
|
||||||
|
EdgeVec nextDeque;
|
||||||
|
PositionedIDDeque linearAirway;
|
||||||
|
PositionedID firstId = rawEdges.front().first,
|
||||||
|
lastId = rawEdges.front().second;
|
||||||
|
|
||||||
|
// first edge is trivial
|
||||||
|
linearAirway.push_back(firstId);
|
||||||
|
linearAirway.push_back(lastId);
|
||||||
|
seen.insert(firstId);
|
||||||
|
seen.insert(lastId);
|
||||||
rawEdges.pop_front();
|
rawEdges.pop_front();
|
||||||
|
|
||||||
// look for new segments
|
while (!rawEdges.empty()) {
|
||||||
if (e.first == firstId) {
|
Edge e = rawEdges.front();
|
||||||
linearAirway.push_front(e.second);
|
rawEdges.pop_front();
|
||||||
seen.insert(e.second);
|
|
||||||
firstId = e.second;
|
bool seenFirst = (seen.find(e.first) != seen.end());
|
||||||
continue;
|
bool seenSecond = (seen.find(e.second) != seen.end());
|
||||||
|
|
||||||
|
// duplicated segment, should be impossible
|
||||||
|
assert(!(seenFirst && seenSecond));
|
||||||
|
|
||||||
|
if (!seenFirst && !seenSecond) {
|
||||||
|
// push back to try later on
|
||||||
|
nextDeque.push_back(e);
|
||||||
|
if (rawEdges.empty()) {
|
||||||
|
rawEdges = nextDeque;
|
||||||
|
nextDeque.clear();
|
||||||
|
|
||||||
|
if (!didAddEdge) {
|
||||||
|
// we have a disjoint, need to start a new section
|
||||||
|
// break out of the inner while loop so the outer
|
||||||
|
// one can process and restart
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
didAddEdge = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have an exterior edge, grow our current linear piece
|
||||||
|
if (seenFirst && (e.first == firstId)) {
|
||||||
|
linearAirway.push_front(e.second);
|
||||||
|
firstId = e.second;
|
||||||
|
seen.insert(e.second);
|
||||||
|
} else if (seenSecond && (e.second == firstId)) {
|
||||||
|
linearAirway.push_front(e.first);
|
||||||
|
firstId = e.first;
|
||||||
|
seen.insert(e.first);
|
||||||
|
} else if (seenFirst && (e.first == lastId)) {
|
||||||
|
linearAirway.push_back(e.second);
|
||||||
|
lastId = e.second;
|
||||||
|
seen.insert(e.second);
|
||||||
|
} else if (seenSecond && (e.second == lastId)) {
|
||||||
|
linearAirway.push_back(e.first);
|
||||||
|
lastId = e.first;
|
||||||
|
seen.insert(e.first);
|
||||||
|
}
|
||||||
|
didAddEdge = true;
|
||||||
|
|
||||||
|
if (rawEdges.empty()) {
|
||||||
|
rawEdges = nextDeque;
|
||||||
|
nextDeque.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!result.empty())
|
||||||
|
result.push_back(0);
|
||||||
|
result.insert(result.end(), linearAirway.begin(), linearAirway.end());
|
||||||
|
} // outer loop
|
||||||
|
|
||||||
|
for (int i=0; i<result.size(); ++i) {
|
||||||
|
if (result.at(i) == 0) {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, i << " break");
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, i << " " << loadById(result.at(i))->ident());
|
||||||
|
|
||||||
if (e.first == lastId) {
|
|
||||||
linearAirway.push_back(e.second);
|
|
||||||
seen.insert(e.second);
|
|
||||||
lastId = e.second;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// look for seen segments - presumed to be reversed internal edges
|
|
||||||
if (seen.find(e.first) != seen.end()) {
|
|
||||||
// if it's the inverse of interior edge, both ends must have been
|
|
||||||
// seen. Otherwise it should have been an exterior edge and
|
|
||||||
// handled by the case above.
|
|
||||||
assert(seen.find(e.second) != seen.end());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// push back to try later on
|
|
||||||
rawEdges.push_back(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return PositionedIDVec(linearAirway.begin(), linearAirway.end());
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
PositionedID NavDataCache::findNavaidForRunway(PositionedID runway, FGPositioned::Type ty)
|
PositionedID NavDataCache::findNavaidForRunway(PositionedID runway, FGPositioned::Type ty)
|
||||||
|
|
|
@ -53,6 +53,9 @@ namespace Octree {
|
||||||
class Branch;
|
class Branch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Airway;
|
||||||
|
using AirwayRef = SGSharedPtr<Airway>;
|
||||||
|
|
||||||
class NavDataCache
|
class NavDataCache
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -268,7 +271,9 @@ public:
|
||||||
TypedPositionedVec getOctreeLeafChildren(int64_t octreeNodeId);
|
TypedPositionedVec getOctreeLeafChildren(int64_t octreeNodeId);
|
||||||
|
|
||||||
// airways
|
// airways
|
||||||
int findAirway(int network, const std::string& aName);
|
int findAirway(int network, const std::string& aName, bool create);
|
||||||
|
|
||||||
|
int findAirway(const std::string& aName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* insert an edge between two positioned nodes, into the network.
|
* insert an edge between two positioned nodes, into the network.
|
||||||
|
@ -282,11 +287,13 @@ public:
|
||||||
bool isInAirwayNetwork(int network, PositionedID pos);
|
bool isInAirwayNetwork(int network, PositionedID pos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrive all the destination points reachcable from a positioned
|
* retrive all the destination points reachable from a positioned
|
||||||
* in an airway
|
* in an airway
|
||||||
*/
|
*/
|
||||||
AirwayEdgeVec airwayEdgesFrom(int network, PositionedID pos);
|
AirwayEdgeVec airwayEdgesFrom(int network, PositionedID pos);
|
||||||
|
|
||||||
|
AirwayRef loadAirway(int airwayID);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waypoints on the airway
|
* Waypoints on the airway
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -47,6 +47,9 @@ using std::vector;
|
||||||
namespace flightgear
|
namespace flightgear
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static std::vector<AirwayRef> static_airwaysCache;
|
||||||
|
typedef SGSharedPtr<FGPositioned> FGPositionedRef;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class AStarOpenNode : public SGReferenced
|
class AStarOpenNode : public SGReferenced
|
||||||
|
@ -56,8 +59,8 @@ public:
|
||||||
int aAirway,
|
int aAirway,
|
||||||
FGPositionedRef aDest, AStarOpenNode* aPrev) :
|
FGPositionedRef aDest, AStarOpenNode* aPrev) :
|
||||||
node(aNode),
|
node(aNode),
|
||||||
airway(aAirway),
|
previous(aPrev),
|
||||||
previous(aPrev)
|
airway(aAirway)
|
||||||
{
|
{
|
||||||
distanceFromStart = aLegDist;
|
distanceFromStart = aLegDist;
|
||||||
if (previous) {
|
if (previous) {
|
||||||
|
@ -72,8 +75,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
FGPositionedRef node;
|
FGPositionedRef node;
|
||||||
int airway;
|
|
||||||
SGSharedPtr<AStarOpenNode> previous;
|
SGSharedPtr<AStarOpenNode> previous;
|
||||||
|
int airway;
|
||||||
double distanceFromStart; // aka 'g(x)'
|
double distanceFromStart; // aka 'g(x)'
|
||||||
double directDistanceToDestination; // aka 'h(x)'
|
double directDistanceToDestination; // aka 'h(x)'
|
||||||
|
|
||||||
|
@ -85,17 +88,17 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef SGSharedPtr<AStarOpenNode> AStarOpenNodeRef;
|
using AStarOpenNodeRef = SGSharedPtr<AStarOpenNode>;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
Airway::Network* Airway::lowLevel()
|
Airway::Network* Airway::lowLevel()
|
||||||
{
|
{
|
||||||
static Network* static_lowLevel = NULL;
|
static Network* static_lowLevel = nullptr;
|
||||||
|
|
||||||
if (!static_lowLevel) {
|
if (!static_lowLevel) {
|
||||||
static_lowLevel = new Network;
|
static_lowLevel = new Network;
|
||||||
static_lowLevel->_networkID = 1;
|
static_lowLevel->_networkID = Airway::LowLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_lowLevel;
|
return static_lowLevel;
|
||||||
|
@ -103,29 +106,33 @@ Airway::Network* Airway::lowLevel()
|
||||||
|
|
||||||
Airway::Network* Airway::highLevel()
|
Airway::Network* Airway::highLevel()
|
||||||
{
|
{
|
||||||
static Network* static_highLevel = NULL;
|
static Network* static_highLevel = nullptr;
|
||||||
if (!static_highLevel) {
|
if (!static_highLevel) {
|
||||||
static_highLevel = new Network;
|
static_highLevel = new Network;
|
||||||
static_highLevel->_networkID = 2;
|
static_highLevel->_networkID = Airway::HighLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_highLevel;
|
return static_highLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
Airway::Airway(const std::string& aIdent, double aTop, double aBottom) :
|
Airway::Airway(const std::string& aIdent,
|
||||||
|
const Level level,
|
||||||
|
int dbId,
|
||||||
|
int aTop, int aBottom) :
|
||||||
_ident(aIdent),
|
_ident(aIdent),
|
||||||
|
_level(level),
|
||||||
|
_cacheId(dbId),
|
||||||
_topAltitudeFt(aTop),
|
_topAltitudeFt(aTop),
|
||||||
_bottomAltitudeFt(aBottom)
|
_bottomAltitudeFt(aBottom)
|
||||||
{
|
{
|
||||||
|
static_airwaysCache.push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Airway::load(const SGPath& path)
|
void Airway::loadAWYDat(const SGPath& path)
|
||||||
{
|
{
|
||||||
std::string identStart, identEnd, name;
|
std::string identStart, identEnd, name;
|
||||||
double latStart, lonStart, latEnd, lonEnd;
|
double latStart, lonStart, latEnd, lonEnd;
|
||||||
int type, base, top;
|
int type, base, top;
|
||||||
//int airwayIndex = 0;
|
|
||||||
//FGNode *n;
|
|
||||||
|
|
||||||
sg_gzifstream in( path );
|
sg_gzifstream in( path );
|
||||||
if ( !in.is_open() ) {
|
if ( !in.is_open() ) {
|
||||||
|
@ -147,47 +154,76 @@ void Airway::load(const SGPath& path)
|
||||||
in >> latStart >> lonStart >> identEnd >> latEnd >> lonEnd >> type >> base >> top >> name;
|
in >> latStart >> lonStart >> identEnd >> latEnd >> lonEnd >> type >> base >> top >> name;
|
||||||
in >> skipeol;
|
in >> skipeol;
|
||||||
|
|
||||||
// type = 1; low-altitude
|
// type = 1; low-altitude (victor)
|
||||||
// type = 2; high-altitude
|
// type = 2; high-altitude (jet)
|
||||||
Network* net = (type == 1) ? lowLevel() : highLevel();
|
Network* net = (type == 1) ? lowLevel() : highLevel();
|
||||||
|
|
||||||
SGGeod startPos(SGGeod::fromDeg(lonStart, latStart)),
|
SGGeod startPos(SGGeod::fromDeg(lonStart, latStart)),
|
||||||
endPos(SGGeod::fromDeg(lonEnd, latEnd));
|
endPos(SGGeod::fromDeg(lonEnd, latEnd));
|
||||||
|
|
||||||
int awy = net->findAirway(name, top, base);
|
if (type == 1) {
|
||||||
net->addEdge(awy, startPos, identStart, endPos, identEnd);
|
|
||||||
|
} else if (type == 2) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_NAVAID, SG_DEV_WARN, "unknown airway type:" << type << " for " << name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pieces = simgear::strutils::split(name, "-");
|
||||||
|
for (auto p : pieces) {
|
||||||
|
int awy = net->findAirway(p);
|
||||||
|
net->addEdge(awy, startPos, identStart, endPos, identEnd);
|
||||||
|
}
|
||||||
} // of file line iteration
|
} // of file line iteration
|
||||||
}
|
}
|
||||||
|
|
||||||
WayptVec::const_iterator Airway::find(WayptRef wpt) const
|
WayptVec::const_iterator Airway::find(WayptRef wpt) const
|
||||||
{
|
{
|
||||||
WayptVec::const_iterator it;
|
assert(!_elements.empty());
|
||||||
for (it = _elements.begin(); it != _elements.end(); ++it) {
|
return std::find_if(_elements.begin(), _elements.end(),
|
||||||
if (wpt->matches(*it)) {
|
[wpt] (const WayptRef& w) { return w->matches(wpt); });
|
||||||
return it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return it;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Airway::canVia(const WayptRef& from, const WayptRef& to) const
|
bool Airway::canVia(const WayptRef& from, const WayptRef& to) const
|
||||||
{
|
{
|
||||||
WayptVec::const_iterator fit = find(from);
|
loadWaypoints();
|
||||||
WayptVec::const_iterator tit = find(to);
|
|
||||||
|
auto fit = find(from);
|
||||||
|
auto tit = find(to);
|
||||||
|
|
||||||
if ((fit == _elements.end()) || (tit == _elements.end())) {
|
if ((fit == _elements.end()) || (tit == _elements.end())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fit < tit) {
|
||||||
|
// forward progression
|
||||||
|
for (++fit; fit != tit; ++fit) {
|
||||||
|
if (*fit == nullptr) {
|
||||||
|
// traversed an airway discontinuity
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// reverse progression
|
||||||
|
for (--fit; fit != tit; --fit) {
|
||||||
|
if (*fit == nullptr) {
|
||||||
|
// traversed an airway discontinuity
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
WayptVec Airway::via(const WayptRef& from, const WayptRef& to) const
|
WayptVec Airway::via(const WayptRef& from, const WayptRef& to) const
|
||||||
{
|
{
|
||||||
|
loadWaypoints();
|
||||||
|
|
||||||
WayptVec v;
|
WayptVec v;
|
||||||
WayptVec::const_iterator fit = find(from);
|
auto fit = find(from);
|
||||||
WayptVec::const_iterator tit = find(to);
|
auto tit = find(to);
|
||||||
|
|
||||||
if ((fit == _elements.end()) || (tit == _elements.end())) {
|
if ((fit == _elements.end()) || (tit == _elements.end())) {
|
||||||
throw sg_exception("bad VIA transition points");
|
throw sg_exception("bad VIA transition points");
|
||||||
|
@ -220,43 +256,129 @@ WayptVec Airway::via(const WayptRef& from, const WayptRef& to) const
|
||||||
|
|
||||||
bool Airway::containsNavaid(const FGPositionedRef &navaid) const
|
bool Airway::containsNavaid(const FGPositionedRef &navaid) const
|
||||||
{
|
{
|
||||||
return find(new NavaidWaypoint(navaid, NULL)) != _elements.end();
|
if (!navaid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
loadWaypoints();
|
||||||
|
auto it = std::find_if(_elements.begin(), _elements.end(),
|
||||||
|
[navaid](WayptRef w)
|
||||||
|
{
|
||||||
|
if (!w) return false;
|
||||||
|
return w->matches(navaid);
|
||||||
|
});
|
||||||
|
return (it != _elements.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
int Airway::Network::findAirway(const std::string& aName, double aTop, double aBase)
|
int Airway::Network::findAirway(const std::string& aName)
|
||||||
{
|
{
|
||||||
return NavDataCache::instance()->findAirway(_networkID, aName);
|
const Level level = _networkID;
|
||||||
}
|
auto it = std::find_if(static_airwaysCache.begin(), static_airwaysCache.end(),
|
||||||
|
[aName, level](const AirwayRef& awy)
|
||||||
Airway* Airway::findByIdent(const std::string& aIdent)
|
{ return (awy->_level == level) && (awy->ident() == aName); });
|
||||||
{
|
if (it != static_airwaysCache.end()) {
|
||||||
NavDataCache* ndc = NavDataCache::instance();
|
return (*it)->_cacheId;
|
||||||
|
|
||||||
int id = ndc->findAirway(0, aIdent);
|
|
||||||
|
|
||||||
PositionedIDVec pts = ndc->airwayWaypts(id);
|
|
||||||
Airway* awy = new Airway(aIdent, 0, 0);
|
|
||||||
|
|
||||||
PositionedIDVec::const_iterator it;
|
|
||||||
for (it = pts.begin(); it != pts.end(); ++it) {
|
|
||||||
FGPositionedRef pos = ndc->loadById(*it);
|
|
||||||
WayptRef w = new NavaidWaypoint(pos, NULL);
|
|
||||||
awy->_elements.push_back(w);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NavDataCache::instance()->findAirway(_networkID, aName, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
AirwayRef Airway::findByIdent(const std::string& aIdent, Level level)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(static_airwaysCache.begin(), static_airwaysCache.end(),
|
||||||
|
[aIdent, level](const AirwayRef& awy)
|
||||||
|
{ return (awy->_level == level) && (awy->ident() == aIdent); });
|
||||||
|
if (it != static_airwaysCache.end()) {
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
NavDataCache* ndc = NavDataCache::instance();
|
||||||
|
int airwayId = ndc->findAirway(level, aIdent, false);
|
||||||
|
if (airwayId == 0) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_INFO, "fooooo");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AirwayRef awy(new Airway(aIdent, level, airwayId, 0, 0));
|
||||||
|
static_airwaysCache.push_back(awy);
|
||||||
return awy;
|
return awy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AirwayRef Airway::loadByCacheId(int cacheId)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(static_airwaysCache.begin(), static_airwaysCache.end(),
|
||||||
|
[cacheId](const AirwayRef& awy)
|
||||||
|
{ return (awy->_cacheId == cacheId); });
|
||||||
|
if (it != static_airwaysCache.end()) {
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
NavDataCache* ndc = NavDataCache::instance();
|
||||||
|
AirwayRef awy = ndc->loadAirway(cacheId);
|
||||||
|
if (awy) {
|
||||||
|
static_airwaysCache.push_back(awy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return awy;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Airway::loadWaypoints() const
|
||||||
|
{
|
||||||
|
NavDataCache* ndc = NavDataCache::instance();
|
||||||
|
for (auto id : ndc->airwayWaypts(_cacheId)) {
|
||||||
|
if (id == 0) {
|
||||||
|
_elements.push_back({});
|
||||||
|
} else {
|
||||||
|
FGPositionedRef pos = ndc->loadById(id);
|
||||||
|
auto wp = new NavaidWaypoint(pos, const_cast<Airway*>(this));
|
||||||
|
wp->setFlag(WPT_VIA);
|
||||||
|
wp->setFlag(WPT_GENERATED);
|
||||||
|
_elements.push_back(wp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AirwayRef Airway::findByIdentAndVia(const std::string& aIdent, const WayptRef& from, const WayptRef& to)
|
||||||
|
{
|
||||||
|
AirwayRef hi = findByIdent(aIdent, HighLevel);
|
||||||
|
if (hi && hi->canVia(from, to)) {
|
||||||
|
return hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
AirwayRef low = findByIdent(aIdent, LowLevel);
|
||||||
|
if (low && low->canVia(from, to)) {
|
||||||
|
return low;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
AirwayRef Airway::findByIdentAndNavaid(const std::string& aIdent, const FGPositionedRef nav)
|
||||||
|
{
|
||||||
|
AirwayRef hi = findByIdent(aIdent, HighLevel);
|
||||||
|
if (hi && hi->containsNavaid(nav)) {
|
||||||
|
return hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
AirwayRef low = findByIdent(aIdent, LowLevel);
|
||||||
|
if (low && low->containsNavaid(nav)) {
|
||||||
|
return low;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
WayptRef Airway::findEnroute(const std::string &aIdent) const
|
WayptRef Airway::findEnroute(const std::string &aIdent) const
|
||||||
{
|
{
|
||||||
WayptVec::const_iterator it;
|
auto it = std::find_if(_elements.begin(), _elements.end(),
|
||||||
for (it = _elements.begin(); it != _elements.end(); ++it) {
|
[&aIdent](WayptRef w)
|
||||||
if ((*it)->ident() == aIdent) {
|
{
|
||||||
return *it;
|
if (!w) return false;
|
||||||
}
|
return w->ident() == aIdent;
|
||||||
}
|
});
|
||||||
|
|
||||||
return WayptRef();
|
if (it != _elements.end())
|
||||||
|
return *it;
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Airway::Network::addEdge(int aWay, const SGGeod& aStartPos,
|
void Airway::Network::addEdge(int aWay, const SGGeod& aStartPos,
|
||||||
|
@ -372,7 +494,14 @@ bool Airway::Network::cleanGeneratedPath(WayptRef aFrom, WayptRef aTo, WayptVec&
|
||||||
std::pair<FGPositionedRef, bool>
|
std::pair<FGPositionedRef, bool>
|
||||||
Airway::Network::findClosestNode(WayptRef aRef)
|
Airway::Network::findClosestNode(WayptRef aRef)
|
||||||
{
|
{
|
||||||
return findClosestNode(aRef->position());
|
if (aRef->source()) {
|
||||||
|
// we can check directly
|
||||||
|
if (inNetwork(aRef->source()->guid())) {
|
||||||
|
return std::make_pair(aRef->source(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return findClosestNode(aRef->position());
|
||||||
}
|
}
|
||||||
|
|
||||||
class InAirwayFilter : public FGPositioned::Filter
|
class InAirwayFilter : public FGPositioned::Filter
|
||||||
|
@ -391,7 +520,7 @@ public:
|
||||||
{ return FGPositioned::WAYPOINT; }
|
{ return FGPositioned::WAYPOINT; }
|
||||||
|
|
||||||
virtual FGPositioned::Type maxType() const
|
virtual FGPositioned::Type maxType() const
|
||||||
{ return FGPositioned::NDB; }
|
{ return FGPositioned::VOR; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Airway::Network* _net;
|
Airway::Network* _net;
|
||||||
|
@ -418,14 +547,20 @@ typedef vector<AStarOpenNodeRef> OpenNodeHeap;
|
||||||
static void buildWaypoints(AStarOpenNodeRef aNode, WayptVec& aRoute)
|
static void buildWaypoints(AStarOpenNodeRef aNode, WayptVec& aRoute)
|
||||||
{
|
{
|
||||||
// count the route length, and hence pre-size aRoute
|
// count the route length, and hence pre-size aRoute
|
||||||
int count = 0;
|
size_t count = 0;
|
||||||
AStarOpenNodeRef n = aNode;
|
AStarOpenNodeRef n = aNode;
|
||||||
for (; n != NULL; ++count, n = n->previous) {;}
|
for (; n != nullptr; ++count, n = n->previous) {;}
|
||||||
aRoute.resize(count);
|
aRoute.resize(count);
|
||||||
|
|
||||||
// run over the route, creating waypoints
|
// run over the route, creating waypoints
|
||||||
for (n = aNode; n; n=n->previous) {
|
for (n = aNode; n; n=n->previous) {
|
||||||
aRoute[--count] = new NavaidWaypoint(n->node, NULL);
|
// get / create airway to be the owner for this waypoint
|
||||||
|
AirwayRef awy = Airway::loadByCacheId(n->airway);
|
||||||
|
// assert(awy);
|
||||||
|
auto wp = new NavaidWaypoint(n->node, awy);
|
||||||
|
wp->setFlag(WPT_VIA);
|
||||||
|
wp->setFlag(WPT_GENERATED);
|
||||||
|
aRoute[--count] = wp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,7 +576,7 @@ findInOpen(const OpenNodeHeap& aHeap, FGPositioned* aPos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
class HeapOrder
|
class HeapOrder
|
||||||
|
@ -462,7 +597,7 @@ bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest,
|
||||||
ClosedNodeSet closedNodes;
|
ClosedNodeSet closedNodes;
|
||||||
HeapOrder ordering;
|
HeapOrder ordering;
|
||||||
|
|
||||||
openNodes.push_back(new AStarOpenNode(aStart, 0.0, 0, aDest, NULL));
|
openNodes.push_back(new AStarOpenNode(aStart, 0.0, 0, aDest, nullptr));
|
||||||
|
|
||||||
// A* open node iteration
|
// A* open node iteration
|
||||||
while (!openNodes.empty()) {
|
while (!openNodes.empty()) {
|
||||||
|
@ -485,7 +620,7 @@ bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest,
|
||||||
|
|
||||||
// adjacent (neighbour) iteration
|
// adjacent (neighbour) iteration
|
||||||
NavDataCache* cache = NavDataCache::instance();
|
NavDataCache* cache = NavDataCache::instance();
|
||||||
BOOST_FOREACH(AirwayEdge other, cache->airwayEdgesFrom(_networkID, xp->guid())) {
|
for (auto other : cache->airwayEdgesFrom(_networkID, xp->guid())) {
|
||||||
if (closedNodes.count(other.second)) {
|
if (closedNodes.count(other.second)) {
|
||||||
continue; // closed, ignore
|
continue; // closed, ignore
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,14 +35,30 @@ namespace flightgear {
|
||||||
struct SearchContext;
|
struct SearchContext;
|
||||||
class AdjacentWaypoint;
|
class AdjacentWaypoint;
|
||||||
class InAirwayFilter;
|
class InAirwayFilter;
|
||||||
|
class Airway;
|
||||||
|
|
||||||
class Airway
|
using AirwayRef = SGSharedPtr<Airway>;
|
||||||
|
|
||||||
|
class Airway : public RouteBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual std::string ident() const
|
enum Level {
|
||||||
|
UnknownLevel = 0,
|
||||||
|
LowLevel = 1, /// Victor airways
|
||||||
|
HighLevel = 2, /// Jet airways
|
||||||
|
Both = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string ident() const override
|
||||||
{ return _ident; }
|
{ return _ident; }
|
||||||
|
|
||||||
static void load(const SGPath& path);
|
int cacheId() const
|
||||||
|
{ return _cacheId; }
|
||||||
|
|
||||||
|
Level level() const
|
||||||
|
{ return _level; }
|
||||||
|
|
||||||
|
static void loadAWYDat(const SGPath& path);
|
||||||
|
|
||||||
double topAltitudeFt() const
|
double topAltitudeFt() const
|
||||||
{ return _topAltitudeFt; }
|
{ return _topAltitudeFt; }
|
||||||
|
@ -50,7 +66,21 @@ public:
|
||||||
double bottomAltitudeFt() const
|
double bottomAltitudeFt() const
|
||||||
{ return _bottomAltitudeFt; }
|
{ return _bottomAltitudeFt; }
|
||||||
|
|
||||||
static Airway* findByIdent(const std::string& aIdent);
|
static AirwayRef loadByCacheId(int cacheId);
|
||||||
|
|
||||||
|
static AirwayRef findByIdent(const std::string& aIdent, Level level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the airway based on its ident. IF both high- and low- level idents
|
||||||
|
* exist, select the one which can route between the from and to waypoints
|
||||||
|
* correctly, preferring high-level airways.
|
||||||
|
*/
|
||||||
|
static AirwayRef findByIdentAndVia(const std::string& aIdent, const WayptRef& from, const WayptRef& to);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an airway by ident, and containing a particula rnavaid/fix.
|
||||||
|
*/
|
||||||
|
static AirwayRef findByIdentAndNavaid(const std::string& aIdent, const FGPositionedRef nav);
|
||||||
|
|
||||||
WayptRef findEnroute(const std::string& aIdent) const;
|
WayptRef findEnroute(const std::string& aIdent) const;
|
||||||
|
|
||||||
|
@ -60,6 +90,8 @@ public:
|
||||||
|
|
||||||
bool containsNavaid(const FGPositionedRef& navaid) const;
|
bool containsNavaid(const FGPositionedRef& navaid) const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track a network of airways
|
* Track a network of airways
|
||||||
*
|
*
|
||||||
|
@ -81,13 +113,18 @@ public:
|
||||||
*/
|
*/
|
||||||
bool route(WayptRef aFrom, WayptRef aTo, WayptVec& aPath);
|
bool route(WayptRef aFrom, WayptRef aTo, WayptVec& aPath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overloaded version working with a raw SGGeod
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::pair<FGPositionedRef, bool> findClosestNode(const SGGeod& aGeod);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addEdge(int aWay, const SGGeod& aStartPos,
|
void addEdge(int aWay, const SGGeod& aStartPos,
|
||||||
const std::string& aStartIdent,
|
const std::string& aStartIdent,
|
||||||
const SGGeod& aEndPos, const std::string& aEndIdent);
|
const SGGeod& aEndPos, const std::string& aEndIdent);
|
||||||
|
|
||||||
int findAirway(const std::string& aName, double aTop, double aBase);
|
int findAirway(const std::string& aName);
|
||||||
|
|
||||||
bool cleanGeneratedPath(WayptRef aFrom, WayptRef aTo, WayptVec& aPath,
|
bool cleanGeneratedPath(WayptRef aFrom, WayptRef aTo, WayptVec& aPath,
|
||||||
bool exactTo, bool exactFrom);
|
bool exactTo, bool exactFrom);
|
||||||
|
@ -113,19 +150,13 @@ public:
|
||||||
*/
|
*/
|
||||||
std::pair<FGPositionedRef, bool> findClosestNode(WayptRef aRef);
|
std::pair<FGPositionedRef, bool> findClosestNode(WayptRef aRef);
|
||||||
|
|
||||||
/**
|
|
||||||
* Overloaded version working with a raw SGGeod
|
|
||||||
*/
|
|
||||||
|
|
||||||
std::pair<FGPositionedRef, bool> findClosestNode(const SGGeod& aGeod);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cache which positioned items are in this network
|
* cache which positioned items are in this network
|
||||||
*/
|
*/
|
||||||
typedef std::map<PositionedID, bool> NetworkMembershipDict;
|
typedef std::map<PositionedID, bool> NetworkMembershipDict;
|
||||||
mutable NetworkMembershipDict _inNetworkCache;
|
mutable NetworkMembershipDict _inNetworkCache;
|
||||||
|
|
||||||
int _networkID;
|
Level _networkID;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,17 +164,23 @@ public:
|
||||||
static Network* lowLevel();
|
static Network* lowLevel();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Airway(const std::string& aIdent, double aTop, double aBottom);
|
Airway(const std::string& aIdent, const Level level, int dbId, int aTop, int aBottom);
|
||||||
|
|
||||||
|
void loadWaypoints() const;
|
||||||
|
|
||||||
WayptVec::const_iterator find(WayptRef wpt) const;
|
WayptVec::const_iterator find(WayptRef wpt) const;
|
||||||
|
|
||||||
friend class Network;
|
friend class Network;
|
||||||
|
friend class NavDataCache;
|
||||||
std::string _ident;
|
|
||||||
double _topAltitudeFt;
|
const std::string _ident;
|
||||||
double _bottomAltitudeFt;
|
const Level _level;
|
||||||
|
const int _cacheId;
|
||||||
WayptVec _elements;
|
|
||||||
|
int _topAltitudeFt;
|
||||||
|
int _bottomAltitudeFt;
|
||||||
|
|
||||||
|
mutable WayptVec _elements;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // of namespace flightgear
|
} // of namespace flightgear
|
||||||
|
|
|
@ -265,13 +265,28 @@ Transition* ArrivalDeparture::findTransitionByEnroute(Waypt* aEnroute) const
|
||||||
WptTransitionMap::const_iterator eit;
|
WptTransitionMap::const_iterator eit;
|
||||||
for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
|
for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
|
||||||
if (eit->second->enroute()->matches(aEnroute)) {
|
if (eit->second->enroute()->matches(aEnroute)) {
|
||||||
SG_LOG(SG_NAVAID, SG_INFO, ident() << " using enroute transition " << eit->second->ident());
|
|
||||||
return eit->second;
|
return eit->second;
|
||||||
}
|
}
|
||||||
} // of enroute transition iteration
|
} // of enroute transition iteration
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Transition* ArrivalDeparture::findTransitionByEnroute(FGPositioned* aEnroute) const
|
||||||
|
{
|
||||||
|
if (!aEnroute) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
WptTransitionMap::const_iterator eit;
|
||||||
|
for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
|
||||||
|
if (eit->second->enroute()->matches(aEnroute)) {
|
||||||
|
return eit->second;
|
||||||
|
}
|
||||||
|
} // of enroute transition iteration
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
WayptRef ArrivalDeparture::findBestTransition(const SGGeod& aPos) const
|
WayptRef ArrivalDeparture::findBestTransition(const SGGeod& aPos) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -204,7 +204,9 @@ public:
|
||||||
* transitions in a human-meaningful way (including persistence).
|
* transitions in a human-meaningful way (including persistence).
|
||||||
*/
|
*/
|
||||||
Transition* findTransitionByName(const std::string& aIdent) const;
|
Transition* findTransitionByName(const std::string& aIdent) const;
|
||||||
|
|
||||||
|
Transition* findTransitionByEnroute(FGPositioned* aEnroute) const;
|
||||||
|
|
||||||
Transition* findTransitionByEnroute(Waypt* aEnroute) const;
|
Transition* findTransitionByEnroute(Waypt* aEnroute) const;
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,7 @@
|
||||||
// along with this program; if not, write to the Free Software
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#include "config.h"
|
||||||
# include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "route.hxx"
|
#include "route.hxx"
|
||||||
|
|
||||||
|
@ -49,6 +47,7 @@
|
||||||
#include <Navaids/waypoint.hxx>
|
#include <Navaids/waypoint.hxx>
|
||||||
#include <Navaids/LevelDXML.hxx>
|
#include <Navaids/LevelDXML.hxx>
|
||||||
#include <Airports/airport.hxx>
|
#include <Airports/airport.hxx>
|
||||||
|
#include <Navaids/airways.hxx>
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
@ -65,12 +64,7 @@ bool isMachRestrict(RouteRestriction rr)
|
||||||
}
|
}
|
||||||
|
|
||||||
Waypt::Waypt(RouteBase* aOwner) :
|
Waypt::Waypt(RouteBase* aOwner) :
|
||||||
_altitudeFt(0.0),
|
|
||||||
_speed(0.0),
|
|
||||||
_altRestrict(RESTRICT_NONE),
|
|
||||||
_speedRestrict(RESTRICT_NONE),
|
|
||||||
_owner(aOwner),
|
_owner(aOwner),
|
||||||
_flags(0),
|
|
||||||
_magVarDeg(NO_MAG_VAR)
|
_magVarDeg(NO_MAG_VAR)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -81,7 +75,7 @@ Waypt::~Waypt()
|
||||||
|
|
||||||
std::string Waypt::ident() const
|
std::string Waypt::ident() const
|
||||||
{
|
{
|
||||||
return "";
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Waypt::flag(WayptFlag aFlag) const
|
bool Waypt::flag(WayptFlag aFlag) const
|
||||||
|
@ -108,7 +102,11 @@ bool Waypt::matches(Waypt* aOther) const
|
||||||
|
|
||||||
return matches(aOther->position());
|
return matches(aOther->position());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Waypt::matches(FGPositioned* aPos) const
|
||||||
|
{
|
||||||
|
return aPos && (aPos == source());
|
||||||
|
}
|
||||||
|
|
||||||
bool Waypt::matches(const SGGeod& aPos) const
|
bool Waypt::matches(const SGGeod& aPos) const
|
||||||
{
|
{
|
||||||
|
@ -158,6 +156,11 @@ double Waypt::headingRadialDeg() const
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Waypt::icaoDescription() const
|
||||||
|
{
|
||||||
|
return ident();
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// persistence
|
// persistence
|
||||||
|
|
||||||
|
@ -191,9 +194,9 @@ static const char* restrictionToString(RouteRestriction aRestrict)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Waypt* Waypt::createInstance(RouteBase* aOwner, const std::string& aTypeName)
|
WayptRef Waypt::createInstance(RouteBase* aOwner, const std::string& aTypeName)
|
||||||
{
|
{
|
||||||
Waypt* r = NULL;
|
WayptRef r;
|
||||||
if (aTypeName == "basic") {
|
if (aTypeName == "basic") {
|
||||||
r = new BasicWaypt(aOwner);
|
r = new BasicWaypt(aOwner);
|
||||||
} else if (aTypeName == "navaid") {
|
} else if (aTypeName == "navaid") {
|
||||||
|
@ -233,6 +236,16 @@ WayptRef Waypt::createFromProperties(RouteBase* aOwner, SGPropertyNode_ptr aProp
|
||||||
"Waypt::createFromProperties");
|
"Waypt::createFromProperties");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flightgear::AirwayRef via;
|
||||||
|
if (aProp->hasChild("airway")) {
|
||||||
|
const auto level = static_cast<flightgear::Airway::Level>(aProp->getIntValue("network"));
|
||||||
|
via = flightgear::Airway::findByIdent(aProp->getStringValue("airway"), level);
|
||||||
|
if (via) {
|
||||||
|
// override owner if we are from an airway
|
||||||
|
aOwner = via.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
WayptRef nd(createInstance(aOwner, aProp->getStringValue("type")));
|
WayptRef nd(createInstance(aOwner, aProp->getStringValue("type")));
|
||||||
nd->initFromProperties(aProp);
|
nd->initFromProperties(aProp);
|
||||||
|
@ -280,6 +293,10 @@ void Waypt::initFromProperties(SGPropertyNode_ptr aProp)
|
||||||
setFlag(WPT_MISS, aProp->getBoolValue("miss"));
|
setFlag(WPT_MISS, aProp->getBoolValue("miss"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aProp->hasChild("airway")) {
|
||||||
|
setFlag(WPT_VIA, true);
|
||||||
|
}
|
||||||
|
|
||||||
if (aProp->hasChild("alt-restrict")) {
|
if (aProp->hasChild("alt-restrict")) {
|
||||||
_altRestrict = restrictionFromString(aProp->getStringValue("alt-restrict"));
|
_altRestrict = restrictionFromString(aProp->getStringValue("alt-restrict"));
|
||||||
_altitudeFt = aProp->getDoubleValue("altitude-ft");
|
_altitudeFt = aProp->getDoubleValue("altitude-ft");
|
||||||
|
@ -311,6 +328,12 @@ void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const
|
||||||
aProp->setBoolValue("approach", true);
|
aProp->setBoolValue("approach", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flag(WPT_VIA)) {
|
||||||
|
flightgear::AirwayRef awy = (flightgear::Airway*) _owner;
|
||||||
|
aProp->setStringValue("airway", awy->ident());
|
||||||
|
aProp->setIntValue("network", awy->level());
|
||||||
|
}
|
||||||
|
|
||||||
if (flag(WPT_MISS)) {
|
if (flag(WPT_MISS)) {
|
||||||
aProp->setBoolValue("miss", true);
|
aProp->setBoolValue("miss", true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,9 @@ namespace flightgear
|
||||||
class RouteBase;
|
class RouteBase;
|
||||||
class Waypt;
|
class Waypt;
|
||||||
class NavdataVisitor;
|
class NavdataVisitor;
|
||||||
|
class Airway;
|
||||||
|
|
||||||
|
using AirwayRef = SGSharedPtr<Airway>;
|
||||||
typedef SGSharedPtr<Waypt> WayptRef;
|
typedef SGSharedPtr<Waypt> WayptRef;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -67,11 +69,14 @@ typedef enum {
|
||||||
|
|
||||||
WPT_DEPARTURE = 1 << 8,
|
WPT_DEPARTURE = 1 << 8,
|
||||||
WPT_ARRIVAL = 1 << 9,
|
WPT_ARRIVAL = 1 << 9,
|
||||||
|
|
||||||
/// waypoint generated by VNAV / speed management profile,
|
/// waypoint generated by VNAV / speed management profile,
|
||||||
/// for step climbs or top of descent
|
/// for step climbs or top of descent
|
||||||
WPT_PSEUDO = 1 << 10,
|
WPT_PSEUDO = 1 << 10,
|
||||||
WPT_APPROACH = 1 << 11
|
WPT_APPROACH = 1 << 11,
|
||||||
|
|
||||||
|
/// waypoint prodcued by expanding a VIA segment
|
||||||
|
WPT_VIA = 1 << 12
|
||||||
} WayptFlag;
|
} WayptFlag;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -89,15 +94,16 @@ bool isMachRestrict(RouteRestriction rr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for waypoints (and things that are treated similarly
|
* Abstract base class for waypoints (and things that are treated similarly
|
||||||
* by navigation systems)
|
* by navigation systems). More precisely this is route path elements,
|
||||||
|
* including their terminator.
|
||||||
*/
|
*/
|
||||||
class Waypt : public SGReferenced
|
class Waypt : public SGReferenced
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~Waypt();
|
virtual ~Waypt();
|
||||||
|
|
||||||
RouteBase* owner() const
|
RouteBase* owner() const
|
||||||
{ return _owner; }
|
{ return const_cast<RouteBase*>(_owner); }
|
||||||
|
|
||||||
virtual SGGeod position() const = 0;
|
virtual SGGeod position() const = 0;
|
||||||
|
|
||||||
|
@ -105,7 +111,7 @@ public:
|
||||||
* The Positioned associated with this element, if one exists
|
* The Positioned associated with this element, if one exists
|
||||||
*/
|
*/
|
||||||
virtual FGPositioned* source() const
|
virtual FGPositioned* source() const
|
||||||
{ return NULL; }
|
{ return nullptr; }
|
||||||
|
|
||||||
virtual double altitudeFt() const
|
virtual double altitudeFt() const
|
||||||
{ return _altitudeFt; }
|
{ return _altitudeFt; }
|
||||||
|
@ -157,6 +163,12 @@ public:
|
||||||
*/
|
*/
|
||||||
bool matches(Waypt* aOther) const;
|
bool matches(Waypt* aOther) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if this element and positioned are the same, i.e matching
|
||||||
|
* ident and lat/lon are approximately equal
|
||||||
|
*/
|
||||||
|
bool matches(FGPositioned* aPos) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test if this element and a position 'the same'
|
* Test if this element and a position 'the same'
|
||||||
* this can be defined by either position, ident or both
|
* this can be defined by either position, ident or both
|
||||||
|
@ -177,6 +189,13 @@ public:
|
||||||
* for a DME intercept it's the heading to hold, and so on.
|
* for a DME intercept it's the heading to hold, and so on.
|
||||||
*/
|
*/
|
||||||
virtual double headingRadialDeg() const;
|
virtual double headingRadialDeg() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief icaoDescription - description of the waypoint in ICAO route plan format
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual std::string icaoDescription() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class NavdataVisitor;
|
friend class NavdataVisitor;
|
||||||
|
|
||||||
|
@ -195,20 +214,20 @@ protected:
|
||||||
typedef Waypt* (FactoryFunction)(RouteBase* aOwner) ;
|
typedef Waypt* (FactoryFunction)(RouteBase* aOwner) ;
|
||||||
static void registerFactory(const std::string aNodeType, FactoryFunction* aFactory);
|
static void registerFactory(const std::string aNodeType, FactoryFunction* aFactory);
|
||||||
|
|
||||||
double _altitudeFt;
|
double _altitudeFt = 0.0;
|
||||||
double _speed; // knots IAS or mach
|
double _speed = 0.0; // knots IAS or mach
|
||||||
RouteRestriction _altRestrict;
|
RouteRestriction _altRestrict = RESTRICT_NONE;
|
||||||
RouteRestriction _speedRestrict;
|
RouteRestriction _speedRestrict = RESTRICT_NONE;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance of a concrete subclass, or throw an exception
|
* Create an instance of a concrete subclass, or throw an exception
|
||||||
*/
|
*/
|
||||||
static Waypt* createInstance(RouteBase* aOwner, const std::string& aTypeName);
|
static WayptRef createInstance(RouteBase* aOwner, const std::string& aTypeName);
|
||||||
|
|
||||||
RouteBase* _owner;
|
const RouteBase* _owner = nullptr;
|
||||||
unsigned short _flags;
|
unsigned short _flags = 0;
|
||||||
mutable double _magVarDeg;
|
mutable double _magVarDeg = 0.0; ///< cached mag var at this location
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<WayptRef> WayptVec;
|
typedef std::vector<WayptRef> WayptVec;
|
||||||
|
|
|
@ -185,14 +185,10 @@ SGGeod turnCenterFromExit(const SGGeod& pt, double outHeadingDeg,
|
||||||
|
|
||||||
struct TurnInfo
|
struct TurnInfo
|
||||||
{
|
{
|
||||||
TurnInfo() : valid(false),
|
|
||||||
inboundCourseDeg(0.0),
|
|
||||||
turnAngleDeg(0.0) { }
|
|
||||||
|
|
||||||
bool valid;
|
|
||||||
SGGeod turnCenter;
|
SGGeod turnCenter;
|
||||||
double inboundCourseDeg;
|
double inboundCourseDeg = 0.0;
|
||||||
double turnAngleDeg;
|
double turnAngleDeg = 0.0;
|
||||||
|
bool valid = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -337,7 +333,7 @@ public:
|
||||||
(wpt->type() == "via"))
|
(wpt->type() == "via"))
|
||||||
{
|
{
|
||||||
// do nothing, we can't compute a valid leg course for these types
|
// do nothing, we can't compute a valid leg course for these types
|
||||||
// we'll generate shrap turns in the path but that's no problem.
|
// we'll generate sharp turns in the path but that's no problem.
|
||||||
} else if (wpt->type() == "runway") {
|
} else if (wpt->type() == "runway") {
|
||||||
FGRunway* rwy = static_cast<RunwayWaypt*>(wpt.get())->runway();
|
FGRunway* rwy = static_cast<RunwayWaypt*>(wpt.get())->runway();
|
||||||
flyOver = true;
|
flyOver = true;
|
||||||
|
@ -840,7 +836,7 @@ RoutePath::RoutePath(const flightgear::FlightPlan* fp) :
|
||||||
d(new RoutePathPrivate)
|
d(new RoutePathPrivate)
|
||||||
{
|
{
|
||||||
for (int l=0; l<fp->numLegs(); ++l) {
|
for (int l=0; l<fp->numLegs(); ++l) {
|
||||||
Waypt *wpt = fp->legAtIndex(l)->waypoint();
|
WayptRef wpt = fp->legAtIndex(l)->waypoint();
|
||||||
if (!wpt) {
|
if (!wpt) {
|
||||||
SG_LOG(SG_NAVAID, SG_DEV_ALERT, "Waypoint " << l << " of " << fp->numLegs() << "is NULL");
|
SG_LOG(SG_NAVAID, SG_DEV_ALERT, "Waypoint " << l << " of " << fp->numLegs() << "is NULL");
|
||||||
break;
|
break;
|
||||||
|
@ -852,6 +848,18 @@ RoutePath::RoutePath(const flightgear::FlightPlan* fp) :
|
||||||
commonInit();
|
commonInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RoutePath::RoutePath(const RoutePath& other)
|
||||||
|
{
|
||||||
|
d.reset(new RoutePathPrivate(*other.d));
|
||||||
|
}
|
||||||
|
|
||||||
|
RoutePath& RoutePath::operator=(const RoutePath& other)
|
||||||
|
{
|
||||||
|
d.reset(new RoutePathPrivate(*other.d));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
RoutePath::~RoutePath()
|
RoutePath::~RoutePath()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,9 @@ public:
|
||||||
RoutePath(const flightgear::FlightPlan* fp);
|
RoutePath(const flightgear::FlightPlan* fp);
|
||||||
~RoutePath();
|
~RoutePath();
|
||||||
|
|
||||||
|
RoutePath(const RoutePath& other);
|
||||||
|
RoutePath& operator=(const RoutePath& other);
|
||||||
|
|
||||||
SGGeodVec pathForIndex(int index) const;
|
SGGeodVec pathForIndex(int index) const;
|
||||||
|
|
||||||
SGGeod positionForIndex(int index) const;
|
SGGeod positionForIndex(int index) const;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "waypoint.hxx"
|
#include "waypoint.hxx"
|
||||||
|
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
|
#include <simgear/misc/strutils.hxx>
|
||||||
|
|
||||||
#include <Airports/airport.hxx>
|
#include <Airports/airport.hxx>
|
||||||
#include <Airports/runways.hxx>
|
#include <Airports/runways.hxx>
|
||||||
|
@ -69,6 +70,11 @@ void BasicWaypt::writeToProperties(SGPropertyNode_ptr aProp) const
|
||||||
aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
|
aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string BasicWaypt::icaoDescription() const
|
||||||
|
{
|
||||||
|
return simgear::strutils::formatGeodAsString(_pos, simgear::strutils::LatLonFormat::ICAO_ROUTE_DEGREES);
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
NavaidWaypoint::NavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner) :
|
NavaidWaypoint::NavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner) :
|
||||||
|
@ -514,7 +520,8 @@ Via::Via(RouteBase *aOwner) :
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Via::Via(RouteBase *aOwner, const std::string &airwayName, FGPositioned *to) :
|
Via::Via(RouteBase *aOwner, const std::string &airwayName,
|
||||||
|
FGPositioned *to) :
|
||||||
Waypt(aOwner),
|
Waypt(aOwner),
|
||||||
_airway(airwayName),
|
_airway(airwayName),
|
||||||
_to(to)
|
_to(to)
|
||||||
|
@ -529,16 +536,17 @@ Via::~Via()
|
||||||
void Via::initFromProperties(SGPropertyNode_ptr aProp)
|
void Via::initFromProperties(SGPropertyNode_ptr aProp)
|
||||||
{
|
{
|
||||||
if (!aProp->hasChild("airway") || !aProp->hasChild("to")) {
|
if (!aProp->hasChild("airway") || !aProp->hasChild("to")) {
|
||||||
throw sg_io_exception("missing airway/to propertie",
|
throw sg_io_exception("missing airway/to properties",
|
||||||
"Via::initFromProperties");
|
"Via::initFromProperties");
|
||||||
}
|
}
|
||||||
|
|
||||||
Waypt::initFromProperties(aProp);
|
Waypt::initFromProperties(aProp);
|
||||||
|
|
||||||
_airway = aProp->getStringValue("airway");
|
_airway = aProp->getStringValue("airway");
|
||||||
Airway* way = Airway::findByIdent(_airway);
|
|
||||||
|
Airway* way = Airway::findByIdent(_airway, Airway::UnknownLevel);
|
||||||
if (!way) {
|
if (!way) {
|
||||||
throw sg_io_exception("unknown airway idnet: '" + _airway + "'",
|
throw sg_io_exception("unknown airway ident: '" + _airway + "'",
|
||||||
"Via::initFromProperties");
|
"Via::initFromProperties");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,11 +557,15 @@ void Via::initFromProperties(SGPropertyNode_ptr aProp)
|
||||||
aProp->getDoubleValue("lat"));
|
aProp->getDoubleValue("lat"));
|
||||||
}
|
}
|
||||||
|
|
||||||
FGPositionedRef nav = FGPositioned::findClosestWithIdent(idn, p, NULL);
|
FGPositionedRef nav = FGPositioned::findClosestWithIdent(idn, p, nullptr);
|
||||||
if (!nav) {
|
if (!nav) {
|
||||||
throw sg_io_exception("unknown navaid ident:" + idn,
|
throw sg_io_exception("unknown navaid ident:" + idn,
|
||||||
"Via::initFromProperties");
|
"Via::initFromProperties");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!way->containsNavaid(nav)) {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, "VIA TO navaid: " << idn << " not found on airway " << _airway);
|
||||||
|
}
|
||||||
|
|
||||||
_to = nav;
|
_to = nav;
|
||||||
}
|
}
|
||||||
|
@ -574,12 +586,13 @@ WayptVec Via::expandToWaypoints(WayptRef aPreceeding) const
|
||||||
throw sg_exception("invalid preceeding waypoint");
|
throw sg_exception("invalid preceeding waypoint");
|
||||||
}
|
}
|
||||||
|
|
||||||
Airway* way = Airway::findByIdent(_airway);
|
WayptRef toWp = new NavaidWaypoint(_to, nullptr);
|
||||||
|
Airway* way = Airway::findByIdentAndVia(_airway, aPreceeding, toWp);
|
||||||
if (!way) {
|
if (!way) {
|
||||||
throw sg_exception("invalid airway");
|
throw sg_exception("invalid airway");
|
||||||
}
|
}
|
||||||
|
|
||||||
return way->via(aPreceeding, new NavaidWaypoint(_to, owner()));
|
return way->via(aPreceeding, toWp);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // of namespace
|
} // of namespace
|
||||||
|
|
|
@ -41,6 +41,7 @@ public:
|
||||||
virtual std::string ident() const
|
virtual std::string ident() const
|
||||||
{ return _ident; }
|
{ return _ident; }
|
||||||
|
|
||||||
|
std::string icaoDescription() const override;
|
||||||
protected:
|
protected:
|
||||||
virtual void initFromProperties(SGPropertyNode_ptr aProp);
|
virtual void initFromProperties(SGPropertyNode_ptr aProp);
|
||||||
virtual void writeToProperties(SGPropertyNode_ptr aProp) const;
|
virtual void writeToProperties(SGPropertyNode_ptr aProp) const;
|
||||||
|
@ -356,6 +357,8 @@ public:
|
||||||
|
|
||||||
virtual std::string ident() const;
|
virtual std::string ident() const;
|
||||||
|
|
||||||
|
std::string airway() const
|
||||||
|
{ return _airway; }
|
||||||
WayptVec expandToWaypoints(WayptRef aPreceeding) const;
|
WayptVec expandToWaypoints(WayptRef aPreceeding) const;
|
||||||
private:
|
private:
|
||||||
std::string _airway;
|
std::string _airway;
|
||||||
|
|
|
@ -2377,7 +2377,7 @@ static naRef f_createViaTo(naContext c, naRef me, int argc, naRef* args)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string airwayName = naStr_data(args[0]);
|
std::string airwayName = naStr_data(args[0]);
|
||||||
Airway* airway = Airway::findByIdent(airwayName);
|
Airway* airway = Airway::findByIdent(airwayName, Airway::UnknownLevel);
|
||||||
if (!airway) {
|
if (!airway) {
|
||||||
naRuntimeError(c, "createViaTo: couldn't find airway with provided name");
|
naRuntimeError(c, "createViaTo: couldn't find airway with provided name");
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <Navaids/waypoint.hxx>
|
#include <Navaids/waypoint.hxx>
|
||||||
#include <Navaids/navlist.hxx>
|
#include <Navaids/navlist.hxx>
|
||||||
#include <Navaids/navrecord.hxx>
|
#include <Navaids/navrecord.hxx>
|
||||||
|
#include <Navaids/airways.hxx>
|
||||||
|
|
||||||
#include <Airports/airport.hxx>
|
#include <Airports/airport.hxx>
|
||||||
|
|
||||||
|
@ -129,8 +130,6 @@ void FlightplanTests::testRoutePathSkipped()
|
||||||
// this tests skipping two preceeding points works as it should
|
// this tests skipping two preceeding points works as it should
|
||||||
SGGeodVec vec = rtepath.pathForIndex(6);
|
SGGeodVec vec = rtepath.pathForIndex(6);
|
||||||
CPPUNIT_ASSERT(vec.size() == 9);
|
CPPUNIT_ASSERT(vec.size() == 9);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlightplanTests::testRoutePathTrivialFlightPlan()
|
void FlightplanTests::testRoutePathTrivialFlightPlan()
|
||||||
|
@ -149,3 +148,81 @@ void FlightplanTests::testRoutePathTrivialFlightPlan()
|
||||||
|
|
||||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, fp1->totalDistanceNm(), 1e-9);
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, fp1->totalDistanceNm(), 1e-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlightplanTests::testBasicAirways()
|
||||||
|
{
|
||||||
|
Airway* awy = Airway::findByIdent("J547", Airway::HighLevel);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(awy->ident(), std::string("J547"));
|
||||||
|
|
||||||
|
FGAirportRef kord = FGAirport::findByIdent("KORD");
|
||||||
|
FlightPlanRef f = new FlightPlan;
|
||||||
|
f->setDeparture(kord);
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT(awy->findEnroute("KITOK"));
|
||||||
|
CPPUNIT_ASSERT(awy->findEnroute("LESUB"));
|
||||||
|
|
||||||
|
auto wpt = awy->findEnroute("FNT");
|
||||||
|
CPPUNIT_ASSERT(wpt);
|
||||||
|
CPPUNIT_ASSERT(wpt->ident() == "FNT");
|
||||||
|
CPPUNIT_ASSERT(wpt->source() == FGNavRecord::findClosestWithIdent("FNT", kord->geod()));
|
||||||
|
|
||||||
|
auto wptKUBBS = f->waypointFromString("KUBBS");
|
||||||
|
auto wptFNT = f->waypointFromString("FNT");
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT(awy->canVia(wptKUBBS, wptFNT));
|
||||||
|
|
||||||
|
WayptVec path = awy->via(wptKUBBS, wptFNT);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(4, static_cast<int>(path.size()));
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("PMM"), path.at(0)->ident());
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("HASTE"), path.at(1)->ident());
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("DEWIT"), path.at(2)->ident());
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("FNT"), path.at(3)->ident());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightplanTests::testAirwayNetworkRoute()
|
||||||
|
{
|
||||||
|
FGAirportRef egph = FGAirport::findByIdent("EGPH");
|
||||||
|
FlightPlanRef f = new FlightPlan;
|
||||||
|
f->setDeparture(egph);
|
||||||
|
|
||||||
|
auto highLevelNet = Airway::highLevel();
|
||||||
|
|
||||||
|
auto wptTLA = f->waypointFromString("TLA");
|
||||||
|
auto wptCNA = f->waypointFromString("CNA");
|
||||||
|
|
||||||
|
WayptVec route;
|
||||||
|
bool ok = highLevelNet->route(wptTLA, wptCNA, route);
|
||||||
|
CPPUNIT_ASSERT(ok);
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL(static_cast<int>(route.size()), 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightplanTests::testParseICAORoute()
|
||||||
|
{
|
||||||
|
FGAirportRef kord = FGAirport::findByIdent("KORD");
|
||||||
|
FlightPlanRef f = new FlightPlan;
|
||||||
|
f->setDeparture(kord);
|
||||||
|
f->setDestination(FGAirport::findByIdent("KSAN"));
|
||||||
|
|
||||||
|
const char* route = "DCT JOT J26 IRK J96 SLN J18 GCK J96 CIM J134 GUP J96 KEYKE J134 DRK J78 LANCY J96 PKE";
|
||||||
|
// const char* route = "DCT KUBBS J547 FNT Q824 HOCKE Q905 SIKBO Q907 MIILS N400A TUDEP NATW GISTI DCT SLANY UL9 DIKAS UL18 GAVGO UL9 KONAN UL607 UBIDU Y863 RUDUS T109 HAREM T104 ANORA STAR";
|
||||||
|
bool ok = f->parseICAORouteString(route);
|
||||||
|
CPPUNIT_ASSERT(ok);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlightplanTests::testParseICANLowLevelRoute()
|
||||||
|
{
|
||||||
|
const char* route = "DCT DPA V6 IOW V216 LAA V210 GOSIP V83 ACHES V210 BLOKE V83 ALS V210 RSK V95 INW V12 HOXOL V264 OATES V12 JUWSO V264 PKE";
|
||||||
|
|
||||||
|
FGAirportRef kord = FGAirport::findByIdent("KORD");
|
||||||
|
FlightPlanRef f = new FlightPlan;
|
||||||
|
f->setDeparture(kord);
|
||||||
|
f->setDestination(FGAirport::findByIdent("KSAN"));
|
||||||
|
|
||||||
|
bool ok = f->parseICAORouteString(route);
|
||||||
|
CPPUNIT_ASSERT(ok);
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,10 @@ class FlightplanTests : public CppUnit::TestFixture
|
||||||
CPPUNIT_TEST(testRoutePathBasic);
|
CPPUNIT_TEST(testRoutePathBasic);
|
||||||
CPPUNIT_TEST(testRoutePathSkipped);
|
CPPUNIT_TEST(testRoutePathSkipped);
|
||||||
CPPUNIT_TEST(testRoutePathTrivialFlightPlan);
|
CPPUNIT_TEST(testRoutePathTrivialFlightPlan);
|
||||||
|
CPPUNIT_TEST(testBasicAirways);
|
||||||
|
CPPUNIT_TEST(testAirwayNetworkRoute);
|
||||||
|
// CPPUNIT_TEST(testParseICAORoute);
|
||||||
|
// CPPUNIT_TEST(testParseICANLowLevelRoute);
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -49,6 +53,10 @@ public:
|
||||||
void testRoutePathBasic();
|
void testRoutePathBasic();
|
||||||
void testRoutePathSkipped();
|
void testRoutePathSkipped();
|
||||||
void testRoutePathTrivialFlightPlan();
|
void testRoutePathTrivialFlightPlan();
|
||||||
|
void testBasicAirways();
|
||||||
|
void testAirwayNetworkRoute();
|
||||||
|
void testParseICAORoute();
|
||||||
|
void testParseICANLowLevelRoute();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _FG_FLIGHTPLAN_UNIT_TESTS_HXX
|
#endif // _FG_FLIGHTPLAN_UNIT_TESTS_HXX
|
||||||
|
|
Loading…
Add table
Reference in a new issue