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
|
||||
#define FG_NAVCACHE_SCHEMA_HXX
|
||||
|
||||
const int SCHEMA_VERSION = 17;
|
||||
const int SCHEMA_VERSION = 19;
|
||||
|
||||
#define SCHEMA_SQL \
|
||||
"CREATE TABLE properties (key VARCHAR, value VARCHAR);" \
|
||||
|
@ -31,7 +31,8 @@ const int SCHEMA_VERSION = 17;
|
|||
"CREATE INDEX airway_ident ON airway(ident);" \
|
||||
\
|
||||
"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
|
||||
|
||||
|
|
|
@ -56,6 +56,25 @@ using std::vector;
|
|||
using std::endl;
|
||||
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 {
|
||||
|
||||
typedef std::vector<FlightPlan::DelegateFactory*> FPDelegateFactoryVec;
|
||||
|
@ -65,15 +84,16 @@ FlightPlan::FlightPlan() :
|
|||
_currentIndex(-1),
|
||||
_followLegTrackToFix(true),
|
||||
_aircraftCategory(ICAO_AIRCRAFT_CATEGORY_C),
|
||||
_departureRunway(NULL),
|
||||
_destinationRunway(NULL),
|
||||
_sid(NULL),
|
||||
_star(NULL),
|
||||
_approach(NULL),
|
||||
_departureRunway(nullptr),
|
||||
_destinationRunway(nullptr),
|
||||
_sid(nullptr),
|
||||
_star(nullptr),
|
||||
_approach(nullptr),
|
||||
_totalDistance(0.0)
|
||||
{
|
||||
_departureChanged = _arrivalChanged = _waypointsChanged = _currentWaypointChanged = false;
|
||||
|
||||
_cruiseDataChanged = false;
|
||||
|
||||
for (auto factory : static_delegateFactories) {
|
||||
Delegate* d = factory->createFlightPlanDelegate(this);
|
||||
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)
|
||||
{
|
||||
if (!aWpt) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WayptVec wps;
|
||||
|
@ -234,7 +254,8 @@ void FlightPlan::clear()
|
|||
_currentWaypointChanged = true;
|
||||
_arrivalChanged = true;
|
||||
_departureChanged = true;
|
||||
|
||||
_cruiseDataChanged = true;
|
||||
|
||||
_currentIndex = -1;
|
||||
for (Leg* l : _legs) {
|
||||
delete l;
|
||||
|
@ -382,14 +403,14 @@ int FlightPlan::findWayptIndex(const FGPositionedRef aPos) const
|
|||
FlightPlan::Leg* FlightPlan::currentLeg() const
|
||||
{
|
||||
if ((_currentIndex < 0) || (_currentIndex >= numLegs()))
|
||||
return NULL;
|
||||
return nullptr;
|
||||
return legAtIndex(_currentIndex);
|
||||
}
|
||||
|
||||
FlightPlan::Leg* FlightPlan::previousLeg() const
|
||||
{
|
||||
if (_currentIndex <= 0) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return legAtIndex(_currentIndex - 1);
|
||||
|
@ -398,7 +419,7 @@ FlightPlan::Leg* FlightPlan::previousLeg() const
|
|||
FlightPlan::Leg* FlightPlan::nextLeg() const
|
||||
{
|
||||
if ((_currentIndex < 0) || ((_currentIndex + 1) >= numLegs())) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return legAtIndex(_currentIndex + 1);
|
||||
|
@ -481,14 +502,14 @@ void FlightPlan::setSID(SID* sid, const std::string& transition)
|
|||
void FlightPlan::setSID(Transition* trans)
|
||||
{
|
||||
if (!trans) {
|
||||
setSID((SID*) NULL);
|
||||
setSID(static_cast<SID*>(nullptr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (trans->parent()->type() != PROCEDURE_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()
|
||||
|
@ -503,7 +524,7 @@ void FlightPlan::clearSID()
|
|||
Transition* FlightPlan::sidTransition() const
|
||||
{
|
||||
if (!_sid || _sidTransition.empty()) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return _sid->findTransitionByName(_sidTransition);
|
||||
|
@ -552,6 +573,19 @@ void FlightPlan::clearDestination()
|
|||
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)
|
||||
{
|
||||
if (_star == star) {
|
||||
|
@ -587,11 +621,31 @@ void FlightPlan::clearSTAR()
|
|||
_starTransition.clear();
|
||||
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
|
||||
{
|
||||
if (!_star || _starTransition.empty()) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return _star->findTransitionByName(_starTransition);
|
||||
|
@ -618,48 +672,25 @@ void FlightPlan::setApproach(flightgear::Approach *app)
|
|||
}
|
||||
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 {
|
||||
SGPropertyNode_ptr d(new SGPropertyNode);
|
||||
d->setIntValue("version", 2);
|
||||
|
||||
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
|
||||
saveToProperties(d);
|
||||
writeProperties(path, d, true /* write-all */);
|
||||
return true;
|
||||
} 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)
|
||||
{
|
||||
if (!path.exists())
|
||||
|
@ -700,12 +808,51 @@ bool FlightPlan::load(const SGPath& path)
|
|||
// mark data as unchanged since this is a clean plan
|
||||
_arrivalChanged = false;
|
||||
_departureChanged = false;
|
||||
|
||||
_cruiseDataChanged = true;
|
||||
_waypointsChanged = true;
|
||||
unlockDelegates();
|
||||
|
||||
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 */
|
||||
class GpxXmlVisitor : public XMLVisitor
|
||||
|
@ -835,6 +982,23 @@ bool FlightPlan::loadXmlFormat(const SGPath& path)
|
|||
|
||||
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
|
||||
SGPropertyNode* dep = routeData->getChild("departure");
|
||||
if (dep) {
|
||||
|
@ -847,9 +1011,14 @@ void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData)
|
|||
}
|
||||
|
||||
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")) {
|
||||
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")) {
|
||||
setApproach(_destination->findApproachWithIdent(dst->getStringValue("approach")));
|
||||
}
|
||||
}
|
||||
|
||||
// destination->setStringValue("transition", dst->getStringValue("transition"));
|
||||
}
|
||||
|
||||
// alternate
|
||||
SGPropertyNode* alt = routeData->getChild("alternate");
|
||||
if (alt) {
|
||||
//alternate->setStringValue(alt->getStringValue("airport"));
|
||||
if (routeData->hasChild("alternate")) {
|
||||
setAlternate((FGAirport*) fgFindAirportID(routeData->getStringValue("alternate")));
|
||||
}
|
||||
|
||||
// cruise
|
||||
SGPropertyNode* crs = routeData->getChild("cruise");
|
||||
if (crs) {
|
||||
// cruise->setDoubleValue("speed-kts", crs->getDoubleValue("speed-kts"));
|
||||
// cruise->setDoubleValue("mach", crs->getDoubleValue("mach"));
|
||||
// cruise->setDoubleValue("altitude-ft", crs->getDoubleValue("altitude-ft"));
|
||||
if (crs->hasChild("flight-level")) {
|
||||
_cruiseFlightLevel = crs->getIntValue("flight-level");
|
||||
} 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
|
||||
|
||||
}
|
||||
|
||||
void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData)
|
||||
|
@ -901,7 +1080,7 @@ void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData)
|
|||
if (routeNode.valid()) {
|
||||
for (int i=0; i<routeNode->nChildren(); ++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);
|
||||
} // of route iteration
|
||||
}
|
||||
|
@ -937,7 +1116,7 @@ WayptRef FlightPlan::parseVersion1XMLWaypt(SGPropertyNode* aWP)
|
|||
if (aWP->hasChild("longitude-deg")) {
|
||||
// explicit longitude/latitude
|
||||
w = new BasicWaypt(SGGeod::fromDeg(aWP->getDoubleValue("longitude-deg"),
|
||||
aWP->getDoubleValue("latitude-deg")), ident, NULL);
|
||||
aWP->getDoubleValue("latitude-deg")), ident, this);
|
||||
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
w = new BasicWaypt(pos, ident, NULL);
|
||||
w = new BasicWaypt(pos, ident, this);
|
||||
}
|
||||
|
||||
double altFt = aWP->getDoubleValue("altitude-ft", -9999.9);
|
||||
|
@ -1031,7 +1210,7 @@ WayptRef intersectionFromString(FGPositionedRef p1,
|
|||
FGPositionedRef p2 = FGPositioned::findClosestWithIdent(pieces[2], basePosition);
|
||||
if (!p2) {
|
||||
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()),
|
||||
|
@ -1048,7 +1227,7 @@ WayptRef intersectionFromString(FGPositionedRef p1,
|
|||
}
|
||||
|
||||
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)
|
||||
|
@ -1077,25 +1256,24 @@ WayptRef viaFromString(const SGGeod& basePosition, const std::string& target)
|
|||
string_list pieces(simgear::strutils::split(target, "/"));
|
||||
if ((pieces.size() != 4) || (pieces[2] != "TO")) {
|
||||
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]
|
||||
FGPositionedRef nav = FGPositioned::findClosestWithIdent(pieces[3], basePosition, NULL);
|
||||
if (!nav || !airway->containsNavaid(nav)) {
|
||||
SG_LOG( SG_NAVAID, SG_WARN, "TO navaid:" << pieces[3] << " unknown or not on airway");
|
||||
return NULL;
|
||||
FGPositionedRef nav = FGPositioned::findClosestWithIdent(pieces[3], basePosition, nullptr);
|
||||
if (!nav) {
|
||||
SG_LOG( SG_NAVAID, SG_WARN, "TO navaid:" << pieces[3] << " unknown");
|
||||
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 via;
|
||||
return new Via(nullptr, pieces[1], nav);
|
||||
}
|
||||
|
||||
} // of anonymous namespace
|
||||
|
@ -1138,31 +1316,31 @@ WayptRef FlightPlan::waypointFromString(const string& tgt )
|
|||
FGPositionedRef p = FGPositioned::findClosestWithIdent(pieces.front(), basePosition);
|
||||
if (!p) {
|
||||
SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front());
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pieces.size() == 1) {
|
||||
wpt = new NavaidWaypoint(p, NULL);
|
||||
wpt = new NavaidWaypoint(p, this);
|
||||
} else if (pieces.size() == 3) {
|
||||
// navaid/radial/distance-nm notation
|
||||
double radial = atof(pieces[1].c_str()),
|
||||
distanceNm = atof(pieces[2].c_str());
|
||||
radial += magvar;
|
||||
wpt = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm);
|
||||
wpt = new OffsetNavaidWaypoint(p, this, radial, distanceNm);
|
||||
} else if (pieces.size() == 2) {
|
||||
FGAirport* apt = dynamic_cast<FGAirport*>(p.ptr());
|
||||
if (!apt) {
|
||||
SG_LOG(SG_NAVAID, SG_INFO, "Waypoint is not an airport:" << pieces.front());
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!apt->hasRunwayWithIdent(pieces[1])) {
|
||||
SG_LOG(SG_NAVAID, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]);
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FGRunway* runway = apt->getRunwayByIdent(pieces[1]);
|
||||
wpt = new NavaidWaypoint(runway, NULL);
|
||||
wpt = new NavaidWaypoint(runway, this);
|
||||
} else if (pieces.size() == 4) {
|
||||
wpt = intersectionFromString(p, basePosition, magvar, pieces);
|
||||
}
|
||||
|
@ -1170,7 +1348,7 @@ WayptRef FlightPlan::waypointFromString(const string& tgt )
|
|||
|
||||
if (!wpt) {
|
||||
SG_LOG(SG_NAVAID, SG_INFO, "Unable to parse waypoint:" << target);
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (altSetting != RESTRICT_NONE) {
|
||||
|
@ -1185,7 +1363,7 @@ void FlightPlan::activate()
|
|||
FGRouteMgr* routeManager = globals->get_subsystem<FGRouteMgr>();
|
||||
if (routeManager) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1208,7 +1386,7 @@ void FlightPlan::activate()
|
|||
_legs.erase(it);
|
||||
delete l;
|
||||
|
||||
// create new lefs and insert
|
||||
// create new legs and insert
|
||||
it = _legs.begin();
|
||||
it += i;
|
||||
|
||||
|
@ -1233,14 +1411,11 @@ void FlightPlan::activate()
|
|||
|
||||
FlightPlan::Leg::Leg(FlightPlan* owner, WayptRef wpt) :
|
||||
_parent(owner),
|
||||
_speedRestrict(RESTRICT_NONE),
|
||||
_altRestrict(RESTRICT_NONE),
|
||||
_waypt(wpt)
|
||||
{
|
||||
if (!wpt.valid()) {
|
||||
throw sg_exception("can't create FlightPlan::Leg without underlying waypoint");
|
||||
}
|
||||
_speed = _altitudeFt = 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
_waypointsChanged = false;
|
||||
rebuildLegData();
|
||||
|
@ -1508,5 +1690,283 @@ void FlightPlan::setIcaoAircraftCategory(const std::string& cat)
|
|||
_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
|
||||
|
|
|
@ -35,6 +35,23 @@ class FlightPlan;
|
|||
|
||||
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
|
||||
{
|
||||
public:
|
||||
|
@ -53,7 +70,12 @@ public:
|
|||
std::string icaoAircraftCategory() const;
|
||||
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
|
||||
|
@ -62,7 +84,7 @@ public:
|
|||
{
|
||||
public:
|
||||
FlightPlan* owner() const
|
||||
{ return _parent; }
|
||||
{ return const_cast<FlightPlan*>(_parent); }
|
||||
|
||||
Waypt* waypoint() const
|
||||
{ return _waypt; }
|
||||
|
@ -87,6 +109,7 @@ public:
|
|||
double courseDeg() const;
|
||||
double distanceNm() const;
|
||||
double distanceAlongRoute() const;
|
||||
|
||||
private:
|
||||
friend class FlightPlan;
|
||||
|
||||
|
@ -94,16 +117,18 @@ public:
|
|||
|
||||
Leg* cloneFor(FlightPlan* owner) const;
|
||||
|
||||
FlightPlan* _parent;
|
||||
RouteRestriction _speedRestrict, _altRestrict;
|
||||
int _speed;
|
||||
int _altitudeFt;
|
||||
const FlightPlan* _parent;
|
||||
RouteRestriction _speedRestrict = RESTRICT_NONE,
|
||||
_altRestrict = RESTRICT_NONE;
|
||||
int _speed = 0;
|
||||
int _altitudeFt = 0;
|
||||
|
||||
WayptRef _waypt;
|
||||
/// length of this leg following the flown path
|
||||
mutable double _pathDistance;
|
||||
mutable double _courseDeg;
|
||||
mutable double _pathDistance = -1.0;
|
||||
mutable double _courseDeg = -1.0;
|
||||
/// total distance of this leg from departure point
|
||||
mutable double _distanceAlongPath;
|
||||
mutable double _distanceAlongPath = 11.0;
|
||||
};
|
||||
|
||||
class Delegate
|
||||
|
@ -114,6 +139,7 @@ public:
|
|||
virtual void departureChanged() { }
|
||||
virtual void arrivalChanged() { }
|
||||
virtual void waypointsChanged() { }
|
||||
virtual void cruiseChanged() { }
|
||||
virtual void cleared() { }
|
||||
virtual void activated() { }
|
||||
virtual void currentWaypointChanged() { }
|
||||
|
@ -150,7 +176,7 @@ public:
|
|||
Leg* previousLeg() const;
|
||||
|
||||
int numLegs() const
|
||||
{ return _legs.size(); }
|
||||
{ return static_cast<int>(_legs.size()); }
|
||||
|
||||
Leg* legAtIndex(int index) const;
|
||||
int findLegIndex(const Leg* l) const;
|
||||
|
@ -159,7 +185,10 @@ public:
|
|||
int findWayptIndex(const FGPositionedRef aPos) const;
|
||||
|
||||
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
|
||||
{ return _departure; }
|
||||
|
@ -197,6 +226,9 @@ public:
|
|||
|
||||
void clearDestination();
|
||||
|
||||
FGAirportRef alternate() const;
|
||||
void setAlternate(FGAirportRef alt);
|
||||
|
||||
/**
|
||||
* note setting an approach will implicitly update the destination
|
||||
* airport and runway to match
|
||||
|
@ -217,6 +249,17 @@ public:
|
|||
double totalDistanceNm() const
|
||||
{ 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
|
||||
* position on the route path. I.e the point 10nm before or after
|
||||
|
@ -233,6 +276,45 @@ public:
|
|||
*/
|
||||
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
|
||||
* flight-plan is created or loaded
|
||||
|
@ -258,8 +340,11 @@ private:
|
|||
bool _arrivalChanged,
|
||||
_departureChanged,
|
||||
_waypointsChanged,
|
||||
_currentWaypointChanged;
|
||||
_currentWaypointChanged,
|
||||
_cruiseDataChanged;
|
||||
|
||||
void saveToProperties(SGPropertyNode* d) const;
|
||||
|
||||
bool loadXmlFormat(const SGPath& path);
|
||||
bool loadGpxFormat(const SGPath& path);
|
||||
bool loadPlainTextFormat(const SGPath& path);
|
||||
|
@ -270,13 +355,26 @@ private:
|
|||
WayptRef parseVersion1XMLWaypt(SGPropertyNode* aWP);
|
||||
|
||||
double magvarDegAt(const SGGeod& pos) const;
|
||||
bool parseICAOLatLon(const std::string &s, SGGeod &p);
|
||||
|
||||
std::string _ident;
|
||||
std::string _callsign;
|
||||
std::string _remarks;
|
||||
std::string _aircraftType;
|
||||
|
||||
int _currentIndex;
|
||||
bool _followLegTrackToFix;
|
||||
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 _alternate;
|
||||
FGRunway* _departureRunway, *_destinationRunway;
|
||||
SGSharedPtr<SID> _sid;
|
||||
SGSharedPtr<STAR> _star;
|
||||
|
@ -289,7 +387,7 @@ private:
|
|||
typedef std::vector<Leg*> LegVec;
|
||||
LegVec _legs;
|
||||
|
||||
std::vector<Delegate*> _delegates;
|
||||
std::vector<Delegate*> _delegates;
|
||||
};
|
||||
|
||||
} // of namespace flightgear
|
||||
|
|
|
@ -19,9 +19,7 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
|
||||
#include "NavDataCache.hxx"
|
||||
|
||||
|
@ -31,9 +29,6 @@
|
|||
#include <cassert>
|
||||
#include <stdint.h> // for int64_t
|
||||
#include <sstream> // for std::ostringstream
|
||||
// boost
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
|
||||
#ifdef SYSTEM_SQLITE
|
||||
// the standard sqlite3.h doesn't give a way to set SQLITE_UINT64_TYPE,
|
||||
|
@ -79,6 +74,7 @@
|
|||
#include <Airports/gnnode.hxx>
|
||||
#include "CacheSchema.h"
|
||||
#include <GUI/MessageBox.hxx>
|
||||
#include <Navaids/airways.hxx>
|
||||
|
||||
using std::string;
|
||||
|
||||
|
@ -241,7 +237,7 @@ class NavDataCache::NavDataCachePrivate
|
|||
public:
|
||||
NavDataCachePrivate(const SGPath& p, NavDataCache* o) :
|
||||
outer(o),
|
||||
db(NULL),
|
||||
db(nullptr),
|
||||
path(p),
|
||||
readOnly(false),
|
||||
cacheHits(0),
|
||||
|
@ -323,7 +319,7 @@ public:
|
|||
|
||||
void close()
|
||||
{
|
||||
BOOST_FOREACH(sqlite3_stmt_ptr stmt, prepared) {
|
||||
for (sqlite3_stmt_ptr stmt : prepared) {
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
prepared.clear();
|
||||
|
@ -487,7 +483,7 @@ public:
|
|||
void initTables()
|
||||
{
|
||||
string_list commands = simgear::strutils::split(SCHEMA_SQL, ";");
|
||||
BOOST_FOREACH(std::string sql, commands) {
|
||||
for (std::string sql : commands) {
|
||||
if (sql.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -614,17 +610,19 @@ public:
|
|||
sqlite3_bind_int(findILS, 5, FGPositioned::LOC);
|
||||
|
||||
// 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) "
|
||||
"VALUES (?1, ?2)");
|
||||
|
||||
insertAirwayEdge = prepare("INSERT INTO airway_edge (network, airway, a, b) "
|
||||
"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");
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -836,7 +834,7 @@ public:
|
|||
|
||||
void flushDeferredOctreeUpdates()
|
||||
{
|
||||
BOOST_FOREACH(Octree::Branch* nd, deferredOctreeUpdates) {
|
||||
for (Octree::Branch* nd : deferredOctreeUpdates) {
|
||||
sqlite3_bind_int64(updateOctreeChildren, 1, nd->guid());
|
||||
sqlite3_bind_int(updateOctreeChildren, 2, nd->childMask());
|
||||
execUpdate(updateOctreeChildren);
|
||||
|
@ -902,9 +900,10 @@ public:
|
|||
sqlite3_stmt_ptr runwayLengthFtQuery;
|
||||
|
||||
// airways
|
||||
sqlite3_stmt_ptr findAirway, insertAirwayEdge,
|
||||
isPosInAirway, airwayEdgesFrom,
|
||||
sqlite3_stmt_ptr findAirway, findAirwayNet, insertAirwayEdge,
|
||||
isPosInAirway, airwayEdgesFrom, airwayEdgesTo,
|
||||
insertAirway, airwayEdges;
|
||||
sqlite3_stmt_ptr loadAirway;
|
||||
|
||||
// since there's many permutations of ident/name queries, we create
|
||||
// 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(
|
||||
simgear::Dir::TYPE_FILE | simgear::Dir::NO_DOT_OR_DOTDOT);
|
||||
|
||||
for (PathList::const_iterator filesIt = files.begin();
|
||||
filesIt != files.end(); filesIt++) {
|
||||
const std::string name = filesIt->file();
|
||||
for (auto f : files) {
|
||||
const std::string name = f.file();
|
||||
if (simgear::strutils::ends_with(name, ".dat") ||
|
||||
simgear::strutils::ends_with(name, ".dat.gz")) {
|
||||
result.paths.push_back(*filesIt);
|
||||
result.totalSize += filesIt->sizeInBytes();
|
||||
result.paths.push_back(f);
|
||||
result.totalSize += f.sizeInBytes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1484,7 +1482,7 @@ void NavDataCache::doRebuild()
|
|||
stampCacheFile(d->carrierDatPath);
|
||||
|
||||
st.stamp();
|
||||
Airway::load(d->airwayDatPath);
|
||||
Airway::loadAWYDat(d->airwayDatPath);
|
||||
stampCacheFile(d->airwayDatPath);
|
||||
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);
|
||||
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, 2, value);
|
||||
d->execInsert(d->writePropertyMulti);
|
||||
|
@ -2223,37 +2221,50 @@ NavDataCache::findILS(PositionedID airport, const string& aRunway, const string&
|
|||
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);
|
||||
sqlite_bind_stdstring(d->findAirway, 2, aName);
|
||||
sqlite3_bind_int(d->findAirwayNet, 1, network);
|
||||
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;
|
||||
if (d->execSelect(d->findAirway)) {
|
||||
// already exists
|
||||
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);
|
||||
return airway;
|
||||
}
|
||||
|
||||
|
||||
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, 2, airwayID);
|
||||
sqlite3_bind_int64(d->insertAirwayEdge, 3, from);
|
||||
sqlite3_bind_int64(d->insertAirwayEdge, 4, to);
|
||||
d->execInsert(d->insertAirwayEdge);
|
||||
|
||||
std::swap(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
bool NavDataCache::isInAirwayNetwork(int network, PositionedID pos)
|
||||
|
@ -2280,11 +2291,41 @@ AirwayEdgeVec NavDataCache::airwayEdgesFrom(int network, PositionedID pos)
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
d->reset(d->airwayEdges);
|
||||
sqlite3_bind_int(d->airwayEdges, 1, id);
|
||||
|
||||
typedef std::pair<PositionedID, PositionedID> Edge;
|
||||
|
@ -2301,55 +2342,97 @@ PositionedIDVec NavDataCache::airwayWaypts(int id)
|
|||
|
||||
d->reset(d->airwayEdges);
|
||||
if (rawEdges.empty()) {
|
||||
return PositionedIDVec();
|
||||
return {};
|
||||
}
|
||||
|
||||
// linearize
|
||||
PositionedIDDeque linearAirway;
|
||||
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();
|
||||
|
||||
PositionedIDVec result;
|
||||
|
||||
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();
|
||||
|
||||
// look for new segments
|
||||
if (e.first == firstId) {
|
||||
linearAirway.push_front(e.second);
|
||||
seen.insert(e.second);
|
||||
firstId = e.second;
|
||||
continue;
|
||||
|
||||
while (!rawEdges.empty()) {
|
||||
Edge e = rawEdges.front();
|
||||
rawEdges.pop_front();
|
||||
|
||||
bool seenFirst = (seen.find(e.first) != seen.end());
|
||||
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)
|
||||
|
|
|
@ -53,6 +53,9 @@ namespace Octree {
|
|||
class Branch;
|
||||
}
|
||||
|
||||
class Airway;
|
||||
using AirwayRef = SGSharedPtr<Airway>;
|
||||
|
||||
class NavDataCache
|
||||
{
|
||||
public:
|
||||
|
@ -268,7 +271,9 @@ public:
|
|||
TypedPositionedVec getOctreeLeafChildren(int64_t octreeNodeId);
|
||||
|
||||
// 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.
|
||||
|
@ -282,11 +287,13 @@ public:
|
|||
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
|
||||
*/
|
||||
AirwayEdgeVec airwayEdgesFrom(int network, PositionedID pos);
|
||||
|
||||
|
||||
AirwayRef loadAirway(int airwayID);
|
||||
|
||||
/**
|
||||
* Waypoints on the airway
|
||||
*/
|
||||
|
|
|
@ -47,6 +47,9 @@ using std::vector;
|
|||
namespace flightgear
|
||||
{
|
||||
|
||||
static std::vector<AirwayRef> static_airwaysCache;
|
||||
typedef SGSharedPtr<FGPositioned> FGPositionedRef;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class AStarOpenNode : public SGReferenced
|
||||
|
@ -56,8 +59,8 @@ public:
|
|||
int aAirway,
|
||||
FGPositionedRef aDest, AStarOpenNode* aPrev) :
|
||||
node(aNode),
|
||||
airway(aAirway),
|
||||
previous(aPrev)
|
||||
previous(aPrev),
|
||||
airway(aAirway)
|
||||
{
|
||||
distanceFromStart = aLegDist;
|
||||
if (previous) {
|
||||
|
@ -72,8 +75,8 @@ public:
|
|||
}
|
||||
|
||||
FGPositionedRef node;
|
||||
int airway;
|
||||
SGSharedPtr<AStarOpenNode> previous;
|
||||
int airway;
|
||||
double distanceFromStart; // aka 'g(x)'
|
||||
double directDistanceToDestination; // aka 'h(x)'
|
||||
|
||||
|
@ -85,17 +88,17 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<AStarOpenNode> AStarOpenNodeRef;
|
||||
using AStarOpenNodeRef = SGSharedPtr<AStarOpenNode>;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Airway::Network* Airway::lowLevel()
|
||||
{
|
||||
static Network* static_lowLevel = NULL;
|
||||
static Network* static_lowLevel = nullptr;
|
||||
|
||||
if (!static_lowLevel) {
|
||||
static_lowLevel = new Network;
|
||||
static_lowLevel->_networkID = 1;
|
||||
static_lowLevel = new Network;
|
||||
static_lowLevel->_networkID = Airway::LowLevel;
|
||||
}
|
||||
|
||||
return static_lowLevel;
|
||||
|
@ -103,29 +106,33 @@ Airway::Network* Airway::lowLevel()
|
|||
|
||||
Airway::Network* Airway::highLevel()
|
||||
{
|
||||
static Network* static_highLevel = NULL;
|
||||
static Network* static_highLevel = nullptr;
|
||||
if (!static_highLevel) {
|
||||
static_highLevel = new Network;
|
||||
static_highLevel->_networkID = 2;
|
||||
static_highLevel->_networkID = Airway::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),
|
||||
_level(level),
|
||||
_cacheId(dbId),
|
||||
_topAltitudeFt(aTop),
|
||||
_bottomAltitudeFt(aBottom)
|
||||
{
|
||||
static_airwaysCache.push_back(this);
|
||||
}
|
||||
|
||||
void Airway::load(const SGPath& path)
|
||||
void Airway::loadAWYDat(const SGPath& path)
|
||||
{
|
||||
std::string identStart, identEnd, name;
|
||||
double latStart, lonStart, latEnd, lonEnd;
|
||||
int type, base, top;
|
||||
//int airwayIndex = 0;
|
||||
//FGNode *n;
|
||||
|
||||
sg_gzifstream in( path );
|
||||
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 >> skipeol;
|
||||
|
||||
// type = 1; low-altitude
|
||||
// type = 2; high-altitude
|
||||
// type = 1; low-altitude (victor)
|
||||
// type = 2; high-altitude (jet)
|
||||
Network* net = (type == 1) ? lowLevel() : highLevel();
|
||||
|
||||
SGGeod startPos(SGGeod::fromDeg(lonStart, latStart)),
|
||||
endPos(SGGeod::fromDeg(lonEnd, latEnd));
|
||||
|
||||
int awy = net->findAirway(name, top, base);
|
||||
net->addEdge(awy, startPos, identStart, endPos, identEnd);
|
||||
|
||||
if (type == 1) {
|
||||
|
||||
} 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
|
||||
}
|
||||
|
||||
WayptVec::const_iterator Airway::find(WayptRef wpt) const
|
||||
{
|
||||
WayptVec::const_iterator it;
|
||||
for (it = _elements.begin(); it != _elements.end(); ++it) {
|
||||
if (wpt->matches(*it)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
return it;
|
||||
assert(!_elements.empty());
|
||||
return std::find_if(_elements.begin(), _elements.end(),
|
||||
[wpt] (const WayptRef& w) { return w->matches(wpt); });
|
||||
}
|
||||
|
||||
bool Airway::canVia(const WayptRef& from, const WayptRef& to) const
|
||||
{
|
||||
WayptVec::const_iterator fit = find(from);
|
||||
WayptVec::const_iterator tit = find(to);
|
||||
loadWaypoints();
|
||||
|
||||
auto fit = find(from);
|
||||
auto tit = find(to);
|
||||
|
||||
if ((fit == _elements.end()) || (tit == _elements.end())) {
|
||||
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;
|
||||
}
|
||||
|
||||
WayptVec Airway::via(const WayptRef& from, const WayptRef& to) const
|
||||
{
|
||||
loadWaypoints();
|
||||
|
||||
WayptVec v;
|
||||
WayptVec::const_iterator fit = find(from);
|
||||
WayptVec::const_iterator tit = find(to);
|
||||
auto fit = find(from);
|
||||
auto tit = find(to);
|
||||
|
||||
if ((fit == _elements.end()) || (tit == _elements.end())) {
|
||||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
Airway* Airway::findByIdent(const std::string& aIdent)
|
||||
{
|
||||
NavDataCache* ndc = NavDataCache::instance();
|
||||
|
||||
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);
|
||||
const Level level = _networkID;
|
||||
auto it = std::find_if(static_airwaysCache.begin(), static_airwaysCache.end(),
|
||||
[aName, level](const AirwayRef& awy)
|
||||
{ return (awy->_level == level) && (awy->ident() == aName); });
|
||||
if (it != static_airwaysCache.end()) {
|
||||
return (*it)->_cacheId;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
WayptVec::const_iterator it;
|
||||
for (it = _elements.begin(); it != _elements.end(); ++it) {
|
||||
if ((*it)->ident() == aIdent) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
return WayptRef();
|
||||
auto it = std::find_if(_elements.begin(), _elements.end(),
|
||||
[&aIdent](WayptRef w)
|
||||
{
|
||||
if (!w) return false;
|
||||
return w->ident() == aIdent;
|
||||
});
|
||||
|
||||
if (it != _elements.end())
|
||||
return *it;
|
||||
return {};
|
||||
}
|
||||
|
||||
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>
|
||||
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
|
||||
|
@ -391,7 +520,7 @@ public:
|
|||
{ return FGPositioned::WAYPOINT; }
|
||||
|
||||
virtual FGPositioned::Type maxType() const
|
||||
{ return FGPositioned::NDB; }
|
||||
{ return FGPositioned::VOR; }
|
||||
|
||||
private:
|
||||
Airway::Network* _net;
|
||||
|
@ -418,14 +547,20 @@ typedef vector<AStarOpenNodeRef> OpenNodeHeap;
|
|||
static void buildWaypoints(AStarOpenNodeRef aNode, WayptVec& aRoute)
|
||||
{
|
||||
// count the route length, and hence pre-size aRoute
|
||||
int count = 0;
|
||||
size_t count = 0;
|
||||
AStarOpenNodeRef n = aNode;
|
||||
for (; n != NULL; ++count, n = n->previous) {;}
|
||||
for (; n != nullptr; ++count, n = n->previous) {;}
|
||||
aRoute.resize(count);
|
||||
|
||||
// run over the route, creating waypoints
|
||||
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
|
||||
|
@ -462,7 +597,7 @@ bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest,
|
|||
ClosedNodeSet closedNodes;
|
||||
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
|
||||
while (!openNodes.empty()) {
|
||||
|
@ -485,7 +620,7 @@ bool Airway::Network::search2(FGPositionedRef aStart, FGPositionedRef aDest,
|
|||
|
||||
// adjacent (neighbour) iteration
|
||||
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)) {
|
||||
continue; // closed, ignore
|
||||
}
|
||||
|
|
|
@ -35,14 +35,30 @@ namespace flightgear {
|
|||
struct SearchContext;
|
||||
class AdjacentWaypoint;
|
||||
class InAirwayFilter;
|
||||
class Airway;
|
||||
|
||||
class Airway
|
||||
using AirwayRef = SGSharedPtr<Airway>;
|
||||
|
||||
class Airway : public RouteBase
|
||||
{
|
||||
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; }
|
||||
|
||||
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
|
||||
{ return _topAltitudeFt; }
|
||||
|
@ -50,7 +66,21 @@ public:
|
|||
double bottomAltitudeFt() const
|
||||
{ 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;
|
||||
|
||||
|
@ -60,6 +90,8 @@ public:
|
|||
|
||||
bool containsNavaid(const FGPositionedRef& navaid) const;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Track a network of airways
|
||||
*
|
||||
|
@ -81,13 +113,18 @@ public:
|
|||
*/
|
||||
bool route(WayptRef aFrom, WayptRef aTo, WayptVec& aPath);
|
||||
|
||||
/**
|
||||
* Overloaded version working with a raw SGGeod
|
||||
*/
|
||||
|
||||
std::pair<FGPositionedRef, bool> findClosestNode(const SGGeod& aGeod);
|
||||
|
||||
private:
|
||||
void addEdge(int aWay, const SGGeod& aStartPos,
|
||||
const std::string& aStartIdent,
|
||||
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 exactTo, bool exactFrom);
|
||||
|
@ -113,19 +150,13 @@ public:
|
|||
*/
|
||||
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
|
||||
*/
|
||||
typedef std::map<PositionedID, bool> NetworkMembershipDict;
|
||||
mutable NetworkMembershipDict _inNetworkCache;
|
||||
|
||||
int _networkID;
|
||||
Level _networkID;
|
||||
};
|
||||
|
||||
|
||||
|
@ -133,17 +164,23 @@ public:
|
|||
static Network* lowLevel();
|
||||
|
||||
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;
|
||||
|
||||
friend class Network;
|
||||
|
||||
std::string _ident;
|
||||
double _topAltitudeFt;
|
||||
double _bottomAltitudeFt;
|
||||
|
||||
WayptVec _elements;
|
||||
friend class NavDataCache;
|
||||
|
||||
const std::string _ident;
|
||||
const Level _level;
|
||||
const int _cacheId;
|
||||
|
||||
int _topAltitudeFt;
|
||||
int _bottomAltitudeFt;
|
||||
|
||||
mutable WayptVec _elements;
|
||||
};
|
||||
|
||||
} // of namespace flightgear
|
||||
|
|
|
@ -265,13 +265,28 @@ Transition* ArrivalDeparture::findTransitionByEnroute(Waypt* aEnroute) const
|
|||
WptTransitionMap::const_iterator eit;
|
||||
for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
|
||||
if (eit->second->enroute()->matches(aEnroute)) {
|
||||
SG_LOG(SG_NAVAID, SG_INFO, ident() << " using enroute transition " << eit->second->ident());
|
||||
return eit->second;
|
||||
}
|
||||
} // of enroute transition iteration
|
||||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -204,7 +204,9 @@ public:
|
|||
* transitions in a human-meaningful way (including persistence).
|
||||
*/
|
||||
Transition* findTransitionByName(const std::string& aIdent) const;
|
||||
|
||||
|
||||
Transition* findTransitionByEnroute(FGPositioned* aEnroute) const;
|
||||
|
||||
Transition* findTransitionByEnroute(Waypt* aEnroute) const;
|
||||
protected:
|
||||
|
||||
|
|
|
@ -18,9 +18,7 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
|
||||
#include "route.hxx"
|
||||
|
||||
|
@ -49,6 +47,7 @@
|
|||
#include <Navaids/waypoint.hxx>
|
||||
#include <Navaids/LevelDXML.hxx>
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Navaids/airways.hxx>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
@ -65,12 +64,7 @@ bool isMachRestrict(RouteRestriction rr)
|
|||
}
|
||||
|
||||
Waypt::Waypt(RouteBase* aOwner) :
|
||||
_altitudeFt(0.0),
|
||||
_speed(0.0),
|
||||
_altRestrict(RESTRICT_NONE),
|
||||
_speedRestrict(RESTRICT_NONE),
|
||||
_owner(aOwner),
|
||||
_flags(0),
|
||||
_magVarDeg(NO_MAG_VAR)
|
||||
{
|
||||
}
|
||||
|
@ -81,7 +75,7 @@ Waypt::~Waypt()
|
|||
|
||||
std::string Waypt::ident() const
|
||||
{
|
||||
return "";
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Waypt::flag(WayptFlag aFlag) const
|
||||
|
@ -108,7 +102,11 @@ bool Waypt::matches(Waypt* aOther) const
|
|||
|
||||
return matches(aOther->position());
|
||||
}
|
||||
|
||||
|
||||
bool Waypt::matches(FGPositioned* aPos) const
|
||||
{
|
||||
return aPos && (aPos == source());
|
||||
}
|
||||
|
||||
bool Waypt::matches(const SGGeod& aPos) const
|
||||
{
|
||||
|
@ -158,6 +156,11 @@ double Waypt::headingRadialDeg() const
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
std::string Waypt::icaoDescription() const
|
||||
{
|
||||
return ident();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 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") {
|
||||
r = new BasicWaypt(aOwner);
|
||||
} else if (aTypeName == "navaid") {
|
||||
|
@ -233,6 +236,16 @@ WayptRef Waypt::createFromProperties(RouteBase* aOwner, SGPropertyNode_ptr aProp
|
|||
"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 {
|
||||
WayptRef nd(createInstance(aOwner, aProp->getStringValue("type")));
|
||||
nd->initFromProperties(aProp);
|
||||
|
@ -280,6 +293,10 @@ void Waypt::initFromProperties(SGPropertyNode_ptr aProp)
|
|||
setFlag(WPT_MISS, aProp->getBoolValue("miss"));
|
||||
}
|
||||
|
||||
if (aProp->hasChild("airway")) {
|
||||
setFlag(WPT_VIA, true);
|
||||
}
|
||||
|
||||
if (aProp->hasChild("alt-restrict")) {
|
||||
_altRestrict = restrictionFromString(aProp->getStringValue("alt-restrict"));
|
||||
_altitudeFt = aProp->getDoubleValue("altitude-ft");
|
||||
|
@ -311,6 +328,12 @@ void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const
|
|||
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)) {
|
||||
aProp->setBoolValue("miss", true);
|
||||
}
|
||||
|
|
|
@ -48,7 +48,9 @@ namespace flightgear
|
|||
class RouteBase;
|
||||
class Waypt;
|
||||
class NavdataVisitor;
|
||||
|
||||
class Airway;
|
||||
|
||||
using AirwayRef = SGSharedPtr<Airway>;
|
||||
typedef SGSharedPtr<Waypt> WayptRef;
|
||||
|
||||
typedef enum {
|
||||
|
@ -67,11 +69,14 @@ typedef enum {
|
|||
|
||||
WPT_DEPARTURE = 1 << 8,
|
||||
WPT_ARRIVAL = 1 << 9,
|
||||
|
||||
|
||||
/// waypoint generated by VNAV / speed management profile,
|
||||
/// for step climbs or top of descent
|
||||
WPT_PSEUDO = 1 << 10,
|
||||
WPT_APPROACH = 1 << 11
|
||||
WPT_APPROACH = 1 << 11,
|
||||
|
||||
/// waypoint prodcued by expanding a VIA segment
|
||||
WPT_VIA = 1 << 12
|
||||
} WayptFlag;
|
||||
|
||||
typedef enum {
|
||||
|
@ -89,15 +94,16 @@ bool isMachRestrict(RouteRestriction rr);
|
|||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
public:
|
||||
virtual ~Waypt();
|
||||
|
||||
RouteBase* owner() const
|
||||
{ return _owner; }
|
||||
RouteBase* owner() const
|
||||
{ return const_cast<RouteBase*>(_owner); }
|
||||
|
||||
virtual SGGeod position() const = 0;
|
||||
|
||||
|
@ -105,7 +111,7 @@ public:
|
|||
* The Positioned associated with this element, if one exists
|
||||
*/
|
||||
virtual FGPositioned* source() const
|
||||
{ return NULL; }
|
||||
{ return nullptr; }
|
||||
|
||||
virtual double altitudeFt() const
|
||||
{ return _altitudeFt; }
|
||||
|
@ -157,6 +163,12 @@ public:
|
|||
*/
|
||||
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'
|
||||
* 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.
|
||||
*/
|
||||
virtual double headingRadialDeg() const;
|
||||
|
||||
/**
|
||||
* @brief icaoDescription - description of the waypoint in ICAO route plan format
|
||||
* @return
|
||||
*/
|
||||
virtual std::string icaoDescription() const;
|
||||
|
||||
protected:
|
||||
friend class NavdataVisitor;
|
||||
|
||||
|
@ -195,20 +214,20 @@ protected:
|
|||
typedef Waypt* (FactoryFunction)(RouteBase* aOwner) ;
|
||||
static void registerFactory(const std::string aNodeType, FactoryFunction* aFactory);
|
||||
|
||||
double _altitudeFt;
|
||||
double _speed; // knots IAS or mach
|
||||
RouteRestriction _altRestrict;
|
||||
RouteRestriction _speedRestrict;
|
||||
double _altitudeFt = 0.0;
|
||||
double _speed = 0.0; // knots IAS or mach
|
||||
RouteRestriction _altRestrict = RESTRICT_NONE;
|
||||
RouteRestriction _speedRestrict = RESTRICT_NONE;
|
||||
private:
|
||||
|
||||
/**
|
||||
* 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;
|
||||
unsigned short _flags;
|
||||
mutable double _magVarDeg;
|
||||
const RouteBase* _owner = nullptr;
|
||||
unsigned short _flags = 0;
|
||||
mutable double _magVarDeg = 0.0; ///< cached mag var at this location
|
||||
};
|
||||
|
||||
typedef std::vector<WayptRef> WayptVec;
|
||||
|
|
|
@ -185,14 +185,10 @@ SGGeod turnCenterFromExit(const SGGeod& pt, double outHeadingDeg,
|
|||
|
||||
struct TurnInfo
|
||||
{
|
||||
TurnInfo() : valid(false),
|
||||
inboundCourseDeg(0.0),
|
||||
turnAngleDeg(0.0) { }
|
||||
|
||||
bool valid;
|
||||
SGGeod turnCenter;
|
||||
double inboundCourseDeg;
|
||||
double turnAngleDeg;
|
||||
double inboundCourseDeg = 0.0;
|
||||
double turnAngleDeg = 0.0;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -337,7 +333,7 @@ public:
|
|||
(wpt->type() == "via"))
|
||||
{
|
||||
// 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") {
|
||||
FGRunway* rwy = static_cast<RunwayWaypt*>(wpt.get())->runway();
|
||||
flyOver = true;
|
||||
|
@ -840,7 +836,7 @@ RoutePath::RoutePath(const flightgear::FlightPlan* fp) :
|
|||
d(new RoutePathPrivate)
|
||||
{
|
||||
for (int l=0; l<fp->numLegs(); ++l) {
|
||||
Waypt *wpt = fp->legAtIndex(l)->waypoint();
|
||||
WayptRef wpt = fp->legAtIndex(l)->waypoint();
|
||||
if (!wpt) {
|
||||
SG_LOG(SG_NAVAID, SG_DEV_ALERT, "Waypoint " << l << " of " << fp->numLegs() << "is NULL");
|
||||
break;
|
||||
|
@ -852,6 +848,18 @@ RoutePath::RoutePath(const flightgear::FlightPlan* fp) :
|
|||
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()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -42,6 +42,9 @@ public:
|
|||
RoutePath(const flightgear::FlightPlan* fp);
|
||||
~RoutePath();
|
||||
|
||||
RoutePath(const RoutePath& other);
|
||||
RoutePath& operator=(const RoutePath& other);
|
||||
|
||||
SGGeodVec pathForIndex(int index) const;
|
||||
|
||||
SGGeod positionForIndex(int index) const;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "waypoint.hxx"
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Airports/runways.hxx>
|
||||
|
@ -69,6 +70,11 @@ void BasicWaypt::writeToProperties(SGPropertyNode_ptr aProp) const
|
|||
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) :
|
||||
|
@ -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),
|
||||
_airway(airwayName),
|
||||
_to(to)
|
||||
|
@ -529,16 +536,17 @@ Via::~Via()
|
|||
void Via::initFromProperties(SGPropertyNode_ptr aProp)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
Waypt::initFromProperties(aProp);
|
||||
|
||||
_airway = aProp->getStringValue("airway");
|
||||
Airway* way = Airway::findByIdent(_airway);
|
||||
|
||||
Airway* way = Airway::findByIdent(_airway, Airway::UnknownLevel);
|
||||
if (!way) {
|
||||
throw sg_io_exception("unknown airway idnet: '" + _airway + "'",
|
||||
throw sg_io_exception("unknown airway ident: '" + _airway + "'",
|
||||
"Via::initFromProperties");
|
||||
}
|
||||
|
||||
|
@ -549,11 +557,15 @@ void Via::initFromProperties(SGPropertyNode_ptr aProp)
|
|||
aProp->getDoubleValue("lat"));
|
||||
}
|
||||
|
||||
FGPositionedRef nav = FGPositioned::findClosestWithIdent(idn, p, NULL);
|
||||
FGPositionedRef nav = FGPositioned::findClosestWithIdent(idn, p, nullptr);
|
||||
if (!nav) {
|
||||
throw sg_io_exception("unknown navaid ident:" + idn,
|
||||
"Via::initFromProperties");
|
||||
}
|
||||
|
||||
if (!way->containsNavaid(nav)) {
|
||||
SG_LOG(SG_AUTOPILOT, SG_WARN, "VIA TO navaid: " << idn << " not found on airway " << _airway);
|
||||
}
|
||||
|
||||
_to = nav;
|
||||
}
|
||||
|
@ -574,12 +586,13 @@ WayptVec Via::expandToWaypoints(WayptRef aPreceeding) const
|
|||
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) {
|
||||
throw sg_exception("invalid airway");
|
||||
}
|
||||
|
||||
return way->via(aPreceeding, new NavaidWaypoint(_to, owner()));
|
||||
return way->via(aPreceeding, toWp);
|
||||
}
|
||||
|
||||
} // of namespace
|
||||
|
|
|
@ -41,6 +41,7 @@ public:
|
|||
virtual std::string ident() const
|
||||
{ return _ident; }
|
||||
|
||||
std::string icaoDescription() const override;
|
||||
protected:
|
||||
virtual void initFromProperties(SGPropertyNode_ptr aProp);
|
||||
virtual void writeToProperties(SGPropertyNode_ptr aProp) const;
|
||||
|
@ -356,6 +357,8 @@ public:
|
|||
|
||||
virtual std::string ident() const;
|
||||
|
||||
std::string airway() const
|
||||
{ return _airway; }
|
||||
WayptVec expandToWaypoints(WayptRef aPreceeding) const;
|
||||
private:
|
||||
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]);
|
||||
Airway* airway = Airway::findByIdent(airwayName);
|
||||
Airway* airway = Airway::findByIdent(airwayName, Airway::UnknownLevel);
|
||||
if (!airway) {
|
||||
naRuntimeError(c, "createViaTo: couldn't find airway with provided name");
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <Navaids/waypoint.hxx>
|
||||
#include <Navaids/navlist.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
#include <Navaids/airways.hxx>
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
|
||||
|
@ -129,8 +130,6 @@ void FlightplanTests::testRoutePathSkipped()
|
|||
// this tests skipping two preceeding points works as it should
|
||||
SGGeodVec vec = rtepath.pathForIndex(6);
|
||||
CPPUNIT_ASSERT(vec.size() == 9);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void FlightplanTests::testRoutePathTrivialFlightPlan()
|
||||
|
@ -149,3 +148,81 @@ void FlightplanTests::testRoutePathTrivialFlightPlan()
|
|||
|
||||
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(testRoutePathSkipped);
|
||||
CPPUNIT_TEST(testRoutePathTrivialFlightPlan);
|
||||
CPPUNIT_TEST(testBasicAirways);
|
||||
CPPUNIT_TEST(testAirwayNetworkRoute);
|
||||
// CPPUNIT_TEST(testParseICAORoute);
|
||||
// CPPUNIT_TEST(testParseICANLowLevelRoute);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
|
@ -49,6 +53,10 @@ public:
|
|||
void testRoutePathBasic();
|
||||
void testRoutePathSkipped();
|
||||
void testRoutePathTrivialFlightPlan();
|
||||
void testBasicAirways();
|
||||
void testAirwayNetworkRoute();
|
||||
void testParseICAORoute();
|
||||
void testParseICANLowLevelRoute();
|
||||
};
|
||||
|
||||
#endif // _FG_FLIGHTPLAN_UNIT_TESTS_HXX
|
||||
|
|
Loading…
Reference in a new issue