From 1b7b69b49874ad369e80222529d5b29bfd863a49 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 24 Apr 2012 22:55:30 +0100 Subject: [PATCH] Create a real FlightPlan (and Leg) class Convert the route-manager to use a flight-plan internally, and expose flightplan, leg and procedure data to Nasal. Move the Level-D parser into its own file. --- src/Airports/runways.cxx | 17 +- src/Airports/runways.hxx | 8 +- src/Airports/simple.cxx | 136 +- src/Airports/simple.hxx | 19 +- src/Autopilot/route_mgr.cxx | 1293 +++++----------- src/Autopilot/route_mgr.hxx | 113 +- src/GUI/MapWidget.cxx | 2 +- src/GUI/WaypointList.cxx | 33 +- src/Instrumentation/NavDisplay.cxx | 41 +- src/Instrumentation/gps.cxx | 10 +- src/Instrumentation/rnav_waypt_controller.cxx | 10 +- src/Navaids/CMakeLists.txt | 6 +- src/Navaids/LevelDXML.cxx | 337 +++++ src/Navaids/LevelDXML.hxx | 66 + src/Navaids/airways.hxx | 2 +- src/Navaids/procedure.cxx | 138 +- src/Navaids/procedure.hxx | 97 +- src/Navaids/route.cxx | 1297 +++++++++++++---- src/Navaids/route.hxx | 251 +++- src/Navaids/routePath.cxx | 15 +- src/Navaids/routePath.hxx | 5 +- src/Navaids/waypoint.cxx | 38 +- src/Navaids/waypoint.hxx | 38 +- src/Scripting/NasalPositioned.cxx | 805 +++++++++- 24 files changed, 3111 insertions(+), 1666 deletions(-) create mode 100644 src/Navaids/LevelDXML.cxx create mode 100644 src/Navaids/LevelDXML.hxx diff --git a/src/Airports/runways.cxx b/src/Airports/runways.cxx index adbd22600..e5adba4f6 100644 --- a/src/Airports/runways.cxx +++ b/src/Airports/runways.cxx @@ -172,7 +172,7 @@ void FGRunway::setReciprocalRunway(FGRunway* other) _reciprocal = other; } -std::vector FGRunway::getSIDs() +std::vector FGRunway::getSIDs() const { std::vector result; for (unsigned int i=0; i<_airport->numSIDs(); ++i) { @@ -185,7 +185,7 @@ std::vector FGRunway::getSIDs() return result; } -std::vector FGRunway::getSTARs() +std::vector FGRunway::getSTARs() const { std::vector result; for (unsigned int i=0; i<_airport->numSTARs(); ++i) { @@ -198,3 +198,16 @@ std::vector FGRunway::getSTARs() return result; } +std::vector FGRunway::getApproaches() const +{ + std::vector result; + for (unsigned int i=0; i<_airport->numApproaches(); ++i) { + flightgear::Approach* s = _airport->getApproachByIndex(i); + if (s->runway() == this) { + result.push_back(s); + } + } // of approaches at the airport iteration + + return result; +} + diff --git a/src/Airports/runways.hxx b/src/Airports/runways.hxx index 9ae89aa7d..651f1e68e 100644 --- a/src/Airports/runways.hxx +++ b/src/Airports/runways.hxx @@ -36,6 +36,7 @@ class SGPropertyNode; namespace flightgear { class SID; class STAR; + class Approach; } class FGRunway : public FGRunwayBase @@ -124,12 +125,15 @@ public: /** * Get SIDs (DPs) associated with this runway */ - std::vector getSIDs(); + std::vector getSIDs() const; /** * Get STARs associared with this runway */ - std::vector getSTARs(); + std::vector getSTARs() const; + + + std::vector getApproaches() const; }; diff --git a/src/Airports/simple.cxx b/src/Airports/simple.cxx index 7ef5b642b..af3945a61 100644 --- a/src/Airports/simple.cxx +++ b/src/Airports/simple.cxx @@ -406,7 +406,7 @@ void FGAirport::loadProcedures() const } SG_LOG(SG_GENERAL, SG_INFO, ident() << ": loading procedures from " << path.str()); - Route::loadAirportProcedures(path, const_cast(this)); + RouteBase::loadAirportProcedures(path, const_cast(this)); } void FGAirport::loadSceneryDefinitions() const @@ -469,128 +469,6 @@ void FGAirport::readTowerData(SGPropertyNode* aRoot) _tower_location = SGGeod::fromDegM(lon, lat, fieldElevationM + elevM); } -bool FGAirport::buildApproach(Waypt* aEnroute, STAR* aSTAR, FGRunway* aRwy, WayptVec& aRoute) -{ - loadProcedures(); - - if ((aRwy && (aRwy->airport() != this))) { - throw sg_exception("invalid parameters", "FGAirport::buildApproach"); - } - - if (aSTAR) { - bool ok = aSTAR->route(aRwy, aEnroute, aRoute); - if (!ok) { - SG_LOG(SG_GENERAL, SG_WARN, ident() << ": build approach, STAR " << aSTAR->ident() - << " failed to route from transition " << aEnroute->ident()); - return false; - } - } else if (aEnroute) { - // no a STAR specified, just use enroute point directly - aRoute.push_back(aEnroute); - } - - if (!aRwy) { - // no runway selected yet, but we loaded the STAR, so that's fine, we're done - return true; - } - -// build the approach (possibly including transition), and including the missed segment - vector aps; - for (unsigned int j=0; jrunway() == aRwy) { - aps.push_back(mApproaches[j]); - } - } // of approach filter by runway - - if (aps.empty()) { - SG_LOG(SG_GENERAL, SG_INFO, ident() << "; no approaches defined for runway " << aRwy->ident()); - // could build a fallback approach here - return false; - } - - for (unsigned int k=0; kroute(aRoute.back(), aRoute)) { - return true; - } - } // of initial approach iteration - - SG_LOG(SG_GENERAL, SG_INFO, ident() << ": unable to find transition to runway " - << aRwy->ident() << ", assume vectors"); - - WayptRef v(new ATCVectors(NULL, this)); - aRoute.push_back(v); - return aps.front()->routeFromVectors(aRoute); -} - -pair -FGAirport::selectSID(const SGGeod& aDest, FGRunway* aRwy) -{ - loadProcedures(); - - WayptRef enroute; - flightgear::SID* sid = NULL; - double d = 1e9; - - for (unsigned int i=0; iisForRunway(aRwy)) { - continue; - } - - WayptRef e = mSIDs[i]->findBestTransition(aDest); - if (!e) { - continue; // strange, but let's not worry about it - } - - // assert(e->isFixedPosition()); - double ed = SGGeodesy::distanceM(aDest, e->position()); - if (ed < d) { // new best match - enroute = e; - d = ed; - sid = mSIDs[i]; - } - } // of SID iteration - - if (!mSIDs.empty() && !sid) { - SG_LOG(SG_GENERAL, SG_INFO, ident() << "selectSID, no SID found (runway=" - << (aRwy ? aRwy->ident() : "no runway preference")); - } - - return std::make_pair(sid, enroute); -} - -pair -FGAirport::selectSTAR(const SGGeod& aOrigin, FGRunway* aRwy) -{ - loadProcedures(); - - WayptRef enroute; - STAR* star = NULL; - double d = 1e9; - - for (unsigned int i=0; iisForRunway(aRwy)) { - continue; - } - - SG_LOG(SG_GENERAL, SG_INFO, "STAR " << mSTARs[i]->ident() << " is valid for runway"); - WayptRef e = mSTARs[i]->findBestTransition(aOrigin); - if (!e) { - continue; // strange, but let's not worry about it - } - - // assert(e->isFixedPosition()); - double ed = SGGeodesy::distanceM(aOrigin, e->position()); - if (ed < d) { // new best match - enroute = e; - d = ed; - star = mSTARs[i]; - } - } // of STAR iteration - - return std::make_pair(star, enroute); -} - - void FGAirport::addSID(flightgear::SID* aSid) { mSIDs.push_back(aSid); @@ -666,6 +544,18 @@ Approach* FGAirport::getApproachByIndex(unsigned int aIndex) const return mApproaches[aIndex]; } +Approach* FGAirport::findApproachWithIdent(const std::string& aIdent) const +{ + loadProcedures(); + for (unsigned int i=0; iident() == aIdent) { + return mApproaches[i]; + } + } + + return NULL; +} + void FGAirport::setCommStations(CommStationList& comms) { mCommStations.swap(comms); diff --git a/src/Airports/simple.hxx b/src/Airports/simple.hxx index c2eafbbe1..6995e6976 100644 --- a/src/Airports/simple.hxx +++ b/src/Airports/simple.hxx @@ -188,7 +188,8 @@ public: unsigned int numApproaches() const; flightgear::Approach* getApproachByIndex(unsigned int aIndex) const; - + flightgear::Approach* findApproachWithIdent(const std::string& aIdent) const; + /** * Syntactic wrapper around FGPositioned::findClosest - find the closest * match for filter, and return it cast to FGAirport. The default filter @@ -216,22 +217,6 @@ public: */ static char** searchNamesAndIdents(const std::string& aFilter); - bool buildApproach(flightgear::Waypt* aEnroute, flightgear::STAR* aSTAR, - FGRunway* aRwy, flightgear::WayptVec& aRoute); - - /** - * Given a destiation point, select the best SID and transition waypt from - * this airport. Returns (NULL,NULL) is no SIDs are defined, otherwise the - * best SID/transition is that which is closest to the destination point. - */ - std::pair selectSID(const SGGeod& aDest, FGRunway* aRwy); - - /** - * Select a STAR and enroute transition waypt, given an origin (departure) position. - * returns (NULL, NULL) is no suitable STAR is exists - */ - std::pair selectSTAR(const SGGeod& aOrigin, FGRunway* aRwy); - void setCommStations(flightgear::CommStationList& comms); flightgear::CommStationList commStationsOfType(FGPositioned::Type aTy) const; diff --git a/src/Autopilot/route_mgr.cxx b/src/Autopilot/route_mgr.cxx index b16318da7..80313d70a 100644 --- a/src/Autopilot/route_mgr.cxx +++ b/src/Autopilot/route_mgr.cxx @@ -63,53 +63,6 @@ using namespace flightgear; -class PropertyWatcher : public SGPropertyChangeListener -{ -public: - void watch(SGPropertyNode* p) - { - p->addChangeListener(this, false); - } - - virtual void valueChanged(SGPropertyNode*) - { - fire(); - } -protected: - virtual void fire() = 0; -}; - -/** - * Template adapter, created by convenience helper below - */ -template -class MethodPropertyWatcher : public PropertyWatcher -{ -public: - typedef void (T::*fire_method)(); - - MethodPropertyWatcher(T* obj, fire_method m) : - _object(obj), - _method(m) - { ; } - -protected: - virtual void fire() - { // dispatch to the object method we're helping - (_object->*_method)(); - } - -private: - T* _object; - fire_method _method; -}; - -template -PropertyWatcher* createWatcher(T* obj, void (T::*m)()) -{ - return new MethodPropertyWatcher(obj, m); -} - static bool commandLoadFlightPlan(const SGPropertyNode* arg) { FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); @@ -148,7 +101,7 @@ static bool commandSetActiveWaypt(const SGPropertyNode* arg) { FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); int index = arg->getIntValue("index"); - if ((index < 0) || (index >= self->numWaypts())) { + if ((index < 0) || (index >= self->numLegs())) { return false; } @@ -232,16 +185,16 @@ static bool commandInsertWaypt(const SGPropertyNode* arg) } else { return false; // failed to build waypoint } - + + FlightPlan::Leg* leg = self->flightPlan()->insertWayptAtIndex(wp, index); if (alt >= 0) { - wp->setAltitude(alt, flightgear::RESTRICT_AT); + leg->setAltitude(RESTRICT_AT, alt); } if (ias > 0) { - wp->setSpeed(ias, flightgear::RESTRICT_AT); + leg->setSpeed(RESTRICT_AT, ias); } - - self->insertWayptAtIndex(wp, index); + return true; } @@ -249,18 +202,16 @@ static bool commandDeleteWaypt(const SGPropertyNode* arg) { FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); int index = arg->getIntValue("index"); - self->removeWayptAtIndex(index); + self->removeLegAtIndex(index); return true; } ///////////////////////////////////////////////////////////////////////////// FGRouteMgr::FGRouteMgr() : - _currentIndex(0), + _plan(NULL), input(fgGetNode( RM "input", true )), - mirror(fgGetNode( RM "route", true )), - _departureWatcher(NULL), - _arrivalWatcher(NULL) + mirror(fgGetNode( RM "route", true )) { listener = new InputListener(this); input->setStringValue(""); @@ -280,32 +231,29 @@ FGRouteMgr::~FGRouteMgr() { input->removeChangeListener(listener); delete listener; - delete _departureWatcher; - delete _arrivalWatcher; } void FGRouteMgr::init() { SGPropertyNode_ptr rm(fgGetNode(RM)); - lon = fgGetNode( "/position/longitude-deg", true ); - lat = fgGetNode( "/position/latitude-deg", true ); - alt = fgGetNode( "/position/altitude-ft", true ); magvar = fgGetNode("/environment/magnetic-variation-deg", true); departure = fgGetNode(RM "departure", true); departure->tie("airport", SGRawValueMethods(*this, &FGRouteMgr::getDepartureICAO, &FGRouteMgr::setDepartureICAO)); + departure->tie("runway", SGRawValueMethods(*this, + &FGRouteMgr::getDepartureRunway, + &FGRouteMgr::setDepartureRunway)); + departure->tie("sid", SGRawValueMethods(*this, + &FGRouteMgr::getSID, + &FGRouteMgr::setSID)); + departure->tie("name", SGRawValueMethods(*this, &FGRouteMgr::getDepartureName, NULL)); - departure->setStringValue("runway", ""); - - delete _departureWatcher; - _departureWatcher = createWatcher(this, &FGRouteMgr::departureChanged); - _departureWatcher->watch(departure->getChild("runway")); - + departure->tie("field-elevation-ft", SGRawValueMethods(*this, + &FGRouteMgr::getDestinationFieldElevation, NULL)); departure->getChild("etd", 0, true); - _departureWatcher->watch(departure->getChild("sid", 0, true)); departure->getChild("takeoff-time", 0, true); destination = fgGetNode(RM "destination", true); @@ -313,16 +261,22 @@ void FGRouteMgr::init() { destination->tie("airport", SGRawValueMethods(*this, &FGRouteMgr::getDestinationICAO, &FGRouteMgr::setDestinationICAO)); + destination->tie("runway", SGRawValueMethods(*this, + &FGRouteMgr::getDestinationRunway, + &FGRouteMgr::setDestinationRunway)); + destination->tie("star", SGRawValueMethods(*this, + &FGRouteMgr::getSTAR, + &FGRouteMgr::setSTAR)); + destination->tie("approach", SGRawValueMethods(*this, + &FGRouteMgr::getApproach, + &FGRouteMgr::setApproach)); + destination->tie("name", SGRawValueMethods(*this, &FGRouteMgr::getDestinationName, NULL)); - - delete _arrivalWatcher; - _arrivalWatcher = createWatcher(this, &FGRouteMgr::arrivalChanged); - _arrivalWatcher->watch(destination->getChild("runway", 0, true)); + destination->tie("field-elevation-ft", SGRawValueMethods(*this, + &FGRouteMgr::getDestinationFieldElevation, NULL)); destination->getChild("eta", 0, true); - _arrivalWatcher->watch(destination->getChild("star", 0, true)); - _arrivalWatcher->watch(destination->getChild("transition", 0, true)); destination->getChild("touchdown-time", 0, true); alternate = fgGetNode(RM "alternate", true); @@ -379,8 +333,8 @@ void FGRouteMgr::init() { wpn->getChild("dist", 0, true); wpn->getChild("eta", 0, true); - update_mirror(); _pathNode = fgGetNode(RM "file-path", 0, true); + setFlightPlan(new FlightPlan()); } @@ -401,11 +355,11 @@ void FGRouteMgr::postinit() for (it = waypoints->begin(); it != waypoints->end(); ++it) { WayptRef w = waypointFromString(*it); if (w) { - _route.push_back(w); + _plan->insertWayptAtIndex(w, -1); } } - SG_LOG(SG_AUTOPILOT, SG_INFO, "loaded initial waypoints:" << _route.size()); + SG_LOG(SG_AUTOPILOT, SG_INFO, "loaded initial waypoints:" << numLegs()); update_mirror(); } @@ -421,6 +375,53 @@ bool FGRouteMgr::isRouteActive() const return active->getBoolValue(); } +bool FGRouteMgr::saveRoute(const SGPath& p) +{ + if (!_plan) { + return false; + } + + return _plan->save(p); +} + +bool FGRouteMgr::loadRoute(const SGPath& p) +{ + FlightPlan* fp = new FlightPlan; + if (!fp->load(p)) { + delete fp; + return false; + } + + setFlightPlan(fp); + return true; +} + +FlightPlan* FGRouteMgr::flightPlan() const +{ + return _plan; +} + +void FGRouteMgr::setFlightPlan(FlightPlan* plan) +{ + if (plan == _plan) { + return; + } + + if (_plan) { + delete _plan; + active->setBoolValue(false); + } + + _plan = plan; + _plan->setDelegate(this); + +// fire all the callbacks! + departureChanged(); + arrivalChanged(); + waypointsChanged(); + currentWaypointChanged(); +} + void FGRouteMgr::update( double dt ) { if (dt <= 0.0) { @@ -446,17 +447,16 @@ void FGRouteMgr::update( double dt ) } // basic course/distance information - SGGeod currentPos = SGGeod::fromDegFt(lon->getDoubleValue(), - lat->getDoubleValue(),alt->getDoubleValue()); + SGGeod currentPos = globals->get_aircraft_position(); - Waypt* curWpt = currentWaypt(); - if (!curWpt) { + FlightPlan::Leg* leg = _plan ? _plan->currentLeg() : NULL; + if (!leg) { return; } double courseDeg; double distanceM; - boost::tie(courseDeg, distanceM) = curWpt->courseAndDistanceFrom(currentPos); + boost::tie(courseDeg, distanceM) = leg->waypoint()->courseAndDistanceFrom(currentPos); // update wp0 / wp1 / wp-last wp0->setDoubleValue("dist", distanceM * SG_METER_TO_NM); @@ -465,42 +465,85 @@ void FGRouteMgr::update( double dt ) wp0->setDoubleValue("bearing-deg", courseDeg); setETAPropertyFromDistance(wp0->getChild("eta"), distanceM); - double totalPathDistance = totalDistance->getDoubleValue() * SG_NM_TO_METER; - double totalDistanceRemaining = distanceM; // distance to current waypoint - double pathDistance = cachedWaypointPathTotalDistance(_currentIndex); + double totalPathDistanceNm = _plan->totalDistanceNm(); + double totalDistanceRemaining = distanceM * SG_METER_TO_NM; // distance to current waypoint // total distance to go, is direct distance to wp0, plus the remaining // path distance from wp0 - totalDistanceRemaining += (totalPathDistance - pathDistance); + totalDistanceRemaining += (totalPathDistanceNm - leg->distanceAlongRoute()); wp0->setDoubleValue("distance-along-route-nm", - pathDistance * SG_METER_TO_NM); + leg->distanceAlongRoute()); wp0->setDoubleValue("remaining-distance-nm", - (totalPathDistance - pathDistance) * SG_METER_TO_NM); + totalPathDistanceNm - leg->distanceAlongRoute()); - Waypt* nextWpt = nextWaypt(); - if (nextWpt) { - boost::tie(courseDeg, distanceM) = nextWpt->courseAndDistanceFrom(currentPos); + FlightPlan::Leg* nextLeg = _plan->nextLeg(); + if (nextLeg) { + boost::tie(courseDeg, distanceM) = nextLeg->waypoint()->courseAndDistanceFrom(currentPos); wp1->setDoubleValue("dist", distanceM * SG_METER_TO_NM); wp1->setDoubleValue("true-bearing-deg", courseDeg); courseDeg -= magvar->getDoubleValue(); // expose magnetic bearing wp1->setDoubleValue("bearing-deg", courseDeg); - setETAPropertyFromDistance(wp1->getChild("eta"), distanceM); - - double pathDistance = cachedWaypointPathTotalDistance(_currentIndex + 1); + setETAPropertyFromDistance(wp1->getChild("eta"), distanceM); wp1->setDoubleValue("distance-along-route-nm", - pathDistance * SG_METER_TO_NM); + nextLeg->distanceAlongRoute()); wp1->setDoubleValue("remaining-distance-nm", - (totalPathDistance - pathDistance) * SG_METER_TO_NM); + totalPathDistanceNm - nextLeg->distanceAlongRoute()); } - distanceToGo->setDoubleValue(totalDistanceRemaining * SG_METER_TO_NM); - wpn->setDoubleValue("dist", totalDistanceRemaining * SG_METER_TO_NM); - ete->setDoubleValue(totalDistanceRemaining * SG_METER_TO_NM / groundSpeed * 3600.0); + distanceToGo->setDoubleValue(totalDistanceRemaining); + wpn->setDoubleValue("dist", totalDistanceRemaining); + ete->setDoubleValue(totalDistanceRemaining / groundSpeed * 3600.0); setETAPropertyFromDistance(wpn->getChild("eta"), totalDistanceRemaining); } +void FGRouteMgr::clearRoute() +{ + if (_plan) { + _plan->clear(); + } +} + +Waypt* FGRouteMgr::currentWaypt() const +{ + if (_plan && _plan->currentLeg()) { + return _plan->currentLeg()->waypoint(); + } + + return NULL; +} + +int FGRouteMgr::currentIndex() const +{ + if (!_plan) { + return 0; + } + + return _plan->currentIndex(); +} + +Waypt* FGRouteMgr::wayptAtIndex(int index) const +{ + if (_plan) { + FlightPlan::Leg* leg = _plan->legAtIndex(index); + if (leg) { + return leg->waypoint(); + } + } + + return NULL; +} + +int FGRouteMgr::numLegs() const +{ + if (_plan) { + return _plan->numLegs(); + } + + return 0; +} + void FGRouteMgr::setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDistance) { double speed = fgGetDouble("/velocities/groundspeed-kt", 0.0); @@ -525,62 +568,15 @@ void FGRouteMgr::setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDi aProp->setStringValue( eta_str ); } -flightgear::WayptRef FGRouteMgr::removeWayptAtIndex(int aIndex) +void FGRouteMgr::removeLegAtIndex(int aIndex) { - int index = aIndex; - if (aIndex < 0) { // negative indices count the the end - index = _route.size() + index; + if (!_plan) { + return; } - if ((index < 0) || (index >= numWaypts())) { - SG_LOG(SG_AUTOPILOT, SG_WARN, "removeWayptAtIndex with invalid index:" << aIndex); - return NULL; - } - WayptVec::iterator it = _route.begin(); - it += index; - - WayptRef w = *it; // hold a ref now, in case _route is the only other owner - _route.erase(it); - - update_mirror(); - - if (_currentIndex == index) { - currentWaypointChanged(); // current waypoint was removed - } - else - if (_currentIndex > index) { - --_currentIndex; // shift current index down if necessary - } - - _edited->fireValueChanged(); - checkFinished(); - - return w; + _plan->deleteIndex(aIndex); } -struct NotGeneratedWayptPredicate : public std::unary_function -{ - bool operator() (const Waypt* w) const - { - return (w->flag(WPT_GENERATED) == false); - } -}; - - -void FGRouteMgr::clearRoute() -{ -// erase all non-generated waypoints - WayptVec::iterator r = - std::remove_if(_route.begin(), _route.end(), NotGeneratedWayptPredicate()); - _route.erase(r, _route.end()); - - _currentIndex = -1; - - update_mirror(); - active->setBoolValue(false); - _edited->fireValueChanged(); -} - /** * route between index-1 and index, using airways. */ @@ -590,27 +586,27 @@ bool FGRouteMgr::routeToIndex(int index, RouteType aRouteType) WayptRef wp2; if (index == -1) { - index = _route.size(); // can still be zero, of course + index = numLegs(); } if (index == 0) { - if (!_departure) { + if (!_plan->departureAirport()) { SG_LOG(SG_AUTOPILOT, SG_WARN, "routeToIndex: no departure set"); return false; } - wp1 = new NavaidWaypoint(_departure.get(), NULL); + wp1 = new NavaidWaypoint(_plan->departureAirport().get(), NULL); } else { wp1 = wayptAtIndex(index - 1); } - if (index >= numWaypts()) { - if (!_destination) { + if (index >= numLegs()) { + if (!_plan->destinationAirport()) { SG_LOG(SG_AUTOPILOT, SG_WARN, "routeToIndex: no destination set"); return false; } - wp2 = new NavaidWaypoint(_destination.get(), NULL); + wp2 = new NavaidWaypoint(_plan->destinationAirport().get(), NULL); } else { wp2 = wayptAtIndex(index); } @@ -640,374 +636,108 @@ bool FGRouteMgr::routeToIndex(int index, RouteType aRouteType) return false; } - WayptVec::iterator it = _route.begin(); - it += index; - _route.insert(it, r.begin(), r.end()); - - update_mirror(); - _edited->fireValueChanged(); + _plan->insertWayptsAtIndex(r, index); return true; } -void FGRouteMgr::autoRoute() -{ - if (!_departure || !_destination) { - return; - } - - string runwayId(departure->getStringValue("runway")); - FGRunway* runway = NULL; - if (_departure->hasRunwayWithIdent(runwayId)) { - runway = _departure->getRunwayByIdent(runwayId); - } - - FGRunway* dstRunway = NULL; - runwayId = destination->getStringValue("runway"); - if (_destination->hasRunwayWithIdent(runwayId)) { - dstRunway = _destination->getRunwayByIdent(runwayId); - } - - _route.clear(); // clear out the existing, first -// SID - flightgear::SID* sid; - WayptRef sidTrans; - - boost::tie(sid, sidTrans) = _departure->selectSID(_destination->geod(), runway); - if (sid) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "selected SID " << sid->ident()); - if (sidTrans) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "\tvia " << sidTrans->ident() << " transition"); - } - - sid->route(runway, sidTrans, _route); - departure->setStringValue("sid", sid->ident()); - } else { - // use airport location for airway search - sidTrans = new NavaidWaypoint(_departure.get(), NULL); - departure->setStringValue("sid", ""); - } - -// STAR - destination->setStringValue("transition", ""); - destination->setStringValue("star", ""); - - STAR* star; - WayptRef starTrans; - boost::tie(star, starTrans) = _destination->selectSTAR(_departure->geod(), dstRunway); - if (star) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "selected STAR " << star->ident()); - if (starTrans) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "\tvia " << starTrans->ident() << " transition"); - destination->setStringValue("transition", starTrans->ident()); - } - destination->setStringValue("star", star->ident()); - } else { - // use airport location for search - starTrans = new NavaidWaypoint(_destination.get(), NULL); - } - -// route between them - WayptVec airwayRoute; - if (Airway::highLevel()->route(sidTrans, starTrans, airwayRoute)) { - _route.insert(_route.end(), airwayRoute.begin(), airwayRoute.end()); - } - -// add the STAR if we have one - if (star) { - _destination->buildApproach(starTrans, star, dstRunway, _route); - } - - update_mirror(); - _edited->fireValueChanged(); -} - void FGRouteMgr::departureChanged() { -// remove existing departure waypoints - WayptVec::iterator it = _route.begin(); - for (; it != _route.end(); ++it) { - if (!(*it)->flag(WPT_DEPARTURE)) { - break; - } - } - - // erase() invalidates iterators, so grab now + _plan->clearWayptsWithFlag(WPT_DEPARTURE); WayptRef enroute; - if (it == _route.end()) { - if (_destination) { - enroute = new NavaidWaypoint(_destination.get(), NULL); - } - } else { - enroute = *it; - } - - _route.erase(_route.begin(), it); - if (!_departure) { - waypointsChanged(); - return; - } - WayptVec wps; buildDeparture(enroute, wps); - for (it = wps.begin(); it != wps.end(); ++it) { - (*it)->setFlag(WPT_DEPARTURE); - (*it)->setFlag(WPT_GENERATED); - } - _route.insert(_route.begin(), wps.begin(), wps.end()); - - update_mirror(); - waypointsChanged(); + _plan->insertWayptsAtIndex(wps, 0); } void FGRouteMgr::buildDeparture(WayptRef enroute, WayptVec& wps) { - string runwayId(departure->getStringValue("runway")); - if (!_departure->hasRunwayWithIdent(runwayId)) { -// valid airport, but no runway selected, so just the airport noide itself - wps.push_back(new NavaidWaypoint(_departure.get(), NULL)); + if (!_plan->departureAirport()) { return; } - FGRunway* r = _departure->getRunwayByIdent(runwayId); - string sidId = departure->getStringValue("sid"); - flightgear::SID* sid = _departure->findSIDWithIdent(sidId); - if (!sid) { -// valid runway, but no SID selected/found, so just the runway node for now - if (!sidId.empty() && (sidId != "(none)")) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "SID not found:" << sidId); - } - - wps.push_back(new RunwayWaypt(r, NULL)); + if (!_plan->departureRunway()) { +// valid airport, but no runway selected, so just the airport _plan itself + WayptRef w = new NavaidWaypoint(_plan->departureAirport(), _plan); + w->setFlag(WPT_DEPARTURE); + wps.push_back(w); return; } -// we have a valid SID, awesome - string trans(departure->getStringValue("transition")); - WayptRef t = sid->findTransitionByName(trans); - if (!t && enroute) { - t = sid->findBestTransition(enroute->position()); - } - - sid->route(r, t, wps); - if (!wps.empty() && wps.front()->flag(WPT_DYNAMIC)) { - // ensure first waypoint is static, to simplify other computations - wps.insert(wps.begin(), new RunwayWaypt(r, NULL)); + WayptRef rwyWaypt = new RunwayWaypt(_plan->departureRunway(), _plan); + rwyWaypt->setFlag(WPT_DEPARTURE); + wps.push_back(rwyWaypt); + + if (!_plan->sid()) { + return; } + + _plan->sid()->route(_plan->departureRunway(), _plan->sidTransition(), wps); } void FGRouteMgr::arrivalChanged() { - // remove existing arrival waypoints - WayptVec::reverse_iterator rit = _route.rbegin(); - for (; rit != _route.rend(); ++rit) { - if (!(*rit)->flag(WPT_ARRIVAL)) { - break; - } - } - - // erase() invalidates iterators, so grab now - WayptRef enroute; - WayptVec::iterator it; - - if (rit != _route.rend()) { - enroute = *rit; - it = rit.base(); // convert to fwd iterator - } else { - it = _route.begin(); - } - - _route.erase(it, _route.end()); - + _plan->clearWayptsWithFlag(WPT_ARRIVAL); + _plan->clearWayptsWithFlag(WPT_APPROACH); WayptVec wps; + WayptRef enroute; buildArrival(enroute, wps); - for (it = wps.begin(); it != wps.end(); ++it) { - (*it)->setFlag(WPT_ARRIVAL); - (*it)->setFlag(WPT_GENERATED); - } - _route.insert(_route.end(), wps.begin(), wps.end()); - - update_mirror(); - waypointsChanged(); + _plan->insertWayptsAtIndex(wps, -1); } void FGRouteMgr::buildArrival(WayptRef enroute, WayptVec& wps) { - if (!_destination) { + FGAirportRef apt = _plan->departureAirport(); + if (!apt.valid()) { return; } - string runwayId(destination->getStringValue("runway")); - if (!_destination->hasRunwayWithIdent(runwayId)) { -// valid airport, but no runway selected, so just the airport node itself - wps.push_back(new NavaidWaypoint(_destination.get(), NULL)); + if (!_plan->destinationRunway()) { + WayptRef w = new NavaidWaypoint(apt.ptr(), _plan); + w->setFlag(WPT_ARRIVAL); + wps.push_back(w); return; } - FGRunway* r = _destination->getRunwayByIdent(runwayId); - string starId = destination->getStringValue("star"); - STAR* star = _destination->findSTARWithIdent(starId); - if (!star) { -// valid runway, but no STAR selected/found, so just the runway node for now - wps.push_back(new RunwayWaypt(r, NULL)); - return; + if (_plan->star()) { + _plan->star()->route(_plan->destinationRunway(), _plan->starTransition(), wps); } -// we have a valid STAR - string trans(destination->getStringValue("transition")); - WayptRef t = star->findTransitionByName(trans); - if (!t && enroute) { - t = star->findBestTransition(enroute->position()); + if (_plan->approach()) { + _plan->approach()->route(wps.back(), wps); + } else { + WayptRef w = new RunwayWaypt(_plan->destinationRunway(), _plan); + w->setFlag(WPT_APPROACH); + wps.push_back(w); } - - _destination->buildApproach(t, star, r, wps); } void FGRouteMgr::waypointsChanged() { - -} - -void FGRouteMgr::insertWayptAtIndex(Waypt* aWpt, int aIndex) -{ - if (!aWpt) { - return; - } - - int index = aIndex; - if ((aIndex == -1) || (aIndex > (int) _route.size())) { - index = _route.size(); - } - - WayptVec::iterator it = _route.begin(); - it += index; - - if (_currentIndex >= index) { - ++_currentIndex; - } - - _route.insert(it, aWpt); - update_mirror(); - _edited->fireValueChanged(); -} - -WayptRef FGRouteMgr::waypointFromString(const string& tgt ) -{ - string target(boost::to_upper_copy(tgt)); - WayptRef wpt; - -// extract altitude - double altFt = cruise->getDoubleValue("altitude-ft"); - RouteRestriction altSetting = RESTRICT_NONE; - - size_t pos = target.find( '@' ); - if ( pos != string::npos ) { - altFt = atof( target.c_str() + pos + 1 ); - target = target.substr( 0, pos ); - if ( !strcmp(fgGetString("/sim/startup/units"), "meter") ) - altFt *= SG_METER_TO_FEET; - altSetting = RESTRICT_AT; - } - -// check for lon,lat - pos = target.find( ',' ); - if ( pos != string::npos ) { - double lon = atof( target.substr(0, pos).c_str()); - double lat = atof( target.c_str() + pos + 1); - char buf[32]; - char ew = (lon < 0.0) ? 'W' : 'E'; - char ns = (lat < 0.0) ? 'S' : 'N'; - snprintf(buf, 32, "%c%03d%c%03d", ew, (int) fabs(lon), ns, (int)fabs(lat)); - - wpt = new BasicWaypt(SGGeod::fromDeg(lon, lat), buf, NULL); - if (altSetting != RESTRICT_NONE) { - wpt->setAltitude(altFt, altSetting); - } - return wpt; - } - - SGGeod basePosition; - if (_route.empty()) { - // route is empty, use current position - basePosition = SGGeod::fromDeg(lon->getDoubleValue(), lat->getDoubleValue()); - } else { - basePosition = _route.back()->position(); - } - - string_list pieces(simgear::strutils::split(target, "/")); - FGPositionedRef p = FGPositioned::findClosestWithIdent(pieces.front(), basePosition); - if (!p) { - SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front()); - return NULL; - } - - if (pieces.size() == 1) { - wpt = new NavaidWaypoint(p, NULL); - } 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->getDoubleValue(); // convert to true bearing - wpt = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm); - } else if (pieces.size() == 2) { - FGAirport* apt = dynamic_cast(p.ptr()); - if (!apt) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "Waypoint is not an airport:" << pieces.front()); - return NULL; - } - - if (!apt->hasRunwayWithIdent(pieces[1])) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]); - return NULL; - } - - FGRunway* runway = apt->getRunwayByIdent(pieces[1]); - wpt = new NavaidWaypoint(runway, NULL); - } else if (pieces.size() == 4) { - // navid/radial/navid/radial notation - FGPositionedRef p2 = FGPositioned::findClosestWithIdent(pieces[2], basePosition); - if (!p2) { - SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << pieces[2]); - return NULL; - } - - double r1 = atof(pieces[1].c_str()), - r2 = atof(pieces[3].c_str()); - r1 += magvar->getDoubleValue(); - r2 += magvar->getDoubleValue(); - - SGGeod intersection; - bool ok = SGGeodesy::radialIntersection(p->geod(), r1, p2->geod(), r2, intersection); - if (!ok) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "no valid intersection for:" << target); - return NULL; - } - - std::string name = p->ident() + "-" + p2->ident(); - wpt = new BasicWaypt(intersection, name, NULL); - } - - if (!wpt) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "Unable to parse waypoint:" << target); - return NULL; - } - - if (altSetting != RESTRICT_NONE) { - wpt->setAltitude(altFt, altSetting); - } - return wpt; + _edited->fireValueChanged(); + checkFinished(); } // mirror internal route to the property system for inspection by other subsystems void FGRouteMgr::update_mirror() { mirror->removeChildren("wp"); + NewGUI * gui = (NewGUI *)globals->get_subsystem("gui"); + FGDialog* rmDlg = gui ? gui->getDialog("route-manager") : NULL; + + if (!_plan) { + mirror->setIntValue("num", 0); + if (rmDlg) { + rmDlg->updateValues(); + } + return; + } - int num = numWaypts(); - double totalDistanceEnroute = 0.0; + int num = _plan->numLegs(); for (int i = 0; i < num; i++) { - Waypt* wp = _route[i]; + FlightPlan::Leg* leg = _plan->legAtIndex(i); + WayptRef wp = leg->waypoint(); SGPropertyNode *prop = mirror->getChild("wp", i, 1); const SGGeod& pos(wp->position()); @@ -1016,18 +746,13 @@ void FGRouteMgr::update_mirror() prop->setDoubleValue("latitude-deg",pos.getLatitudeDeg()); // leg course+distance - if (i < (num - 1)) { - Waypt* next = _route[i+1]; - std::pair crsDist = - next->courseAndDistanceFrom(pos); - prop->setDoubleValue("leg-bearing-true-deg", crsDist.first); - prop->setDoubleValue("leg-distance-nm", crsDist.second * SG_METER_TO_NM); - prop->setDoubleValue("distance-along-route-nm", totalDistanceEnroute); - totalDistanceEnroute += crsDist.second * SG_METER_TO_NM; - } + + prop->setDoubleValue("leg-bearing-true-deg", leg->courseDeg()); + prop->setDoubleValue("leg-distance-nm", leg->distanceNm()); + prop->setDoubleValue("distance-along-route-nm", leg->distanceAlongRoute()); - if (wp->altitudeRestriction() != RESTRICT_NONE) { - double ft = wp->altitudeFt(); + if (leg->altitudeRestriction() != RESTRICT_NONE) { + double ft = leg->altitudeFt(); prop->setDoubleValue("altitude-m", ft * SG_FEET_TO_METER); prop->setDoubleValue("altitude-ft", ft); prop->setIntValue("flight-level", static_cast(ft / 1000) * 10); @@ -1036,10 +761,10 @@ void FGRouteMgr::update_mirror() prop->setDoubleValue("altitude-ft", -9999.9); } - if (wp->speedRestriction() == SPEED_RESTRICT_MACH) { - prop->setDoubleValue("speed-mach", wp->speedMach()); - } else if (wp->speedRestriction() != RESTRICT_NONE) { - prop->setDoubleValue("speed-kts", wp->speedKts()); + if (leg->speedRestriction() == SPEED_RESTRICT_MACH) { + prop->setDoubleValue("speed-mach", leg->speedMach()); + } else if (leg->speedRestriction() != RESTRICT_NONE) { + prop->setDoubleValue("speed-kts", leg->speedKts()); } if (wp->flag(WPT_ARRIVAL)) { @@ -1058,35 +783,13 @@ void FGRouteMgr::update_mirror() } // of waypoint iteration // set number as listener attachment point - mirror->setIntValue("num", _route.size()); + mirror->setIntValue("num", _plan->numLegs()); - NewGUI * gui = (NewGUI *)globals->get_subsystem("gui"); - FGDialog* rmDlg = gui->getDialog("route-manager"); if (rmDlg) { rmDlg->updateValues(); } - - if (_departure) { - departure->setDoubleValue("field-elevation-ft", _departure->getElevation()); - } - if (_destination) { - destination->setDoubleValue("field-elevation-ft", _destination->getElevation()); - } - - totalDistance->setDoubleValue(totalDistanceEnroute); -} - -double FGRouteMgr::cachedLegPathDistanceM(int index) const -{ - SGPropertyNode *prop = mirror->getChild("wp", index, 1); - return prop->getDoubleValue("leg-distance-nm") * SG_NM_TO_METER; -} - -double FGRouteMgr::cachedWaypointPathTotalDistance(int index) const -{ - SGPropertyNode *prop = mirror->getChild("wp", index, 1); - return prop->getDoubleValue("distance-along-route-nm") * SG_NM_TO_METER; + totalDistance->setDoubleValue(_plan->totalDistanceNm()); } // command interface /autopilot/route-manager/input: @@ -1115,13 +818,13 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop) SGPath path(mgr->_pathNode->getStringValue()); mgr->saveRoute(path); } else if (!strcmp(s, "@NEXT")) { - mgr->jumpToIndex(mgr->_currentIndex + 1); + mgr->jumpToIndex(mgr->currentIndex() + 1); } else if (!strcmp(s, "@PREVIOUS")) { - mgr->jumpToIndex(mgr->_currentIndex - 1); + mgr->jumpToIndex(mgr->currentIndex() - 1); } else if (!strncmp(s, "@JUMP", 5)) { mgr->jumpToIndex(atoi(s + 5)); } else if (!strncmp(s, "@DELETE", 7)) - mgr->removeWayptAtIndex(atoi(s + 7)); + mgr->removeLegAtIndex(atoi(s + 7)); else if (!strncmp(s, "@INSERT", 7)) { char *r; int pos = strtol(s + 7, &r, 10); @@ -1130,18 +833,16 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop) while (isspace(*r)) r++; if (*r) - mgr->insertWayptAtIndex(mgr->waypointFromString(r), pos); + mgr->flightPlan()->insertWayptAtIndex(mgr->waypointFromString(r), pos); } else if (!strncmp(s, "@ROUTE", 6)) { char* r; int endIndex = strtol(s + 6, &r, 10); RouteType rt = (RouteType) mgr->_routingType->getIntValue(); mgr->routeToIndex(endIndex, rt); - } else if (!strcmp(s, "@AUTOROUTE")) { - mgr->autoRoute(); } else if (!strcmp(s, "@POSINIT")) { mgr->initAtPosition(); } else - mgr->insertWayptAtIndex(mgr->waypointFromString(s), -1); + mgr->flightPlan()->insertWayptAtIndex(mgr->waypointFromString(s), -1); } void FGRouteMgr::initAtPosition() @@ -1158,63 +859,56 @@ void FGRouteMgr::initAtPosition() if (airborne->getBoolValue()) { SG_LOG(SG_AUTOPILOT, SG_INFO, "initAtPosition: airborne, clearing departure info"); - _departure = NULL; - departure->setStringValue("runway", ""); + _plan->setDeparture((FGAirport*) NULL); return; } // on the ground - SGGeod pos = SGGeod::fromDegFt(lon->getDoubleValue(), - lat->getDoubleValue(), alt->getDoubleValue()); - if (!_departure) { - _departure = FGAirport::findClosest(pos, 20.0); - if (!_departure) { + SGGeod pos = globals->get_aircraft_position(); + if (!_plan->departureAirport()) { + _plan->setDeparture(FGAirport::findClosest(pos, 20.0)); + if (!_plan->departureAirport()) { SG_LOG(SG_AUTOPILOT, SG_INFO, "initAtPosition: couldn't find an airport within 20nm"); - departure->setStringValue("runway", ""); return; } } std::string rwy = departure->getStringValue("runway"); + FGRunway* r = NULL; if (!rwy.empty()) { - // runway already set, fine - return; + r = _plan->departureAirport()->getRunwayByIdent(rwy); + } else { + r = _plan->departureAirport()->findBestRunwayForPos(pos); } - FGRunway* r = _departure->findBestRunwayForPos(pos); if (!r) { return; } - departure->setStringValue("runway", r->ident().c_str()); + _plan->setDeparture(r); SG_LOG(SG_AUTOPILOT, SG_INFO, "initAtPosition: starting at " - << _departure->ident() << " on runway " << r->ident()); + << _plan->departureAirport()->ident() << " on runway " << r->ident()); } bool FGRouteMgr::haveUserWaypoints() const { - return std::find_if(_route.begin(), _route.end(), NotGeneratedWayptPredicate()) != _route.end(); + // FIXME + return false; } bool FGRouteMgr::activate() { + if (!_plan) { + SG_LOG(SG_AUTOPILOT, SG_WARN, "::activate, no flight plan defined"); + return false; + } + if (isRouteActive()) { SG_LOG(SG_AUTOPILOT, SG_WARN, "duplicate route-activation, no-op"); return false; } - _currentIndex = 0; - currentWaypointChanged(); - - /* double routeDistanceNm = _route->total_distance() * SG_METER_TO_NM; - totalDistance->setDoubleValue(routeDistanceNm); - double cruiseSpeedKts = cruise->getDoubleValue("speed", 0.0); - if (cruiseSpeedKts > 1.0) { - // very very crude approximation, doesn't allow for climb / descent - // performance or anything else at all - ete->setDoubleValue(routeDistanceNm / cruiseSpeedKts * (60.0 * 60.0)); - } - */ + _plan->setCurrentIndex(0); active->setBoolValue(true); SG_LOG(SG_AUTOPILOT, SG_INFO, "route-manager, activate route ok"); return true; @@ -1223,7 +917,7 @@ bool FGRouteMgr::activate() void FGRouteMgr::sequence() { - if (!active->getBoolValue()) { + if (!_plan || !active->getBoolValue()) { SG_LOG(SG_AUTOPILOT, SG_ALERT, "trying to sequence waypoints with no active route"); return; } @@ -1232,13 +926,16 @@ void FGRouteMgr::sequence() return; } - _currentIndex++; - currentWaypointChanged(); + _plan->setCurrentIndex(_plan->currentIndex() + 1); } bool FGRouteMgr::checkFinished() { - if (_currentIndex < (int) _route.size()) { + if (!_plan) { + return true; + } + + if (_plan->currentIndex() < _plan->numLegs()) { return false; } @@ -1250,416 +947,226 @@ bool FGRouteMgr::checkFinished() void FGRouteMgr::jumpToIndex(int index) { - if ((index < 0) || (index >= (int) _route.size())) { - SG_LOG(SG_AUTOPILOT, SG_ALERT, "passed invalid index (" << - index << ") to FGRouteMgr::jumpToIndex"); + if (!_plan) { return; } - - if (_currentIndex == index) { - return; // no-op - } -// all the checks out the way, go ahead and update state - _currentIndex = index; - currentWaypointChanged(); - _currentWpt->fireValueChanged(); + _plan->setCurrentIndex(index); } void FGRouteMgr::currentWaypointChanged() { Waypt* cur = currentWaypt(); - Waypt* next = nextWaypt(); + FlightPlan::Leg* next = _plan ? _plan->nextLeg() : NULL; wp0->getChild("id")->setStringValue(cur ? cur->ident() : ""); - wp1->getChild("id")->setStringValue(next ? next->ident() : ""); + wp1->getChild("id")->setStringValue(next ? next->waypoint()->ident() : ""); _currentWpt->fireValueChanged(); - SG_LOG(SG_AUTOPILOT, SG_INFO, "route manager, current-wp is now " << _currentIndex); -} - -int FGRouteMgr::findWayptIndex(const SGGeod& aPos) const -{ - for (int i=0; imatches(aPos)) { - return i; - } - } - - return -1; -} - -Waypt* FGRouteMgr::currentWaypt() const -{ - if ((_currentIndex < 0) || (_currentIndex >= numWaypts())) - return NULL; - return wayptAtIndex(_currentIndex); -} - -Waypt* FGRouteMgr::previousWaypt() const -{ - if (_currentIndex == 0) { - return NULL; - } - - return wayptAtIndex(_currentIndex - 1); -} - -Waypt* FGRouteMgr::nextWaypt() const -{ - if ((_currentIndex < 0) || ((_currentIndex + 1) >= numWaypts())) { - return NULL; - } - - return wayptAtIndex(_currentIndex + 1); -} - -Waypt* FGRouteMgr::wayptAtIndex(int index) const -{ - if ((index < 0) || (index >= numWaypts())) { - throw sg_range_exception("waypt index out of range", "FGRouteMgr::wayptAtIndex"); - } - - return _route[index]; -} - -SGPropertyNode_ptr FGRouteMgr::wayptNodeAtIndex(int index) const -{ - if ((index < 0) || (index >= numWaypts())) { - throw sg_range_exception("waypt index out of range", "FGRouteMgr::wayptAtIndex"); - } - - return mirror->getChild("wp", index); -} - -bool FGRouteMgr::saveRoute(const SGPath& path) -{ - SG_LOG(SG_IO, SG_INFO, "Saving route to " << path.str()); - try { - SGPropertyNode_ptr d(new SGPropertyNode); - SGPath path(_pathNode->getStringValue()); - d->setIntValue("version", 2); - - if (_departure) { - d->setStringValue("departure/airport", _departure->ident()); - d->setStringValue("departure/sid", departure->getStringValue("sid")); - d->setStringValue("departure/runway", departure->getStringValue("runway")); - } - - if (_destination) { - d->setStringValue("destination/airport", _destination->ident()); - d->setStringValue("destination/star", destination->getStringValue("star")); - d->setStringValue("destination/transition", destination->getStringValue("transition")); - d->setStringValue("destination/runway", destination->getStringValue("runway")); - } - - // route nodes - SGPropertyNode* routeNode = d->getChild("route", 0, true); - for (unsigned int i=0; i<_route.size(); ++i) { - Waypt* wpt = _route[i]; - wpt->saveAsNode(routeNode->getChild("wp", i, true)); - } // of waypoint iteration - writeProperties(path.str(), d, true /* write-all */); - return true; - } catch (sg_exception& e) { - SG_LOG(SG_IO, SG_ALERT, "Failed to save flight-plan '" << path.str() << "'. " << e.getMessage()); - return false; - } -} - -bool FGRouteMgr::loadRoute(const SGPath& path) -{ - if (!path.exists()) - { - SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << path.str() - << "'. The file does not exist."); - return false; - } - - // deactivate route first - active->setBoolValue(false); - - SGPropertyNode_ptr routeData(new SGPropertyNode); - - SG_LOG(SG_IO, SG_INFO, "going to read flight-plan from:" << path.str()); - - bool Status = false; - try { - readProperties(path.str(), routeData); - } catch (sg_exception& ) { - // if XML parsing fails, the file might be simple textual list of waypoints - Status = loadPlainTextRoute(path); - routeData = 0; - } - - if (routeData.valid()) - { - try { - int version = routeData->getIntValue("version", 1); - if (version == 1) { - loadVersion1XMLRoute(routeData); - } else if (version == 2) { - loadVersion2XMLRoute(routeData); - } else { - throw sg_io_exception("unsupported XML route version"); - } - Status = true; - } catch (sg_exception& e) { - SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin() - << "'. " << e.getMessage()); - Status = false; - } - } - - update_mirror(); - - return Status; -} - -void FGRouteMgr::loadXMLRouteHeader(SGPropertyNode_ptr routeData) -{ - // departure nodes - SGPropertyNode* dep = routeData->getChild("departure"); - if (dep) { - string depIdent = dep->getStringValue("airport"); - _departure = (FGAirport*) fgFindAirportID(depIdent); - departure->setStringValue("runway", dep->getStringValue("runway")); - departure->setStringValue("sid", dep->getStringValue("sid")); - departure->setStringValue("transition", dep->getStringValue("transition")); - } - -// destination - SGPropertyNode* dst = routeData->getChild("destination"); - if (dst) { - _destination = (FGAirport*) fgFindAirportID(dst->getStringValue("airport")); - destination->setStringValue("runway", dst->getStringValue("runway")); - destination->setStringValue("star", dst->getStringValue("star")); - destination->setStringValue("transition", dst->getStringValue("transition")); - } - -// alternate - SGPropertyNode* alt = routeData->getChild("alternate"); - if (alt) { - alternate->setStringValue(alt->getStringValue("airport")); - } // of cruise data loading - -// 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")); - } // of cruise data loading - -} - -void FGRouteMgr::loadVersion2XMLRoute(SGPropertyNode_ptr routeData) -{ - loadXMLRouteHeader(routeData); - -// route nodes - WayptVec wpts; - SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); - for (int i=0; inChildren(); ++i) { - SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i); - WayptRef wpt = Waypt::createFromProperties(NULL, wpNode); - wpts.push_back(wpt); - } // of route iteration - - _route = wpts; -} - -void FGRouteMgr::loadVersion1XMLRoute(SGPropertyNode_ptr routeData) -{ - loadXMLRouteHeader(routeData); - -// route nodes - WayptVec wpts; - SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); - for (int i=0; inChildren(); ++i) { - SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i); - WayptRef wpt = parseVersion1XMLWaypt(wpNode); - wpts.push_back(wpt); - } // of route iteration - - _route = wpts; -} - -WayptRef FGRouteMgr::parseVersion1XMLWaypt(SGPropertyNode* aWP) -{ - SGGeod lastPos; - if (!_route.empty()) { - lastPos = _route.back()->position(); - } else if (_departure) { - lastPos = _departure->geod(); - } - - WayptRef w; - string ident(aWP->getStringValue("ident")); - if (aWP->hasChild("longitude-deg")) { - // explicit longitude/latitude - w = new BasicWaypt(SGGeod::fromDeg(aWP->getDoubleValue("longitude-deg"), - aWP->getDoubleValue("latitude-deg")), ident, NULL); - - } else { - string nid = aWP->getStringValue("navid", ident.c_str()); - FGPositionedRef p = FGPositioned::findClosestWithIdent(nid, lastPos); - if (!p) { - throw sg_io_exception("bad route file, unknown navid:" + nid); - } - - SGGeod pos(p->geod()); - if (aWP->hasChild("offset-nm") && aWP->hasChild("offset-radial")) { - double radialDeg = aWP->getDoubleValue("offset-radial"); - // convert magnetic radial to a true radial! - radialDeg += magvar->getDoubleValue(); - double offsetNm = aWP->getDoubleValue("offset-nm"); - double az2; - SGGeodesy::direct(p->geod(), radialDeg, offsetNm * SG_NM_TO_METER, pos, az2); - } - - w = new BasicWaypt(pos, ident, NULL); - } - - double altFt = aWP->getDoubleValue("altitude-ft", -9999.9); - if (altFt > -9990.0) { - w->setAltitude(altFt, RESTRICT_AT); - } - - return w; -} - -bool FGRouteMgr::loadPlainTextRoute(const SGPath& path) -{ - try { - sg_gzifstream in(path.str().c_str()); - if (!in.is_open()) { - throw sg_io_exception("Cannot open file for reading."); - } - - WayptVec wpts; - while (!in.eof()) { - string line; - getline(in, line, '\n'); - // trim CR from end of line, if found - if (line[line.size() - 1] == '\r') { - line.erase(line.size() - 1, 1); - } - - line = simgear::strutils::strip(line); - if (line.empty() || (line[0] == '#')) { - continue; // ignore empty/comment lines - } - - WayptRef w = waypointFromString(line); - if (!w) { - throw sg_io_exception("Failed to create waypoint from line '" + line + "'."); - } - - wpts.push_back(w); - } // of line iteration - - _route = wpts; - return true; - } catch (sg_exception& e) { - SG_LOG(SG_IO, SG_ALERT, "Failed to load route from: '" << path.str() << "'. " << e.getMessage()); - return false; - } + SG_LOG(SG_AUTOPILOT, SG_INFO, "route manager, current-wp is now " << currentIndex()); } const char* FGRouteMgr::getDepartureICAO() const { - if (!_departure) { + if (!_plan || !_plan->departureAirport()) { return ""; } - return _departure->ident().c_str(); + return _plan->departureAirport()->ident().c_str(); } const char* FGRouteMgr::getDepartureName() const { - if (!_departure) { + if (!_plan || !_plan->departureAirport()) { return ""; } - return _departure->name().c_str(); + return _plan->departureAirport()->name().c_str(); +} + +const char* FGRouteMgr::getDepartureRunway() const +{ + if (_plan && _plan->departureRunway()) { + return _plan->departureRunway()->ident().c_str(); + } + + return ""; +} + +void FGRouteMgr::setDepartureRunway(const char* aIdent) +{ + FGAirport* apt = _plan->departureAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setDeparture(apt); + } else if (apt->hasRunwayWithIdent(aIdent)) { + _plan->setDeparture(apt->getRunwayByIdent(aIdent)); + } } void FGRouteMgr::setDepartureICAO(const char* aIdent) { if ((aIdent == NULL) || (strlen(aIdent) < 4)) { - _departure = NULL; + _plan->setDeparture((FGAirport*) NULL); } else { - _departure = FGAirport::findByIdent(aIdent); + _plan->setDeparture(FGAirport::findByIdent(aIdent)); + } +} + +const char* FGRouteMgr::getSID() const +{ + if (_plan && _plan->sid()) { + return _plan->sid()->ident().c_str(); } - departureChanged(); + return ""; +} + +void FGRouteMgr::setSID(const char* aIdent) +{ + FGAirport* apt = _plan->departureAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setSID((SID*) NULL); + return; + } + + string ident(aIdent); + size_t hyphenPos = ident.find('-'); + if (hyphenPos != string::npos) { + string sidIdent = ident.substr(0, hyphenPos); + string transIdent = ident.substr(hyphenPos + 1); + + SID* sid = apt->findSIDWithIdent(sidIdent); + Transition* trans = sid ? sid->findTransitionByName(transIdent) : NULL; + _plan->setSID(trans); + } else { + _plan->setSID(apt->findSIDWithIdent(aIdent)); + } } const char* FGRouteMgr::getDestinationICAO() const { - if (!_destination) { + if (!_plan || !_plan->destinationAirport()) { return ""; } - return _destination->ident().c_str(); + return _plan->destinationAirport()->ident().c_str(); } const char* FGRouteMgr::getDestinationName() const { - if (!_destination) { + if (!_plan || !_plan->destinationAirport()) { return ""; } - return _destination->name().c_str(); + return _plan->destinationAirport()->name().c_str(); } void FGRouteMgr::setDestinationICAO(const char* aIdent) { if ((aIdent == NULL) || (strlen(aIdent) < 4)) { - _destination = NULL; + _plan->setDestination((FGAirport*) NULL); } else { - _destination = FGAirport::findByIdent(aIdent); + _plan->setDestination(FGAirport::findByIdent(aIdent)); + } +} + +const char* FGRouteMgr::getDestinationRunway() const +{ + if (_plan && _plan->destinationRunway()) { + return _plan->destinationRunway()->ident().c_str(); } - arrivalChanged(); + return ""; } -FGAirportRef FGRouteMgr::departureAirport() const +void FGRouteMgr::setDestinationRunway(const char* aIdent) { - return _departure; + FGAirport* apt = _plan->destinationAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setDestination(apt); + } else if (apt->hasRunwayWithIdent(aIdent)) { + _plan->setDestination(apt->getRunwayByIdent(aIdent)); + } } -FGAirportRef FGRouteMgr::destinationAirport() const +const char* FGRouteMgr::getApproach() const { - return _destination; + if (_plan && _plan->approach()) { + return _plan->approach()->ident().c_str(); + } + + return ""; } -FGRunway* FGRouteMgr::departureRunway() const +void FGRouteMgr::setApproach(const char* aIdent) { - if (!_departure) { - return NULL; - } - - string runwayId(departure->getStringValue("runway")); - if (!_departure->hasRunwayWithIdent(runwayId)) { - return NULL; - } - - return _departure->getRunwayByIdent(runwayId); + FGAirport* apt = _plan->destinationAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setApproach(NULL); + } else { + _plan->setApproach(apt->findApproachWithIdent(aIdent)); + } } -FGRunway* FGRouteMgr::destinationRunway() const +const char* FGRouteMgr::getSTAR() const { - if (!_destination) { - return NULL; - } - - string runwayId(destination->getStringValue("runway")); - if (!_destination->hasRunwayWithIdent(runwayId)) { - return NULL; - } - - return _destination->getRunwayByIdent(runwayId); + if (_plan && _plan->star()) { + return _plan->star()->ident().c_str(); + } + + return ""; } +void FGRouteMgr::setSTAR(const char* aIdent) +{ + FGAirport* apt = _plan->destinationAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setSTAR((STAR*) NULL); + return; + } + + string ident(aIdent); + size_t hyphenPos = ident.find('-'); + if (hyphenPos != string::npos) { + string starIdent = ident.substr(0, hyphenPos); + string transIdent = ident.substr(hyphenPos + 1); + + STAR* star = apt->findSTARWithIdent(starIdent); + Transition* trans = star ? star->findTransitionByName(transIdent) : NULL; + _plan->setSTAR(trans); + } else { + _plan->setSTAR(apt->findSTARWithIdent(aIdent)); + } +} + +WayptRef FGRouteMgr::waypointFromString(const std::string& target) +{ + return _plan->waypointFromString(target); +} + +double FGRouteMgr::getDepartureFieldElevation() const +{ + if (!_plan || !_plan->departureAirport()) { + return 0.0; + } + + return _plan->departureAirport()->elevation(); +} + +double FGRouteMgr::getDestinationFieldElevation() const +{ + if (!_plan || !_plan->destinationAirport()) { + return 0.0; + } + + return _plan->destinationAirport()->elevation(); +} + +SGPropertyNode_ptr FGRouteMgr::wayptNodeAtIndex(int index) const +{ + if ((index < 0) || (index >= numWaypts())) { + throw sg_range_exception("waypt index out of range", "FGRouteMgr::wayptAtIndex"); + } + + return mirror->getChild("wp", index); +} diff --git a/src/Autopilot/route_mgr.hxx b/src/Autopilot/route_mgr.hxx index b0f14ba79..840ec3160 100644 --- a/src/Autopilot/route_mgr.hxx +++ b/src/Autopilot/route_mgr.hxx @@ -44,7 +44,8 @@ typedef SGSharedPtr FGAirportRef; * */ -class FGRouteMgr : public SGSubsystem +class FGRouteMgr : public SGSubsystem, + public flightgear::FlightPlan::Delegate { public: FGRouteMgr(); @@ -55,11 +56,6 @@ public: void bind (); void unbind (); void update (double dt); - - void insertWayptAtIndex(flightgear::Waypt* aWpt, int aIndex); - flightgear::WayptRef removeWayptAtIndex(int index); - - void clearRoute(); typedef enum { ROUTE_HIGH_AIRWAYS, ///< high-level airways routing @@ -74,34 +70,31 @@ public: * used as the final waypoint. */ bool routeToIndex(int index, RouteType aRouteType); - - void autoRoute(); bool isRouteActive() const; - int currentIndex() const - { return _currentIndex; } - + int currentIndex() const; + + void setFlightPlan(flightgear::FlightPlan* plan); + flightgear::FlightPlan* flightPlan() const; + + void clearRoute(); + flightgear::Waypt* currentWaypt() const; - flightgear::Waypt* nextWaypt() const; - flightgear::Waypt* previousWaypt() const; - const flightgear::WayptVec& waypts() const - { return _route; } + int numLegs() const; +// deprecated int numWaypts() const - { return _route.size(); } - + { return numLegs(); } + +// deprecated flightgear::Waypt* wayptAtIndex(int index) const; - + SGPropertyNode_ptr wayptNodeAtIndex(int index) const; - - /** - * Find a waypoint in the route, by position, and return its index, or - * -1 if no matching waypoint was found in the route. - */ - int findWayptIndex(const SGGeod& aPos) const; - + + void removeLegAtIndex(int aIndex); + /** * Activate a built route. This checks for various mandatory pieces of * data, such as departure and destination airports, and creates waypoints @@ -125,38 +118,20 @@ public: bool saveRoute(const SGPath& p); bool loadRoute(const SGPath& p); + flightgear::WayptRef waypointFromString(const std::string& target); + /** * Helper command to setup current airport/runway if necessary */ void initAtPosition(); - - /** - * Create a WayPoint from a string in the following format: - * - simple identifier - * - decimal-lon,decimal-lat - * - airport-id/runway-id - * - navaid/radial-deg/offset-nm - */ - flightgear::WayptRef waypointFromString(const std::string& target); - - FGAirportRef departureAirport() const; - FGAirportRef destinationAirport() const; - - FGRunway* departureRunway() const; - FGRunway* destinationRunway() const; + private: - flightgear::WayptVec _route; - int _currentIndex; + flightgear::FlightPlan* _plan; time_t _takeoffTime; time_t _touchdownTime; - FGAirportRef _departure; - FGAirportRef _destination; - + // automatic inputs - SGPropertyNode_ptr lon; - SGPropertyNode_ptr lat; - SGPropertyNode_ptr alt; SGPropertyNode_ptr magvar; // automatic outputs @@ -215,11 +190,11 @@ private: InputListener *listener; SGPropertyNode_ptr mirror; - - void departureChanged(); + + virtual void departureChanged(); void buildDeparture(flightgear::WayptRef enroute, flightgear::WayptVec& wps); - void arrivalChanged(); + virtual void arrivalChanged(); void buildArrival(flightgear::WayptRef enroute, flightgear::WayptVec& wps); /** @@ -227,16 +202,11 @@ private: * modified (waypoints added, inserted, removed). Notably, this fires the * 'edited' signal. */ - void waypointsChanged(); + virtual void waypointsChanged(); void update_mirror(); - void currentWaypointChanged(); - - /** - * Parse a route/wp node (from a saved, property-lsit formatted route) - */ - void parseRouteWaypoint(SGPropertyNode* aWP); + virtual void currentWaypointChanged(); /** * Check if we've reached the final waypoint. @@ -244,14 +214,6 @@ private: */ bool checkFinished(); - - bool loadPlainTextRoute(const SGPath& path); - - void loadVersion1XMLRoute(SGPropertyNode_ptr routeData); - void loadVersion2XMLRoute(SGPropertyNode_ptr routeData); - void loadXMLRouteHeader(SGPropertyNode_ptr routeData); - flightgear::WayptRef parseVersion1XMLWaypt(SGPropertyNode* aWP); - /** * Predicate for helping the UI - test if at least one waypoint was * entered by the user (as opposed to being generated by the route-manager) @@ -263,12 +225,27 @@ private: const char* getDepartureName() const; void setDepartureICAO(const char* aIdent); + const char* getDepartureRunway() const; + void setDepartureRunway(const char* aIdent); + + const char* getSID() const; + void setSID(const char* aIdent); + const char* getDestinationICAO() const; const char* getDestinationName() const; void setDestinationICAO(const char* aIdent); - PropertyWatcher* _departureWatcher; - PropertyWatcher* _arrivalWatcher; + const char* getDestinationRunway() const; + void setDestinationRunway(const char* aIdent); + + const char* getApproach() const; + void setApproach(const char* aIdent); + + const char* getSTAR() const; + void setSTAR(const char* aIdent); + + double getDepartureFieldElevation() const; + double getDestinationFieldElevation() const; }; diff --git a/src/GUI/MapWidget.cxx b/src/GUI/MapWidget.cxx index a0c6d2b4c..99e536a4f 100644 --- a/src/GUI/MapWidget.cxx +++ b/src/GUI/MapWidget.cxx @@ -682,7 +682,7 @@ void MapWidget::paintRoute() return; } - RoutePath path(_route->waypts()); + RoutePath path(_route->flightPlan()); // first pass, draw the actual lines glLineWidth(2.0); diff --git a/src/GUI/WaypointList.cxx b/src/GUI/WaypointList.cxx index 235ef960f..57b741169 100644 --- a/src/GUI/WaypointList.cxx +++ b/src/GUI/WaypointList.cxx @@ -37,20 +37,19 @@ enum { static const double BLINK_TIME = 0.3; static const int DRAG_START_DISTANCE_PX = 5; -class RouteManagerWaypointModel : +class FlightPlanWaypointModel : public WaypointList::Model, public SGPropertyChangeListener { public: - RouteManagerWaypointModel() - { - _rm = static_cast(globals->get_subsystem("route-manager")); - + FlightPlanWaypointModel(flightgear::FlightPlan* fp) : + _fp(fp) + { SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true); routeEdited->addChangeListener(this); } - virtual ~RouteManagerWaypointModel() + ~FlightPlanWaypointModel() { SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true); routeEdited->removeChangeListener(this); @@ -59,12 +58,12 @@ public: // implement WaypointList::Model virtual unsigned int numWaypoints() const { - return _rm->numWaypts(); + return _fp->numLegs(); } virtual int currentWaypoint() const { - return _rm->currentIndex(); + return _fp->currentIndex(); } virtual flightgear::Waypt* waypointAt(unsigned int index) const @@ -73,12 +72,12 @@ public: return NULL; } - return _rm->wayptAtIndex(index); + return _fp->legAtIndex(index)->waypoint(); } virtual void deleteAt(unsigned int index) { - _rm->removeWayptAtIndex(index); + _fp->deleteIndex(index); } virtual void moveWaypointToIndex(unsigned int srcIndex, unsigned int destIndex) @@ -89,13 +88,15 @@ public: } unsigned int currentWpIndex = currentWaypoint(); - WayptRef w(_rm->removeWayptAtIndex(srcIndex)); + WayptRef w(waypointAt(currentWpIndex)); + _fp->deleteIndex(currentWpIndex); + SG_LOG(SG_GENERAL, SG_INFO, "wpt:" << w->ident()); - _rm->insertWayptAtIndex(w, destIndex); + _fp->insertWayptAtIndex(w, destIndex); if (srcIndex == currentWpIndex) { // current waypoint was moved - _rm->jumpToIndex(destIndex); + _fp->setCurrentIndex(destIndex); } } @@ -112,7 +113,7 @@ public: } } private: - FGRouteMgr* _rm; + flightgear::FlightPlan* _fp; SGCallback* _cb; }; @@ -152,7 +153,9 @@ WaypointList::WaypointList(int x, int y, int width, int height) : { // pretend to be a list, so fgPopup doesn't mess with our mouse events type |= PUCLASS_LIST; - setModel(new RouteManagerWaypointModel()); + flightgear::FlightPlan* fp = + static_cast(globals->get_subsystem("route-manager"))->flightPlan(); + setModel(new FlightPlanWaypointModel(fp)); setSize(width, height); setValue(-1); diff --git a/src/Instrumentation/NavDisplay.cxx b/src/Instrumentation/NavDisplay.cxx index 015f8ff59..2528a7ef4 100644 --- a/src/Instrumentation/NavDisplay.cxx +++ b/src/Instrumentation/NavDisplay.cxx @@ -159,7 +159,6 @@ public: virtual void valueChanged (SGPropertyNode * prop) { _nd->invalidatePositionedCache(); - SG_LOG(SG_INSTR, SG_INFO, "invalidating NavDisplay cache"); } private: NavDisplay* _nd; @@ -174,7 +173,6 @@ public: virtual void valueChanged (SGPropertyNode * prop) { - SG_LOG(SG_INSTR, SG_INFO, "forcing NavDisplay update"); _nd->forceUpdate(); } private: @@ -683,9 +681,6 @@ NavDisplay::update (double delta_time_sec) SGVec3d cartNow(SGVec3d::fromGeod(_pos)); double movedNm = dist(_cachedPos, cartNow) * SG_METER_TO_NM; _cachedItemsValid = (movedNm < 1.0); - if (!_cachedItemsValid) { - SG_LOG(SG_INSTR, SG_INFO, "invalidating NavDisplay cache due to moving: " << movedNm); - } } _vertices->clear(); @@ -951,7 +946,6 @@ public: void NavDisplay::findItems() { if (!_cachedItemsValid) { - SG_LOG(SG_INSTR, SG_INFO, "re-validating NavDisplay cache"); Filter filt; filt.minRunwayLengthFt = 2000; _itemsInRange = FGPositioned::findWithinRange(_pos, _rangeNm, &filt); @@ -967,29 +961,31 @@ void NavDisplay::findItems() void NavDisplay::processRoute() { _routeSources.clear(); - RoutePath path(_route->waypts()); + flightgear::FlightPlan* fp = _route->flightPlan(); + RoutePath path(fp); int current = _route->currentIndex(); - for (int w=0; w<_route->numWaypts(); ++w) { - flightgear::WayptRef wpt(_route->wayptAtIndex(w)); + for (int l=0; lnumLegs(); ++l) { + flightgear::FlightPlan::Leg* leg = fp->legAtIndex(l); + flightgear::WayptRef wpt(leg->waypoint()); _routeSources.insert(wpt->source()); string_set state; state.insert("on-active-route"); - if (w < current) { + if (l < current) { state.insert("passed"); } - if (w == current) { + if (l == current) { state.insert("current-wp"); } - if (w > current) { + if (l > current) { state.insert("future"); } - if (w == (current + 1)) { + if (l == (current + 1)) { state.insert("next-wp"); } @@ -999,8 +995,12 @@ void NavDisplay::processRoute() return; // no rules matched, we can skip this item } - SGGeod g = path.positionForIndex(w); - SGPropertyNode* vars = _route->wayptNodeAtIndex(w); + SGGeod g = path.positionForIndex(l); + SGPropertyNode* vars = _route->wayptNodeAtIndex(l); + if (!vars) { + continue; // shouldn't happen, but let's guard against it + } + double heading; computeWayptPropsAndHeading(wpt, g, vars, heading); @@ -1009,7 +1009,7 @@ void NavDisplay::processRoute() addSymbolInstance(projected, heading, r->getDefinition(), vars); if (r->getDefinition()->drawRouteLeg) { - SGGeodVec gv(path.pathForIndex(w)); + SGGeodVec gv(path.pathForIndex(l)); if (!gv.empty()) { osg::Vec2 pr = projectGeod(gv[0]); for (unsigned int i=1; iflightPlan(); switch (pos->type()) { case FGPositioned::VOR: case FGPositioned::LOC: @@ -1209,21 +1210,21 @@ void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states) // mark alternates! // once the FMS system has some way to tell us about them, of course - if (pos == _route->departureAirport()) { + if (pos == fp->departureAirport()) { states.insert("departure"); } - if (pos == _route->destinationAirport()) { + if (pos == fp->destinationAirport()) { states.insert("destination"); } break; case FGPositioned::RUNWAY: - if (pos == _route->departureRunway()) { + if (pos == fp->departureRunway()) { states.insert("departure"); } - if (pos == _route->destinationRunway()) { + if (pos == fp->destinationRunway()) { states.insert("destination"); } break; diff --git a/src/Instrumentation/gps.cxx b/src/Instrumentation/gps.cxx index d9acf4f9f..4d7b79b4f 100644 --- a/src/Instrumentation/gps.cxx +++ b/src/Instrumentation/gps.cxx @@ -724,7 +724,7 @@ void GPS::routeManagerSequenced() SG_LOG(SG_INSTR, SG_INFO, "GPS waypoint index is now " << index); if (index > 0) { - _prevWaypt = _routeMgr->previousWaypt(); + _prevWaypt = _routeMgr->wayptAtIndex(index - 1); if (_prevWaypt->flag(WPT_DYNAMIC)) { _wp0_position = _indicated_pos; } else { @@ -829,7 +829,7 @@ void GPS::updateOverflight() // check for wp1 being on active route - resume leg mode if (_routeMgr->isRouteActive()) { - int index = _routeMgr->findWayptIndex(_currentWaypt->position()); + int index = _routeMgr->flightPlan()->findWayptIndex(_currentWaypt->position()); if (index >= 0) { SG_LOG(SG_INSTR, SG_INFO, "GPS DTO, resuming LEG mode at wp:" << index); _mode = "leg"; @@ -881,7 +881,7 @@ void GPS::computeTurnData() return; } - WayptRef next = _routeMgr->nextWaypt(); + WayptRef next = _routeMgr->wayptAtIndex(_routeMgr->currentIndex() + 1); if (!next || next->flag(WPT_DYNAMIC)) { _anticipateTurn = false; return; @@ -1684,7 +1684,7 @@ void GPS::insertWaypointAtIndex(int aIndex) string ident = _scratchNode->getStringValue("ident"); WayptRef wpt = new BasicWaypt(_scratchPos, ident, NULL); - _routeMgr->insertWayptAtIndex(wpt, aIndex); + _routeMgr->flightPlan()->insertWayptAtIndex(wpt, aIndex); } void GPS::removeWaypointAtIndex(int aIndex) @@ -1693,7 +1693,7 @@ void GPS::removeWaypointAtIndex(int aIndex) throw sg_range_exception("GPS::removeWaypointAtIndex: index out of bounds"); } - _routeMgr->removeWayptAtIndex(aIndex); + _routeMgr->removeLegAtIndex(aIndex); } void GPS::tieSGGeod(SGPropertyNode* aNode, SGGeod& aRef, diff --git a/src/Instrumentation/rnav_waypt_controller.cxx b/src/Instrumentation/rnav_waypt_controller.cxx index e0f30e92a..f893b9413 100644 --- a/src/Instrumentation/rnav_waypt_controller.cxx +++ b/src/Instrumentation/rnav_waypt_controller.cxx @@ -317,7 +317,9 @@ public: double curAlt = _rnav->position().getElevationFt(); switch (_waypt->altitudeRestriction()) { - case RESTRICT_AT: { + case RESTRICT_AT: + case RESTRICT_COMPUTED: + { double d = curAlt - _waypt->altitudeFt(); if (fabs(d) < 50.0) { SG_LOG(SG_INSTR, SG_INFO, "ConstHdgToAltCtl, reached target altitude " << _waypt->altitudeFt()); @@ -339,11 +341,7 @@ public: } break; - case RESTRICT_NONE: - assert(false); - break; - case SPEED_RESTRICT_MACH: - assert(false); + default: break; } } diff --git a/src/Navaids/CMakeLists.txt b/src/Navaids/CMakeLists.txt index bd5cce9b7..cd806a65e 100644 --- a/src/Navaids/CMakeLists.txt +++ b/src/Navaids/CMakeLists.txt @@ -12,7 +12,8 @@ set(SOURCES route.cxx routePath.cxx waypoint.cxx -) + LevelDXML.cxx + ) set(HEADERS airways.hxx @@ -26,6 +27,7 @@ set(HEADERS route.hxx routePath.hxx waypoint.hxx -) + LevelDXML.hxx + ) flightgear_component(Navaids "${SOURCES}" "${HEADERS}") \ No newline at end of file diff --git a/src/Navaids/LevelDXML.cxx b/src/Navaids/LevelDXML.cxx new file mode 100644 index 000000000..49ab15d3f --- /dev/null +++ b/src/Navaids/LevelDXML.cxx @@ -0,0 +1,337 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "LevelDXML.hxx" + +#include + +#include +#include + +#include +#include + +using std::string; +using std::vector; + +namespace flightgear +{ + +NavdataVisitor::NavdataVisitor(FGAirport* aApt, const SGPath& aPath): + _airport(aApt), + _path(aPath), + _sid(NULL), + _star(NULL), + _approach(NULL), + _transition(NULL), + _procedure(NULL) +{ +} + +void NavdataVisitor::startXML() +{ +} + +void NavdataVisitor::endXML() +{ +} + +void NavdataVisitor::startElement(const char* name, const XMLAttributes &atts) +{ + _text.clear(); + string tag(name); + if (tag == "Airport") { + string icao(atts.getValue("ICAOcode")); + if (_airport->ident() != icao) { + throw sg_format_exception("Airport and ICAO mismatch", icao, _path.str()); + } + } else if (tag == "Sid") { + string ident(atts.getValue("Name")); + _sid = new SID(ident, _airport); + _procedure = _sid; + _waypoints.clear(); + processRunways(_sid, atts); + } else if (tag == "Star") { + string ident(atts.getValue("Name")); + _star = new STAR(ident, _airport); + _procedure = _star; + _waypoints.clear(); + processRunways(_star, atts); + } else if ((tag == "Sid_Waypoint") || + (tag == "App_Waypoint") || + (tag == "Star_Waypoint") || + (tag == "AppTr_Waypoint") || + (tag == "SidTr_Waypoint") || + (tag == "RwyTr_Waypoint")) + { + // reset waypoint data + _speed = 0.0; + _altRestrict = RESTRICT_NONE; + _altitude = 0.0; + } else if (tag == "Approach") { + _ident = atts.getValue("Name"); + _waypoints.clear(); + ProcedureType ty = PROCEDURE_APPROACH_RNAV; + _approach = new Approach(_ident, ty); + _procedure = _approach; + } else if ((tag == "Sid_Transition") || + (tag == "App_Transition") || + (tag == "Star_Transition")) { + _transIdent = atts.getValue("Name"); + _transition = new Transition(_transIdent, PROCEDURE_TRANSITION, _procedure); + _transWaypts.clear(); + } else if (tag == "RunwayTransition") { + _transIdent = atts.getValue("Runway"); + _transition = new Transition(_transIdent, PROCEDURE_RUNWAY_TRANSITION, _procedure); + _transWaypts.clear(); + } else { + + } +} + +void NavdataVisitor::processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts) +{ + string v("All"); + if (atts.hasAttribute("Runways")) { + v = atts.getValue("Runways"); + } + + if (v == "All") { + for (unsigned int r=0; r<_airport->numRunways(); ++r) { + aProc->addRunway(_airport->getRunwayByIndex(r)); + } + return; + } + + vector rwys; + boost::split(rwys, v, boost::is_any_of(" ,")); + for (unsigned int r=0; rgetRunwayByIdent(rwys[r]); + aProc->addRunway(rwy); + } +} + +void NavdataVisitor::endElement(const char* name) +{ + string tag(name); + if ((tag == "Sid_Waypoint") || + (tag == "App_Waypoint") || + (tag == "Star_Waypoint")) + { + _waypoints.push_back(buildWaypoint(_procedure)); + } else if ((tag == "AppTr_Waypoint") || + (tag == "SidTr_Waypoint") || + (tag == "RwyTr_Waypoint") || + (tag == "StarTr_Waypoint")) + { + _transWaypts.push_back(buildWaypoint(_transition)); + } else if (tag == "Sid_Transition") { + assert(_sid); + // SID waypoints are stored backwards, to share code with STARs + std::reverse(_transWaypts.begin(), _transWaypts.end()); + _transition->setPrimary(_transWaypts); + _sid->addTransition(_transition); + } else if (tag == "Star_Transition") { + assert(_star); + _transition->setPrimary(_transWaypts); + _star->addTransition(_transition); + } else if (tag == "App_Transition") { + assert(_approach); + _transition->setPrimary(_transWaypts); + _approach->addTransition(_transition); + } else if (tag == "RunwayTransition") { + ArrivalDeparture* ad; + if (_sid) { + // SID waypoints are stored backwards, to share code with STARs + std::reverse(_transWaypts.begin(), _transWaypts.end()); + ad = _sid; + } else { + ad = _star; + } + + _transition->setPrimary(_transWaypts); + FGRunwayRef rwy = _airport->getRunwayByIdent(_transIdent); + ad->addRunwayTransition(rwy, _transition); + } else if (tag == "Approach") { + finishApproach(); + } else if (tag == "Sid") { + finishSid(); + } else if (tag == "Star") { + finishStar(); + } else if (tag == "Longitude") { + _longitude = atof(_text.c_str()); + } else if (tag == "Latitude") { + _latitude = atof(_text.c_str()); + } else if (tag == "Name") { + _wayptName = _text; + } else if (tag == "Type") { + _wayptType = _text; + } else if (tag == "Speed") { + _speed = atoi(_text.c_str()); + } else if (tag == "Altitude") { + _altitude = atof(_text.c_str()); + } else if (tag == "AltitudeRestriction") { + if (_text == "at") { + _altRestrict = RESTRICT_AT; + } else if (_text == "above") { + _altRestrict = RESTRICT_ABOVE; + } else if (_text == "below") { + _altRestrict = RESTRICT_BELOW; + } else { + throw sg_format_exception("Unrecognized altitude restriction", _text); + } + } else if (tag == "Hld_Rad_or_Inbd") { + if (_text == "Inbd") { + _holdRadial = -1.0; + } + } else if (tag == "Hld_Time_or_Dist") { + _holdDistance = (_text == "Dist"); + } else if (tag == "Hld_Rad_value") { + _holdRadial = atof(_text.c_str()); + } else if (tag == "Hld_Turn") { + _holdRighthanded = (_text == "Right"); + } else if (tag == "Hld_td_value") { + _holdTD = atof(_text.c_str()); + } else if (tag == "Hdg_Crs_value") { + _course = atof(_text.c_str()); + } else if (tag == "DMEtoIntercept") { + _dmeDistance = atof(_text.c_str()); + } else if (tag == "RadialtoIntercept") { + _radial = atof(_text.c_str()); + } else { + + } +} + +Waypt* NavdataVisitor::buildWaypoint(RouteBase* owner) +{ + Waypt* wp = NULL; + if (_wayptType == "Normal") { + // new LatLonWaypoint + SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); + wp = new BasicWaypt(pos, _wayptName, owner); + } else if (_wayptType == "Runway") { + string ident = _wayptName.substr(2); + FGRunwayRef rwy = _airport->getRunwayByIdent(ident); + wp = new RunwayWaypt(rwy, owner); + } else if (_wayptType == "Hold") { + SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); + Hold* h = new Hold(pos, _wayptName, owner); + wp = h; + if (_holdRighthanded) { + h->setRightHanded(); + } else { + h->setLeftHanded(); + } + + if (_holdDistance) { + h->setHoldDistance(_holdTD); + } else { + h->setHoldTime(_holdTD * 60.0); + } + + if (_holdRadial >= 0.0) { + h->setHoldRadial(_holdRadial); + } + } else if (_wayptType == "Vectors") { + wp = new ATCVectors(owner, _airport); + } else if ((_wayptType == "Intc") || (_wayptType == "VorRadialIntc")) { + SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); + wp = new RadialIntercept(owner, _wayptName, pos, _course, _radial); + } else if (_wayptType == "DmeIntc") { + SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); + wp = new DMEIntercept(owner, _wayptName, pos, _course, _dmeDistance); + } else if (_wayptType == "ConstHdgtoAlt") { + wp = new HeadingToAltitude(owner, _wayptName, _course); + } else if (_wayptType == "PBD") { + SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)), pos2; + double az2; + SGGeodesy::direct(pos, _course, _dmeDistance, pos2, az2); + wp = new BasicWaypt(pos2, _wayptName, owner); + } else { + SG_LOG(SG_GENERAL, SG_ALERT, "implement waypoint type:" << _wayptType); + throw sg_format_exception("Unrecognized waypt type", _wayptType); + } + + assert(wp); + if ((_altitude > 0.0) && (_altRestrict != RESTRICT_NONE)) { + wp->setAltitude(_altitude,_altRestrict); + } + + if (_speed > 0.0) { + wp->setSpeed(_speed, RESTRICT_AT); // or _BELOW? + } + + return wp; +} + +void NavdataVisitor::finishApproach() +{ + WayptVec::iterator it; + FGRunwayRef rwy; + +// find the runway node + for (it = _waypoints.begin(); it != _waypoints.end(); ++it) { + FGPositionedRef navid = (*it)->source(); + if (!navid) { + continue; + } + + if (navid->type() == FGPositioned::RUNWAY) { + rwy = (FGRunway*) navid.get(); + break; + } + } + + if (!rwy) { + throw sg_format_exception("Malformed approach, no runway waypt", _ident); + } + + WayptVec primary(_waypoints.begin(), it); + // erase all points up to and including the runway, to leave only the + // missed segments + _waypoints.erase(_waypoints.begin(), ++it); + + _approach->setRunway(rwy); + _approach->setPrimaryAndMissed(primary, _waypoints); + _airport->addApproach(_approach); + _approach = NULL; +} + +void NavdataVisitor::finishSid() +{ + // reverse order, because that's how we deal with commonality between + // STARs and SIDs. SID::route undoes this + std::reverse(_waypoints.begin(), _waypoints.end()); + _sid->setCommon(_waypoints); + _airport->addSID(_sid); + _sid = NULL; +} + +void NavdataVisitor::finishStar() +{ + _star->setCommon(_waypoints); + _airport->addSTAR(_star); + _star = NULL; +} + +void NavdataVisitor::data (const char * s, int len) +{ + _text += string(s, len); +} + + +void NavdataVisitor::pi (const char * target, const char * data) { + //cout << "Processing instruction " << target << ' ' << data << endl; +} + +void NavdataVisitor::warning (const char * message, int line, int column) { + SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')'); +} + +void NavdataVisitor::error (const char * message, int line, int column) { + SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')'); +} + +} diff --git a/src/Navaids/LevelDXML.hxx b/src/Navaids/LevelDXML.hxx new file mode 100644 index 000000000..7d61bb8f9 --- /dev/null +++ b/src/Navaids/LevelDXML.hxx @@ -0,0 +1,66 @@ +#ifndef FG_NAV_LEVELDXML_HXX +#define FG_NAV_LEVELDXML_HXX + +class FGAirport; +class SGPath; + +#include +#include +#include + +namespace flightgear +{ + +class NavdataVisitor : public XMLVisitor { +public: + NavdataVisitor(FGAirport* aApt, const SGPath& aPath); + +protected: + virtual void startXML (); + virtual void endXML (); + virtual void startElement (const char * name, const XMLAttributes &atts); + virtual void endElement (const char * name); + virtual void data (const char * s, int len); + virtual void pi (const char * target, const char * data); + virtual void warning (const char * message, int line, int column); + virtual void error (const char * message, int line, int column); + +private: + Waypt* buildWaypoint(RouteBase* owner); + void processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts); + + void finishApproach(); + void finishSid(); + void finishStar(); + + FGAirport* _airport; + SGPath _path; + std::string _text; ///< last element text value + + SID* _sid; + STAR* _star; + Approach* _approach; + Transition* _transition; + Procedure* _procedure; + + WayptVec _waypoints; ///< waypoint list for current approach/sid/star + WayptVec _transWaypts; ///< waypoint list for current transition + + std::string _wayptName; + std::string _wayptType; + std::string _ident; // id of segment under construction + std::string _transIdent; + double _longitude, _latitude, _altitude, _speed; + RouteRestriction _altRestrict; + + double _holdRadial; // inbound hold radial, or -1 if radial is 'inbound' + double _holdTD; ///< hold time (seconds) or distance (nm), based on flag below + bool _holdRighthanded; + bool _holdDistance; // true, TD is distance in nm; false, TD is time in seconds + + double _course, _radial, _dmeDistance; +}; + +} + +#endif \ No newline at end of file diff --git a/src/Navaids/airways.hxx b/src/Navaids/airways.hxx index f29ecbb20..a2ea9d93f 100644 --- a/src/Navaids/airways.hxx +++ b/src/Navaids/airways.hxx @@ -35,7 +35,7 @@ struct SearchContext; class AdjacentWaypoint; class InAirwayFilter; -class Airway : public Route +class Airway : public RouteBase { public: virtual std::string ident() const diff --git a/src/Navaids/procedure.cxx b/src/Navaids/procedure.cxx index 67c78c18e..bff774b4d 100644 --- a/src/Navaids/procedure.cxx +++ b/src/Navaids/procedure.cxx @@ -30,14 +30,22 @@ using std::string; namespace flightgear { + +static void markWaypoints(WayptVec& wps, WayptFlag f) +{ + for (unsigned int i=0; isetFlag(f, true); + } +} Procedure::Procedure(const string& aIdent) : _ident(aIdent) { } -Approach::Approach(const string& aIdent) : - Procedure(aIdent) +Approach::Approach(const string& aIdent, ProcedureType ty) : + Procedure(aIdent), + _type(ty) { } @@ -47,22 +55,32 @@ void Approach::setRunway(FGRunwayRef aRwy) _runway = aRwy; } +FGAirport* Approach::airport() const +{ + return _runway->airport(); +} + +RunwayVec Approach::runways() const +{ + RunwayVec r; + r.push_back(_runway); + return r; +} + void Approach::setPrimaryAndMissed(const WayptVec& aPrimary, const WayptVec& aMissed) { _primary = aPrimary; _primary[0]->setFlag(WPT_IAF, true); _primary[_primary.size()-1]->setFlag(WPT_FAF, true); + markWaypoints(_primary, WPT_APPROACH); _missed = aMissed; if (!_missed.empty()) { // mark the first point as the published missed-approach point _missed[0]->setFlag(WPT_MAP, true); - - // mark all the points as being on the missed approach route - for (unsigned int i=0; i<_missed.size(); ++i) { - _missed[i]->setFlag(WPT_MISS, true); - } + markWaypoints(_missed, WPT_MISS); + markWaypoints(_missed, WPT_APPROACH); } } @@ -70,6 +88,7 @@ void Approach::addTransition(Transition* aTrans) { WayptRef entry = aTrans->enroute(); _transitions[entry] = aTrans; + aTrans->mark(WPT_APPROACH); } bool Approach::route(WayptRef aIAF, WayptVec& aWps) @@ -78,8 +97,9 @@ bool Approach::route(WayptRef aIAF, WayptVec& aWps) bool haveTrans = false; for (it = _transitions.begin(); it != _transitions.end(); ++it) { Transition* t= it->second; - if (t->route(aIAF, aWps)) { - haveTrans = true; + if (t->enroute()->matches(aIAF)) { + t->route(aWps); + haveTrans = true; break; } } // of transitions iteration @@ -90,10 +110,7 @@ bool Approach::route(WayptRef aIAF, WayptVec& aWps) return false; } - aWps.insert(aWps.end(), _primary.begin(), _primary.end()); - aWps.push_back(new RunwayWaypt(_runway, NULL)); - aWps.insert(aWps.end(), _missed.begin(), _missed.end()); - return true; + return routeFromVectors(aWps); } bool Approach::routeFromVectors(WayptVec& aWps) @@ -104,34 +121,64 @@ bool Approach::routeFromVectors(WayptVec& aWps) return true; } +bool Approach::isApproach(ProcedureType ty) +{ + return (ty >= PROCEDURE_APPROACH_ILS) && (ty <= PROCEDURE_APPROACH_RNAV); +} + ////////////////////////////////////////////////////////////////////////////// -ArrivalDeparture::ArrivalDeparture(const string& aIdent) : - Procedure(aIdent) +ArrivalDeparture::ArrivalDeparture(const string& aIdent, FGAirport* apt) : + Procedure(aIdent), + _airport(apt) { } void ArrivalDeparture::addRunway(FGRunwayRef aWay) { + assert(aWay->airport() == _airport); _runways[aWay] = NULL; } -bool ArrivalDeparture::isForRunway(FGRunwayRef aWay) const +bool ArrivalDeparture::isForRunway(const FGRunway* aWay) const { // null runway always passes if (!aWay) { return true; } - return (_runways.count(aWay)); + FGRunwayRef r(const_cast(aWay)); + return (_runways.count(r)); } +RunwayVec ArrivalDeparture::runways() const +{ + RunwayVec r; + RunwayTransitionMap::const_iterator it = _runways.begin(); + for (; it != _runways.end(); ++it) { + r.push_back(it->first); + } + + return r; +} + void ArrivalDeparture::addTransition(Transition* aTrans) { WayptRef entry = aTrans->enroute(); + aTrans->mark(flagType()); _enrouteTransitions[entry] = aTrans; } +string_list ArrivalDeparture::transitionIdents() const +{ + string_list r; + WptTransitionMap::const_iterator eit; + for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) { + r.push_back(eit->second->ident()); + } + return r; +} + void ArrivalDeparture::addRunwayTransition(FGRunwayRef aWay, Transition* aTrans) { assert(aWay->ident() == aTrans->ident()); @@ -139,23 +186,24 @@ void ArrivalDeparture::addRunwayTransition(FGRunwayRef aWay, Transition* aTrans) throw sg_io_exception("adding transition for unspecified runway:" + aWay->ident(), ident()); } + aTrans->mark(flagType()); _runways[aWay] = aTrans; } void ArrivalDeparture::setCommon(const WayptVec& aWps) { _common = aWps; + markWaypoints(_common, flagType()); } -bool ArrivalDeparture::commonRoute(Waypt* aEnroute, WayptVec& aPath, FGRunwayRef aRwy) +bool ArrivalDeparture::commonRoute(Transition* t, WayptVec& aPath, FGRunwayRef aRwy) { // assume we're routing from enroute, to the runway. // for departures, we'll flip the result points - Transition* t = findTransitionByEnroute(aEnroute); WayptVec::iterator firstCommon = _common.begin(); if (t) { - t->route(aEnroute, aPath); + t->route(aPath); Waypt* transEnd = t->procedureEnd(); for (; firstCommon != _common.end(); ++firstCommon) { @@ -170,9 +218,7 @@ bool ArrivalDeparture::commonRoute(Waypt* aEnroute, WayptVec& aPath, FGRunwayRef // common section after the transition. firstCommon = _common.begin(); } else { - if (aEnroute && !(*firstCommon)->matches(aEnroute)) { - return false; - } + // no tranasition } // of not using a transition // append (some) common points @@ -193,7 +239,7 @@ bool ArrivalDeparture::commonRoute(Waypt* aEnroute, WayptVec& aPath, FGRunwayRef } SG_LOG(SG_GENERAL, SG_INFO, ident() << " using runway transition for " << r->first->ident()); - r->second->route(NULL, aPath); + r->second->route(aPath); return true; } @@ -241,13 +287,12 @@ WayptRef ArrivalDeparture::findBestTransition(const SGGeod& aPos) const return w; } -WayptRef ArrivalDeparture::findTransitionByName(const string& aIdent) const +Transition* ArrivalDeparture::findTransitionByName(const string& aIdent) const { WptTransitionMap::const_iterator eit; for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) { - WayptRef c = eit->second->enroute(); - if (c->ident() == aIdent) { - return c; + if (eit->second->ident() == aIdent) { + return eit->second; } } @@ -256,12 +301,12 @@ WayptRef ArrivalDeparture::findTransitionByName(const string& aIdent) const //////////////////////////////////////////////////////////////////////////// -SID::SID(const string& aIdent) : - ArrivalDeparture(aIdent) +SID::SID(const string& aIdent, FGAirport* apt) : + ArrivalDeparture(aIdent, apt) { } -bool SID::route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath) +bool SID::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath) { if (!isForRunway(aWay)) { SG_LOG(SG_GENERAL, SG_WARN, "SID " << ident() << " not for runway " << aWay->ident()); @@ -269,7 +314,7 @@ bool SID::route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath) } WayptVec path; - if (!commonRoute(aEnroute, path, aWay)) { + if (!commonRoute(trans, path, aWay)) { return false; } @@ -283,24 +328,25 @@ bool SID::route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath) //////////////////////////////////////////////////////////////////////////// -STAR::STAR(const string& aIdent) : - ArrivalDeparture(aIdent) +STAR::STAR(const string& aIdent, FGAirport* apt) : + ArrivalDeparture(aIdent, apt) { } -bool STAR::route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath) +bool STAR::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath) { if (aWay && !isForRunway(aWay)) { return false; } - return commonRoute(aEnroute, aPath, aWay); + return commonRoute(trans, aPath, aWay); } ///////////////////////////////////////////////////////////////////////////// -Transition::Transition(const std::string& aIdent, Procedure* aPr) : - _ident(aIdent), +Transition::Transition(const std::string& aIdent, ProcedureType ty, Procedure* aPr) : + Procedure(aIdent), + _type(ty), _parent(aPr) { assert(aPr); @@ -325,14 +371,20 @@ WayptRef Transition::procedureEnd() const return _primary[_primary.size() - 1]; } -bool Transition::route(Waypt* aEnroute, WayptVec& aPath) +bool Transition::route(WayptVec& aPath) { - if (aEnroute && !enroute()->matches(aEnroute)) { - return false; - } - aPath.insert(aPath.end(), _primary.begin(), _primary.end()); return true; } +FGAirport* Transition::airport() const +{ + return _parent->airport(); +} + +void Transition::mark(WayptFlag f) +{ + markWaypoints(_primary, f); +} + } // of namespace diff --git a/src/Navaids/procedure.hxx b/src/Navaids/procedure.hxx index f6d63e286..11bbf4908 100644 --- a/src/Navaids/procedure.hxx +++ b/src/Navaids/procedure.hxx @@ -22,6 +22,8 @@ #include +#include // for string_list + #include #include @@ -32,12 +34,32 @@ namespace flightgear { // forward decls class NavdataVisitor; -class Procedure : public Route +typedef std::vector RunwayVec; + +typedef enum { + PROCEDURE_INVALID, + PROCEDURE_APPROACH_ILS, + PROCEDURE_APPROACH_VOR, + PROCEDURE_APPROACH_NDB, + PROCEDURE_APPROACH_RNAV, + PROCEDURE_SID, + PROCEDURE_STAR, + PROCEDURE_TRANSITION, + PROCEDURE_RUNWAY_TRANSITION +} ProcedureType; + +class Procedure : public RouteBase { -public: - +public: + virtual ProcedureType type() const = 0; + virtual std::string ident() const { return _ident; } + + virtual FGAirport* airport() const = 0; + + virtual RunwayVec runways() const + { return RunwayVec(); } protected: Procedure(const std::string& aIdent); @@ -47,14 +69,16 @@ protected: /** * Encapsulate a transition segment */ -class Transition : public Route +class Transition : public Procedure { public: - bool route(Waypt* aEnroute, WayptVec& aPath); + bool route(WayptVec& aPath); Procedure* parent() const { return _parent; } + virtual FGAirport* airport() const; + /** * Return the enroute end of the transition */ @@ -65,16 +89,19 @@ public: */ WayptRef procedureEnd() const; - virtual std::string ident() const - { return _ident; } + + virtual ProcedureType type() const + { return _type; } + + void mark(WayptFlag f); private: friend class NavdataVisitor; - Transition(const std::string& aIdent, Procedure* aPr); + Transition(const std::string& aIdent, ProcedureType ty, Procedure* aPr); void setPrimary(const WayptVec& aWps); - std::string _ident; + ProcedureType _type; Procedure* _parent; WayptVec _primary; }; @@ -89,6 +116,12 @@ public: FGRunwayRef runway() { return _runway; } + static bool isApproach(ProcedureType ty); + + virtual FGAirport* airport() const; + + virtual RunwayVec runways() const; + /** * Build a route from a valid IAF to the runway, including the missed * segment. Return false if no valid transition from the specified IAF @@ -108,16 +141,19 @@ public: const WayptVec& missed() const { return _missed; } + virtual ProcedureType type() const + { return _type; } private: friend class NavdataVisitor; - Approach(const std::string& aIdent); + Approach(const std::string& aIdent, ProcedureType ty); void setRunway(FGRunwayRef aRwy); void setPrimaryAndMissed(const WayptVec& aPrimary, const WayptVec& aMissed); void addTransition(Transition* aTrans); FGRunwayRef _runway; + ProcedureType _type; typedef std::map WptTransitionMap; WptTransitionMap _transitions; @@ -129,20 +165,27 @@ private: class ArrivalDeparture : public Procedure { public: + virtual FGAirport* airport() const + { return _airport; } + /** * Predicate, test if this procedure applies to the requested runway */ - virtual bool isForRunway(FGRunwayRef aWay) const; + virtual bool isForRunway(const FGRunway* aWay) const; + + virtual RunwayVec runways() const; /** * Find a path between the runway and enroute structure. Waypoints * corresponding to the appropriate transitions and segments will be created. */ - virtual bool route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath) = 0; + virtual bool route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath) = 0; const WayptVec& common() const { return _common; } + string_list transitionIdents() const; + /** * Given an enroute location, find the best enroute transition point for * this arrival/departure. Best is currently determined as 'closest to the @@ -155,14 +198,14 @@ public: * for the route-manager and similar code that that needs to talk about * transitions in a human-meaningful way (including persistence). */ - WayptRef findTransitionByName(const std::string& aIdent) const; + Transition* findTransitionByName(const std::string& aIdent) const; Transition* findTransitionByEnroute(Waypt* aEnroute) const; protected: - bool commonRoute(Waypt* aEnroute, WayptVec& aPath, FGRunwayRef aRwy); + bool commonRoute(Transition* t, WayptVec& aPath, FGRunwayRef aRwy); - ArrivalDeparture(const std::string& aIdent); + ArrivalDeparture(const std::string& aIdent, FGAirport* apt); void addRunway(FGRunwayRef aRwy); @@ -170,6 +213,7 @@ protected: typedef std::map RunwayTransitionMap; RunwayTransitionMap _runways; + virtual WayptFlag flagType() const = 0; private: friend class NavdataVisitor; @@ -179,6 +223,7 @@ private: void addRunwayTransition(FGRunwayRef aRwy, Transition* aTrans); + FGAirport* _airport; WayptVec _common; typedef std::map WptTransitionMap; @@ -190,23 +235,37 @@ private: class SID : public ArrivalDeparture { public: - virtual bool route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath); + virtual bool route(FGRunwayRef aWay, Transition* aTrans, WayptVec& aPath); + + virtual ProcedureType type() const + { return PROCEDURE_SID; } + +protected: + virtual WayptFlag flagType() const + { return WPT_DEPARTURE; } private: friend class NavdataVisitor; - SID(const std::string& aIdent); + SID(const std::string& aIdent, FGAirport* apt); }; class STAR : public ArrivalDeparture { public: - virtual bool route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath); + virtual bool route(FGRunwayRef aWay, Transition* aTrans, WayptVec& aPath); + + virtual ProcedureType type() const + { return PROCEDURE_STAR; } + +protected: + virtual WayptFlag flagType() const + { return WPT_ARRIVAL; } private: friend class NavdataVisitor; - STAR(const std::string& aIdent); + STAR(const std::string& aIdent, FGAirport* apt); }; } // of namespace diff --git a/src/Navaids/route.cxx b/src/Navaids/route.cxx index 4afed5184..88d950821 100644 --- a/src/Navaids/route.cxx +++ b/src/Navaids/route.cxx @@ -31,18 +31,23 @@ // Boost #include #include +#include // SimGear #include -#include #include #include #include +#include +#include +#include // FlightGear #include
+#include "Main/fg_props.hxx" #include #include +#include #include using std::string; @@ -54,7 +59,12 @@ namespace flightgear { const double NO_MAG_VAR = -1000.0; // an impossible mag-var value -Waypt::Waypt(Route* aOwner) : +bool isMachRestrict(RouteRestriction rr) +{ + return (rr == SPEED_RESTRICT_MACH) || (rr == SPEED_COMPUTED_MACH); +} + +Waypt::Waypt(RouteBase* aOwner) : _altitudeFt(0.0), _speed(0.0), _altRestrict(RESTRICT_NONE), @@ -189,7 +199,7 @@ static const char* restrictionToString(RouteRestriction aRestrict) } } -Waypt* Waypt::createInstance(Route* aOwner, const std::string& aTypeName) +Waypt* Waypt::createInstance(RouteBase* aOwner, const std::string& aTypeName) { Waypt* r = NULL; if (aTypeName == "basic") { @@ -220,7 +230,7 @@ Waypt* Waypt::createInstance(Route* aOwner, const std::string& aTypeName) return r; } -WayptRef Waypt::createFromProperties(Route* aOwner, SGPropertyNode_ptr aProp) +WayptRef Waypt::createFromProperties(RouteBase* aOwner, SGPropertyNode_ptr aProp) { if (!aProp->hasChild("type")) { throw sg_io_exception("bad props node, no type provided", @@ -252,6 +262,10 @@ void Waypt::initFromProperties(SGPropertyNode_ptr aProp) setFlag(WPT_ARRIVAL, aProp->getBoolValue("arrival")); } + if (aProp->hasChild("approach")) { + setFlag(WPT_APPROACH, aProp->getBoolValue("approach")); + } + if (aProp->hasChild("departure")) { setFlag(WPT_DEPARTURE, aProp->getBoolValue("departure")); } @@ -287,6 +301,10 @@ void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const aProp->setBoolValue("arrival", true); } + if (flag(WPT_APPROACH)) { + aProp->setBoolValue("approach", true); + } + if (flag(WPT_MISS)) { aProp->setBoolValue("miss", true); } @@ -306,7 +324,7 @@ void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const } } -void Route::dumpRouteToFile(const WayptVec& aRoute, const std::string& aName) +void RouteBase::dumpRouteToKML(const WayptVec& aRoute, const std::string& aName) { SGPath p = "/Users/jmt/Desktop/" + aName + ".kml"; std::fstream f; @@ -321,7 +339,7 @@ void Route::dumpRouteToFile(const WayptVec& aRoute, const std::string& aName) "\n" "\n"; - dumpRouteToLineString(aName, aRoute, f); + dumpRouteToKMLLineString(aName, aRoute, f); // post-amble f << "\n" @@ -329,7 +347,7 @@ void Route::dumpRouteToFile(const WayptVec& aRoute, const std::string& aName) f.close(); } -void Route::dumpRouteToLineString(const std::string& aIdent, +void RouteBase::dumpRouteToKMLLineString(const std::string& aIdent, const WayptVec& aRoute, std::ostream& aStream) { // preamble @@ -351,59 +369,7 @@ void Route::dumpRouteToLineString(const std::string& aIdent, "\n" << endl; } -/////////////////////////////////////////////////////////////////////////// - -class NavdataVisitor : public XMLVisitor { -public: - NavdataVisitor(FGAirport* aApt, const SGPath& aPath); - -protected: - virtual void startXML (); - virtual void endXML (); - virtual void startElement (const char * name, const XMLAttributes &atts); - virtual void endElement (const char * name); - virtual void data (const char * s, int len); - virtual void pi (const char * target, const char * data); - virtual void warning (const char * message, int line, int column); - virtual void error (const char * message, int line, int column); - -private: - Waypt* buildWaypoint(Route* owner); - void processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts); - - void finishApproach(); - void finishSid(); - void finishStar(); - - FGAirport* _airport; - SGPath _path; - string _text; ///< last element text value - - SID* _sid; - STAR* _star; - Approach* _approach; - Transition* _transition; - Procedure* _procedure; - - WayptVec _waypoints; ///< waypoint list for current approach/sid/star - WayptVec _transWaypts; ///< waypoint list for current transition - - string _wayptName; - string _wayptType; - string _ident; // id of segment under construction - string _transIdent; - double _longitude, _latitude, _altitude, _speed; - RouteRestriction _altRestrict; - - double _holdRadial; // inbound hold radial, or -1 if radial is 'inbound' - double _holdTD; ///< hold time (seconds) or distance (nm), based on flag below - bool _holdRighthanded; - bool _holdDistance; // true, TD is distance in nm; false, TD is time in seconds - - double _course, _radial, _dmeDistance; -}; - -void Route::loadAirportProcedures(const SGPath& aPath, FGAirport* aApt) +void RouteBase::loadAirportProcedures(const SGPath& aPath, FGAirport* aApt) { assert(aApt); try { @@ -418,319 +384,1002 @@ void Route::loadAirportProcedures(const SGPath& aPath, FGAirport* aApt) } } -NavdataVisitor::NavdataVisitor(FGAirport* aApt, const SGPath& aPath): - _airport(aApt), - _path(aPath), +//////////////////////////////////////////////////////////////////////////// + +FlightPlan::FlightPlan() : + _currentIndex(-1), + _departureRunway(NULL), + _destinationRunway(NULL), _sid(NULL), _star(NULL), _approach(NULL), - _transition(NULL), - _procedure(NULL) + _delegate(NULL) { + } - -void NavdataVisitor::startXML() + +FlightPlan::~FlightPlan() { + } - -void NavdataVisitor::endXML() + +FlightPlan* FlightPlan::clone(const string& newIdent) const { -} - -void NavdataVisitor::startElement(const char* name, const XMLAttributes &atts) -{ - _text.clear(); - string tag(name); - if (tag == "Airport") { - string icao(atts.getValue("ICAOcode")); - if (_airport->ident() != icao) { - throw sg_format_exception("Airport and ICAO mismatch", icao, _path.str()); - } - } else if (tag == "Sid") { - string ident(atts.getValue("Name")); - _sid = new SID(ident); - _procedure = _sid; - _waypoints.clear(); - processRunways(_sid, atts); - } else if (tag == "Star") { - string ident(atts.getValue("Name")); - _star = new STAR(ident); - _procedure = _star; - _waypoints.clear(); - processRunways(_star, atts); - } else if ((tag == "Sid_Waypoint") || - (tag == "App_Waypoint") || - (tag == "Star_Waypoint") || - (tag == "AppTr_Waypoint") || - (tag == "SidTr_Waypoint") || - (tag == "RwyTr_Waypoint")) - { - // reset waypoint data - _speed = 0.0; - _altRestrict = RESTRICT_NONE; - _altitude = 0.0; - } else if (tag == "Approach") { - _ident = atts.getValue("Name"); - _waypoints.clear(); - _approach = new Approach(_ident); - _procedure = _approach; - } else if ((tag == "Sid_Transition") || - (tag == "App_Transition") || - (tag == "Star_Transition")) { - _transIdent = atts.getValue("Name"); - _transition = new Transition(_transIdent, _procedure); - _transWaypts.clear(); - } else if (tag == "RunwayTransition") { - _transIdent = atts.getValue("Runway"); - _transition = new Transition(_transIdent, _procedure); - _transWaypts.clear(); - } else { - - } -} - -void NavdataVisitor::processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts) -{ - string v("All"); - if (atts.hasAttribute("Runways")) { - v = atts.getValue("Runways"); + FlightPlan* c = new FlightPlan(); + c->_ident = newIdent.empty() ? _ident : newIdent; + +// copy destination / departure data. + c->setDeparture(_departure); + c->setDeparture(_departureRunway); + + if (_approach) { + c->setApproach(_approach); + } else if (_destinationRunway) { + c->setDestination(_destinationRunway); + } else if (_destination) { + c->setDestination(_destination); } - if (v == "All") { - for (unsigned int r=0; r<_airport->numRunways(); ++r) { - aProc->addRunway(_airport->getRunwayByIndex(r)); - } + c->setSTAR(_star); + c->setSID(_sid); + +// copy legs + for (int l=0; l < numLegs(); ++l) { + c->_legs.push_back(_legs[l]->cloneFor(c)); + } + + return c; +} + +void FlightPlan::setIdent(const string& s) +{ + _ident = s; +} + +string FlightPlan::ident() const +{ + return _ident; +} + +FlightPlan::Leg* FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex) +{ + if (!aWpt) { + return NULL; + } + + WayptVec wps; + wps.push_back(aWpt); + + int index = aIndex; + if ((aIndex == -1) || (aIndex > (int) _legs.size())) { + index = _legs.size(); + } + + insertWayptsAtIndex(wps, index); + return legAtIndex(aIndex); +} + +void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex) +{ + if (wps.empty()) { return; } - vector rwys; - boost::split(rwys, v, boost::is_any_of(" ,")); - for (unsigned int r=0; rgetRunwayByIdent(rwys[r]); - aProc->addRunway(rwy); + int index = aIndex; + if ((aIndex == -1) || (aIndex > (int) _legs.size())) { + index = _legs.size(); + } + + LegVec::iterator it = _legs.begin(); + it += index; + + int endIndex = index + wps.size() - 1; + if (_currentIndex >= endIndex) { + _currentIndex += wps.size(); + } + + LegVec newLegs; + BOOST_FOREACH(WayptRef wp, wps) { + newLegs.push_back(new Leg(this, wp)); + } + + _legs.insert(it, newLegs.begin(), newLegs.end()); + rebuildLegData(); + + if (_delegate) { + _delegate->runWaypointsChanged(); } } -void NavdataVisitor::endElement(const char* name) +void FlightPlan::deleteIndex(int aIndex) { - string tag(name); - if ((tag == "Sid_Waypoint") || - (tag == "App_Waypoint") || - (tag == "Star_Waypoint")) - { - _waypoints.push_back(buildWaypoint(_procedure)); - } else if ((tag == "AppTr_Waypoint") || - (tag == "SidTr_Waypoint") || - (tag == "RwyTr_Waypoint") || - (tag == "StarTr_Waypoint")) - { - _transWaypts.push_back(buildWaypoint(_transition)); - } else if (tag == "Sid_Transition") { - assert(_sid); - // SID waypoints are stored backwards, to share code with STARs - std::reverse(_transWaypts.begin(), _transWaypts.end()); - _transition->setPrimary(_transWaypts); - _sid->addTransition(_transition); - } else if (tag == "Star_Transition") { - assert(_star); - _transition->setPrimary(_transWaypts); - _star->addTransition(_transition); - } else if (tag == "App_Transition") { - assert(_approach); - _transition->setPrimary(_transWaypts); - _approach->addTransition(_transition); - } else if (tag == "RunwayTransition") { - ArrivalDeparture* ad; - if (_sid) { - // SID waypoints are stored backwards, to share code with STARs - std::reverse(_transWaypts.begin(), _transWaypts.end()); - ad = _sid; - } else { - ad = _star; + int index = aIndex; + if (aIndex < 0) { // negative indices count the the end + index = _legs.size() + index; + } + + if ((index < 0) || (index >= numLegs())) { + SG_LOG(SG_AUTOPILOT, SG_WARN, "removeAtIndex with invalid index:" << aIndex); + return; + } + LegVec::iterator it = _legs.begin(); + it += index; + Leg* l = *it; + _legs.erase(it); + delete l; + + bool curChanged = false; + if (_currentIndex == index) { + // current waypoint was removed + curChanged = true; + } else if (_currentIndex > index) { + --_currentIndex; // shift current index down if necessary + } + + rebuildLegData(); + if (_delegate) { + _delegate->runWaypointsChanged(); + if (curChanged) { + _delegate->runCurrentWaypointChanged(); } - - _transition->setPrimary(_transWaypts); - FGRunwayRef rwy = _airport->getRunwayByIdent(_transIdent); - ad->addRunwayTransition(rwy, _transition); - } else if (tag == "Approach") { - finishApproach(); - } else if (tag == "Sid") { - finishSid(); - } else if (tag == "Star") { - finishStar(); - } else if (tag == "Longitude") { - _longitude = atof(_text.c_str()); - } else if (tag == "Latitude") { - _latitude = atof(_text.c_str()); - } else if (tag == "Name") { - _wayptName = _text; - } else if (tag == "Type") { - _wayptType = _text; - } else if (tag == "Speed") { - _speed = atoi(_text.c_str()); - } else if (tag == "Altitude") { - _altitude = atof(_text.c_str()); - } else if (tag == "AltitudeRestriction") { - if (_text == "at") { - _altRestrict = RESTRICT_AT; - } else if (_text == "above") { - _altRestrict = RESTRICT_ABOVE; - } else if (_text == "below") { - _altRestrict = RESTRICT_BELOW; - } else { - throw sg_format_exception("Unrecognized altitude restriction", _text); - } - } else if (tag == "Hld_Rad_or_Inbd") { - if (_text == "Inbd") { - _holdRadial = -1.0; - } - } else if (tag == "Hld_Time_or_Dist") { - _holdDistance = (_text == "Dist"); - } else if (tag == "Hld_Rad_value") { - _holdRadial = atof(_text.c_str()); - } else if (tag == "Hld_Turn") { - _holdRighthanded = (_text == "Right"); - } else if (tag == "Hld_td_value") { - _holdTD = atof(_text.c_str()); - } else if (tag == "Hdg_Crs_value") { - _course = atof(_text.c_str()); - } else if (tag == "DMEtoIntercept") { - _dmeDistance = atof(_text.c_str()); - } else if (tag == "RadialtoIntercept") { - _radial = atof(_text.c_str()); - } else { - } } - -Waypt* NavdataVisitor::buildWaypoint(Route* owner) + +void FlightPlan::clear() { - Waypt* wp = NULL; - if (_wayptType == "Normal") { - // new LatLonWaypoint - SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); - wp = new BasicWaypt(pos, _wayptName, owner); - } else if (_wayptType == "Runway") { - string ident = _wayptName.substr(2); - FGRunwayRef rwy = _airport->getRunwayByIdent(ident); - wp = new RunwayWaypt(rwy, owner); - } else if (_wayptType == "Hold") { - SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); - Hold* h = new Hold(pos, _wayptName, owner); - wp = h; - if (_holdRighthanded) { - h->setRightHanded(); - } else { - h->setLeftHanded(); - } - - if (_holdDistance) { - h->setHoldDistance(_holdTD); - } else { - h->setHoldTime(_holdTD * 60.0); - } - - if (_holdRadial >= 0.0) { - h->setHoldRadial(_holdRadial); - } - } else if (_wayptType == "Vectors") { - wp = new ATCVectors(owner, _airport); - } else if ((_wayptType == "Intc") || (_wayptType == "VorRadialIntc")) { - SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); - wp = new RadialIntercept(owner, _wayptName, pos, _course, _radial); - } else if (_wayptType == "DmeIntc") { - SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); - wp = new DMEIntercept(owner, _wayptName, pos, _course, _dmeDistance); - } else if (_wayptType == "ConstHdgtoAlt") { - wp = new HeadingToAltitude(owner, _wayptName, _course); - } else if (_wayptType == "PBD") { - SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)), pos2; - double az2; - SGGeodesy::direct(pos, _course, _dmeDistance, pos2, az2); - wp = new BasicWaypt(pos2, _wayptName, owner); - } else { - SG_LOG(SG_GENERAL, SG_ALERT, "implement waypoint type:" << _wayptType); - throw sg_format_exception("Unrecognized waypt type", _wayptType); + _currentIndex = -1; + BOOST_FOREACH(Leg* l, _legs) { + delete l; } - - assert(wp); - if ((_altitude > 0.0) && (_altRestrict != RESTRICT_NONE)) { - wp->setAltitude(_altitude,_altRestrict); + _legs.clear(); + rebuildLegData(); + if (_delegate) { + _delegate->runDepartureChanged(); + _delegate->runArrivalChanged(); + _delegate->runWaypointsChanged(); + _delegate->runCurrentWaypointChanged(); } - - if (_speed > 0.0) { - wp->setSpeed(_speed, RESTRICT_AT); // or _BELOW? - } - - return wp; } - -void NavdataVisitor::finishApproach() -{ - WayptVec::iterator it; - FGRunwayRef rwy; -// find the runway node - for (it = _waypoints.begin(); it != _waypoints.end(); ++it) { - FGPositionedRef navid = (*it)->source(); - if (!navid) { +int FlightPlan::clearWayptsWithFlag(WayptFlag flag) +{ + int count = 0; + for (unsigned int i=0; i<_legs.size(); ++i) { + Leg* l = _legs[i]; + if (!l->waypoint()->flag(flag)) { continue; } - if (navid->type() == FGPositioned::RUNWAY) { - rwy = (FGRunway*) navid.get(); - break; + // okay, we're going to clear this leg + ++count; + if (_currentIndex > (int) i) { + --_currentIndex; + } + + delete l; + LegVec::iterator it = _legs.begin(); + it += i; + _legs.erase(it); + } + + if (count == 0) { + return 0; // nothing was cleared, don't fire the delegate + } + + rebuildLegData(); + if (_delegate) { + _delegate->runWaypointsChanged(); + _delegate->runCurrentWaypointChanged(); + } + + return count; +} + +void FlightPlan::setCurrentIndex(int index) +{ + if ((index < 0) || (index >= numLegs())) { + throw sg_range_exception("invalid leg index", "FlightPlan::setCurrentIndex"); + } + + if (index == _currentIndex) { + return; + } + + _currentIndex = index; + if (_delegate) { + _delegate->runCurrentWaypointChanged(); + } +} + +int FlightPlan::findWayptIndex(const SGGeod& aPos) const +{ + for (int i=0; iwaypoint()->matches(aPos)) { + return i; } } - if (!rwy) { - throw sg_format_exception("Malformed approach, no runway waypt", _ident); + return -1; +} + +FlightPlan::Leg* FlightPlan::currentLeg() const +{ + if ((_currentIndex < 0) || (_currentIndex >= numLegs())) + return NULL; + return legAtIndex(_currentIndex); +} + +FlightPlan::Leg* FlightPlan::previousLeg() const +{ + if (_currentIndex == 0) { + return NULL; } - WayptVec primary(_waypoints.begin(), it); - // erase all points up to and including the runway, to leave only the - // missed segments - _waypoints.erase(_waypoints.begin(), ++it); + return legAtIndex(_currentIndex - 1); +} + +FlightPlan::Leg* FlightPlan::nextLeg() const +{ + if ((_currentIndex < 0) || ((_currentIndex + 1) >= numLegs())) { + return NULL; + } - _approach->setRunway(rwy); - _approach->setPrimaryAndMissed(primary, _waypoints); - _airport->addApproach(_approach); - _approach = NULL; + return legAtIndex(_currentIndex + 1); } -void NavdataVisitor::finishSid() +FlightPlan::Leg* FlightPlan::legAtIndex(int index) const { - // reverse order, because that's how we deal with commonality between - // STARs and SIDs. SID::route undoes this - std::reverse(_waypoints.begin(), _waypoints.end()); - _sid->setCommon(_waypoints); - _airport->addSID(_sid); - _sid = NULL; + if ((index < 0) || (index >= numLegs())) { + throw sg_range_exception("index out of range", "FlightPlan::legAtIndex"); + } + + return _legs[index]; } - -void NavdataVisitor::finishStar() + +int FlightPlan::findLegIndex(const Leg *l) const { - _star->setCommon(_waypoints); - _airport->addSTAR(_star); - _star = NULL; + for (unsigned int i=0; i<_legs.size(); ++i) { + if (_legs[i] == l) { + return i; + } + } + + return -1; } -void NavdataVisitor::data (const char * s, int len) +void FlightPlan::setDeparture(FGAirport* apt) { - _text += string(s, len); + if (apt == _departure) { + return; + } + + _departure = apt; + _departureRunway = NULL; + setSID((SID*)NULL); + + if (_delegate) { + _delegate->runDepartureChanged(); + } +} + +void FlightPlan::setDeparture(FGRunway* rwy) +{ + if (_departureRunway == rwy) { + return; + } + + _departureRunway = rwy; + if (rwy->airport() != _departure) { + _departure = rwy->airport(); + setSID((SID*)NULL); + } + + if (_delegate) { + _delegate->runDepartureChanged(); + } +} + +void FlightPlan::setSID(SID* sid, const std::string& transition) +{ + if (sid == _sid) { + return; + } + + _sid = sid; + _sidTransition = transition; + + if (_delegate) { + _delegate->runDepartureChanged(); + } +} + +void FlightPlan::setSID(Transition* trans) +{ + if (!trans) { + setSID((SID*) NULL); + 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()); +} + +Transition* FlightPlan::sidTransition() const +{ + if (!_sid || _sidTransition.empty()) { + return NULL; + } + + return _sid->findTransitionByName(_sidTransition); } +void FlightPlan::setDestination(FGAirport* apt) +{ + if (apt == _destination) { + return; + } + + _destination = apt; + _destinationRunway = NULL; + setSTAR((STAR*)NULL); -void NavdataVisitor::pi (const char * target, const char * data) { - //cout << "Processing instruction " << target << ' ' << data << endl; + if (_delegate) { + _delegate->runArrivalChanged(); + } +} + +void FlightPlan::setDestination(FGRunway* rwy) +{ + if (_destinationRunway == rwy) { + return; + } + + _destinationRunway = rwy; + if (_destination != rwy->airport()) { + _destination = rwy->airport(); + setSTAR((STAR*)NULL); + } + + if (_delegate) { + _delegate->runArrivalChanged(); + } +} + +void FlightPlan::setSTAR(STAR* star, const std::string& transition) +{ + if (_star == star) { + return; + } + + _star = star; + _starTransition = transition; + + if (_delegate) { + _delegate->runArrivalChanged(); + } +} + +void FlightPlan::setSTAR(Transition* trans) +{ + if (!trans) { + setSTAR((STAR*) NULL); + return; + } + + if (trans->parent()->type() != PROCEDURE_STAR) + throw sg_exception("FlightPlan::setSTAR: transition does not belong to a STAR"); + + setSTAR((STAR*) trans->parent(), trans->ident()); } -void NavdataVisitor::warning (const char * message, int line, int column) { - SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')'); +Transition* FlightPlan::starTransition() const +{ + if (!_star || _starTransition.empty()) { + return NULL; + } + + return _star->findTransitionByName(_starTransition); +} + +void FlightPlan::setApproach(flightgear::Approach *app) +{ + if (_approach == app) { + return; + } + + _approach = app; + if (app) { + // keep runway + airport in sync + if (_destinationRunway != _approach->runway()) { + _destinationRunway = _approach->runway(); + } + + if (_destination != _destinationRunway->airport()) { + _destination = _destinationRunway->airport(); + } + } + + if (_delegate) { + _delegate->runArrivalChanged(); + } +} + +bool FlightPlan::save(const SGPath& path) +{ + SG_LOG(SG_IO, SG_INFO, "Saving route to " << path.str()); + 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 + writeProperties(path.str(), d, true /* write-all */); + return true; + } catch (sg_exception& e) { + SG_LOG(SG_IO, SG_ALERT, "Failed to save flight-plan '" << path.str() << "'. " << e.getMessage()); + return false; + } +} + +bool FlightPlan::load(const SGPath& path) +{ + if (!path.exists()) + { + SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << path.str() + << "'. The file does not exist."); + return false; + } + + SGPropertyNode_ptr routeData(new SGPropertyNode); + SG_LOG(SG_IO, SG_INFO, "going to read flight-plan from:" << path.str()); + + bool Status = false; + try { + readProperties(path.str(), routeData); + } catch (sg_exception& ) { + // if XML parsing fails, the file might be simple textual list of waypoints + Status = loadPlainTextRoute(path); + routeData = 0; + } + + if (routeData.valid()) + { + try { + int version = routeData->getIntValue("version", 1); + if (version == 1) { + loadVersion1XMLRoute(routeData); + } else if (version == 2) { + loadVersion2XMLRoute(routeData); + } else { + throw sg_io_exception("unsupported XML route version"); + } + Status = true; + } catch (sg_exception& e) { + SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin() + << "'. " << e.getMessage()); + Status = false; + } + } + + rebuildLegData(); + if (_delegate) { + _delegate->runWaypointsChanged(); + } + + return Status; } -void NavdataVisitor::error (const char * message, int line, int column) { - SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')'); +void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData) +{ + // departure nodes + SGPropertyNode* dep = routeData->getChild("departure"); + if (dep) { + string depIdent = dep->getStringValue("airport"); + setDeparture((FGAirport*) fgFindAirportID(depIdent)); + if (_departure) { + if (dep->hasChild("runway")) { + setDeparture(_departure->getRunwayByIdent(dep->getStringValue("runway"))); + } + + if (dep->hasChild("sid")) { + setSID(_departure->findSIDWithIdent(dep->getStringValue("sid"))); + } + // departure->setStringValue("transition", dep->getStringValue("transition")); + } + } + + // destination + SGPropertyNode* dst = routeData->getChild("destination"); + if (dst) { + setDestination((FGAirport*) fgFindAirportID(dst->getStringValue("airport"))); + if (_destination) { + if (dst->hasChild("runway")) { + setDestination(_destination->getRunwayByIdent(dst->getStringValue("runway"))); + } + + if (dst->hasChild("star")) { + setSTAR(_destination->findSTARWithIdent(dst->getStringValue("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")); + } + + // 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")); + } // of cruise data loading + } +void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData) +{ + loadXMLRouteHeader(routeData); + + // route nodes + _legs.clear(); + SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); + for (int i=0; inChildren(); ++i) { + SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i); + Leg* l = new Leg(this, Waypt::createFromProperties(NULL, wpNode)); + _legs.push_back(l); + } // of route iteration +} + +void FlightPlan::loadVersion1XMLRoute(SGPropertyNode_ptr routeData) +{ + loadXMLRouteHeader(routeData); + + // _legs nodes + _legs.clear(); + SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); + for (int i=0; inChildren(); ++i) { + SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i); + Leg* l = new Leg(this, parseVersion1XMLWaypt(wpNode)); + _legs.push_back(l); + } // of route iteration + +} + +WayptRef FlightPlan::parseVersion1XMLWaypt(SGPropertyNode* aWP) +{ + SGGeod lastPos; + if (!_legs.empty()) { + lastPos = _legs.back()->waypoint()->position(); + } else if (_departure) { + lastPos = _departure->geod(); + } + + WayptRef w; + string ident(aWP->getStringValue("ident")); + if (aWP->hasChild("longitude-deg")) { + // explicit longitude/latitude + w = new BasicWaypt(SGGeod::fromDeg(aWP->getDoubleValue("longitude-deg"), + aWP->getDoubleValue("latitude-deg")), ident, NULL); + + } else { + string nid = aWP->getStringValue("navid", ident.c_str()); + FGPositionedRef p = FGPositioned::findClosestWithIdent(nid, lastPos); + if (!p) { + throw sg_io_exception("bad route file, unknown navid:" + nid); + } + + SGGeod pos(p->geod()); + if (aWP->hasChild("offset-nm") && aWP->hasChild("offset-radial")) { + double radialDeg = aWP->getDoubleValue("offset-radial"); + // convert magnetic radial to a true radial! + radialDeg += magvarDegAt(pos); + double offsetNm = aWP->getDoubleValue("offset-nm"); + double az2; + SGGeodesy::direct(p->geod(), radialDeg, offsetNm * SG_NM_TO_METER, pos, az2); + } + + w = new BasicWaypt(pos, ident, NULL); + } + + double altFt = aWP->getDoubleValue("altitude-ft", -9999.9); + if (altFt > -9990.0) { + w->setAltitude(altFt, RESTRICT_AT); + } + + return w; +} + +bool FlightPlan::loadPlainTextRoute(const SGPath& path) +{ + try { + sg_gzifstream in(path.str().c_str()); + if (!in.is_open()) { + throw sg_io_exception("Cannot open file for reading."); + } + + _legs.clear(); + while (!in.eof()) { + string line; + getline(in, line, '\n'); + // trim CR from end of line, if found + if (line[line.size() - 1] == '\r') { + line.erase(line.size() - 1, 1); + } + + line = simgear::strutils::strip(line); + if (line.empty() || (line[0] == '#')) { + continue; // ignore empty/comment lines + } + + WayptRef w = waypointFromString(line); + if (!w) { + throw sg_io_exception("Failed to create waypoint from line '" + line + "'."); + } + + _legs.push_back(new Leg(this, w)); + } // of line iteration + } catch (sg_exception& e) { + SG_LOG(SG_IO, SG_ALERT, "Failed to load route from: '" << path.str() << "'. " << e.getMessage()); + _legs.clear(); + return false; + } + + return true; +} + +double FlightPlan::magvarDegAt(const SGGeod& pos) const +{ + double jd = globals->get_time_params()->getJD(); + return sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES; +} + +WayptRef FlightPlan::waypointFromString(const string& tgt ) +{ + string target(boost::to_upper_copy(tgt)); + WayptRef wpt; + + // extract altitude + double altFt = 0.0; + RouteRestriction altSetting = RESTRICT_NONE; + + size_t pos = target.find( '@' ); + if ( pos != string::npos ) { + altFt = atof( target.c_str() + pos + 1 ); + target = target.substr( 0, pos ); + if ( !strcmp(fgGetString("/sim/startup/units"), "meter") ) + altFt *= SG_METER_TO_FEET; + altSetting = RESTRICT_AT; + } + + // check for lon,lat + pos = target.find( ',' ); + if ( pos != string::npos ) { + double lon = atof( target.substr(0, pos).c_str()); + double lat = atof( target.c_str() + pos + 1); + char buf[32]; + char ew = (lon < 0.0) ? 'W' : 'E'; + char ns = (lat < 0.0) ? 'S' : 'N'; + snprintf(buf, 32, "%c%03d%c%03d", ew, (int) fabs(lon), ns, (int)fabs(lat)); + + wpt = new BasicWaypt(SGGeod::fromDeg(lon, lat), buf, NULL); + if (altSetting != RESTRICT_NONE) { + wpt->setAltitude(altFt, altSetting); + } + return wpt; + } + + SGGeod basePosition; + if (_legs.empty()) { + // route is empty, use current position + basePosition = globals->get_aircraft_position(); + } else { + basePosition = _legs.back()->waypoint()->position(); + } + + string_list pieces(simgear::strutils::split(target, "/")); + FGPositionedRef p = FGPositioned::findClosestWithIdent(pieces.front(), basePosition); + if (!p) { + SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front()); + return NULL; + } + + double magvar = magvarDegAt(basePosition); + + if (pieces.size() == 1) { + wpt = new NavaidWaypoint(p, NULL); + } 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); + } else if (pieces.size() == 2) { + FGAirport* apt = dynamic_cast(p.ptr()); + if (!apt) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "Waypoint is not an airport:" << pieces.front()); + return NULL; + } + + if (!apt->hasRunwayWithIdent(pieces[1])) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]); + return NULL; + } + + FGRunway* runway = apt->getRunwayByIdent(pieces[1]); + wpt = new NavaidWaypoint(runway, NULL); + } else if (pieces.size() == 4) { + // navid/radial/navid/radial notation + FGPositionedRef p2 = FGPositioned::findClosestWithIdent(pieces[2], basePosition); + if (!p2) { + SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << pieces[2]); + return NULL; + } + + double r1 = atof(pieces[1].c_str()), + r2 = atof(pieces[3].c_str()); + r1 += magvar; + r2 += magvar; + + SGGeod intersection; + bool ok = SGGeodesy::radialIntersection(p->geod(), r1, p2->geod(), r2, intersection); + if (!ok) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "no valid intersection for:" << target); + return NULL; + } + + std::string name = p->ident() + "-" + p2->ident(); + wpt = new BasicWaypt(intersection, name, NULL); + } + + if (!wpt) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "Unable to parse waypoint:" << target); + return NULL; + } + + if (altSetting != RESTRICT_NONE) { + wpt->setAltitude(altFt, altSetting); + } + return wpt; +} + +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 +{ + Leg* c = new Leg(owner, _waypt); +// clone local data + c->_speed = _speed; + c->_speedRestrict = _speedRestrict; + c->_altitudeFt = _altitudeFt; + c->_altRestrict = _altRestrict; + + return c; +} + +FlightPlan::Leg* FlightPlan::Leg::nextLeg() const +{ + return _parent->legAtIndex(index() + 1); +} + +unsigned int FlightPlan::Leg::index() const +{ + return _parent->findLegIndex(this); +} + +int FlightPlan::Leg::altitudeFt() const +{ + if (_altRestrict != RESTRICT_NONE) { + return _altitudeFt; + } + + return _waypt->altitudeFt(); +} + +int FlightPlan::Leg::speed() const +{ + if (_speedRestrict != RESTRICT_NONE) { + return _speed; + } + + return _waypt->speed(); +} + +int FlightPlan::Leg::speedKts() const +{ + return speed(); +} + +double FlightPlan::Leg::speedMach() const +{ + if (!isMachRestrict(_speedRestrict)) { + return 0.0; + } + + return -(_speed / 100.0); +} + +RouteRestriction FlightPlan::Leg::altitudeRestriction() const +{ + if (_altRestrict != RESTRICT_NONE) { + return _altRestrict; + } + + return _waypt->altitudeRestriction(); +} + +RouteRestriction FlightPlan::Leg::speedRestriction() const +{ + if (_speedRestrict != RESTRICT_NONE) { + return _speedRestrict; + } + + return _waypt->speedRestriction(); +} + +void FlightPlan::Leg::setSpeed(RouteRestriction ty, double speed) +{ + _speedRestrict = ty; + if (isMachRestrict(ty)) { + _speed = (speed * -100); + } else { + _speed = speed; + } +} + +void FlightPlan::Leg::setAltitude(RouteRestriction ty, int altFt) +{ + _altRestrict = ty; + _altitudeFt = altFt; +} + +double FlightPlan::Leg::courseDeg() const +{ + return _courseDeg; +} + +double FlightPlan::Leg::distanceNm() const +{ + return _pathDistance; +} + +double FlightPlan::Leg::distanceAlongRoute() const +{ + return _distanceAlongPath; +} + +void FlightPlan::rebuildLegData() +{ + _totalDistance = 0.0; + int lastLeg = static_cast(_legs.size()) - 1; + for (int l=0; l crsDist = + next->waypoint()->courseAndDistanceFrom(cur->waypoint()->position()); + _legs[l]->_courseDeg = crsDist.first; + _legs[l]->_pathDistance = crsDist.second * SG_METER_TO_NM; + _legs[l]->_distanceAlongPath = _totalDistance; + _totalDistance += crsDist.second * SG_METER_TO_NM; + } // of legs iteration +} + +void FlightPlan::setDelegate(Delegate* d) +{ + // wrap any existing delegate(s) in the new one + d->_inner = _delegate; + _delegate = d; +} + +void FlightPlan::removeDelegate(Delegate* d) +{ + if (d == _delegate) { + _delegate = _delegate->_inner; + } else if (_delegate) { + _delegate->removeInner(d); + } +} + +FlightPlan::Delegate::Delegate() : + _inner(NULL) +{ + +} + +FlightPlan::Delegate::~Delegate() +{ + +} + +void FlightPlan::Delegate::removeInner(Delegate* d) +{ + if (!_inner) { + return; + } + + if (_inner == d) { + // replace with grand-child + _inner = d->_inner; + } else { // recurse downwards + _inner->removeInner(d); + } +} + +void FlightPlan::Delegate::runDepartureChanged() +{ + if (_inner) _inner->runDepartureChanged(); + departureChanged(); +} + +void FlightPlan::Delegate::runArrivalChanged() +{ + if (_inner) _inner->runArrivalChanged(); + arrivalChanged(); +} + +void FlightPlan::Delegate::runWaypointsChanged() +{ + if (_inner) _inner->runWaypointsChanged(); + waypointsChanged(); +} + +void FlightPlan::Delegate::runCurrentWaypointChanged() +{ + if (_inner) _inner->runCurrentWaypointChanged(); + currentWaypointChanged(); +} + } // of namespace flightgear diff --git a/src/Navaids/route.hxx b/src/Navaids/route.hxx index 9c35c7c60..79ccfd1e7 100644 --- a/src/Navaids/route.hxx +++ b/src/Navaids/route.hxx @@ -39,16 +39,22 @@ // forward decls class FGPositioned; class SGPath; -class FGAirport; +class FGRunway; + +#include +typedef SGSharedPtr FGAirportRef; namespace flightgear { // forward decls -class Route; +class RouteBase; class Waypt; class NavdataVisitor; - +class SID; +class STAR; +class Transition; + typedef SGSharedPtr WayptRef; typedef enum { @@ -66,7 +72,12 @@ typedef enum { WPT_GENERATED = 1 << 7, WPT_DEPARTURE = 1 << 8, - WPT_ARRIVAL = 1 << 9 + 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 } WayptFlag; typedef enum { @@ -74,9 +85,14 @@ typedef enum { RESTRICT_AT, RESTRICT_ABOVE, RESTRICT_BELOW, - SPEED_RESTRICT_MACH + SPEED_RESTRICT_MACH, ///< encode an 'AT' restriction in Mach, not IAS + RESTRICT_DELETE, ///< ignore underlying restriction (on a leg) + RESTRICT_COMPUTED, ///< data is computed, not a real restriction + SPEED_COMPUTED_MACH ///< variant on above to encode a Mach value } RouteRestriction; +bool isMachRestrict(RouteRestriction rr); + /** * Abstract base class for waypoints (and things that are treated similarly * by navigation systems) @@ -86,7 +102,7 @@ class Waypt : public SGReferenced public: virtual ~Waypt(); - Route* owner() const + RouteBase* owner() const { return _owner; } /** @@ -140,7 +156,7 @@ public: /** * Factory method */ - static WayptRef createFromProperties(Route* aOwner, SGPropertyNode_ptr aProp); + static WayptRef createFromProperties(RouteBase* aOwner, SGPropertyNode_ptr aProp); void saveAsNode(SGPropertyNode* node) const; @@ -173,7 +189,7 @@ public: protected: friend class NavdataVisitor; - Waypt(Route* aOwner); + Waypt(RouteBase* aOwner); /** * Persistence helper - read node properties from a file @@ -185,7 +201,7 @@ protected: */ virtual void writeToProperties(SGPropertyNode_ptr aProp) const; - typedef Waypt* (FactoryFunction)(Route* aOwner) ; + typedef Waypt* (FactoryFunction)(RouteBase* aOwner) ; static void registerFactory(const std::string aNodeType, FactoryFunction* aFactory); double _altitudeFt; @@ -197,16 +213,16 @@ private: /** * Create an instance of a concrete subclass, or throw an exception */ - static Waypt* createInstance(Route* aOwner, const std::string& aTypeName); + static Waypt* createInstance(RouteBase* aOwner, const std::string& aTypeName); - Route* _owner; + RouteBase* _owner; unsigned short _flags; mutable double _magVarDeg; }; typedef std::vector WayptVec; -class Route +class RouteBase { public: /** @@ -216,14 +232,219 @@ public: static void loadAirportProcedures(const SGPath& aPath, FGAirport* aApt); - static void dumpRouteToFile(const WayptVec& aRoute, const std::string& aName); + static void dumpRouteToKML(const WayptVec& aRoute, const std::string& aName); - static void dumpRouteToLineString(const std::string& aIdent, + static void dumpRouteToKMLLineString(const std::string& aIdent, const WayptVec& aRoute, std::ostream& aStream); private: }; - + +class FlightPlan : public RouteBase +{ +public: + FlightPlan(); + virtual ~FlightPlan(); + + virtual std::string ident() const; + void setIdent(const std::string& s); + + FlightPlan* clone(const std::string& newIdent = std::string()) const; + + /** + * flight-plan leg encapsulation + */ + class Leg + { + public: + FlightPlan* owner() const + { return _parent; } + + Waypt* waypoint() const + { return _waypt; } + + // reutrn the next leg after this one + Leg* nextLeg() const; + + unsigned int index() const; + + int altitudeFt() const; + int speed() const; + + int speedKts() const; + double speedMach() const; + + RouteRestriction altitudeRestriction() const; + RouteRestriction speedRestriction() const; + + void setSpeed(RouteRestriction ty, double speed); + void setAltitude(RouteRestriction ty, int altFt); + + double courseDeg() const; + double distanceNm() const; + double distanceAlongRoute() const; + private: + friend class FlightPlan; + + Leg(FlightPlan* owner, WayptRef wpt); + + Leg* cloneFor(FlightPlan* owner) const; + + FlightPlan* _parent; + RouteRestriction _speedRestrict, _altRestrict; + int _speed; + int _altitudeFt; + WayptRef _waypt; + /// length of this leg following the flown path + mutable double _pathDistance; + mutable double _courseDeg; + /// total distance of this leg from departure point + mutable double _distanceAlongPath; + }; + + class Delegate + { + public: + virtual ~Delegate(); + + virtual void departureChanged() { } + virtual void arrivalChanged() { } + virtual void waypointsChanged() { } + + virtual void currentWaypointChanged() { } + + protected: + Delegate(); + + private: + void removeInner(Delegate* d); + + void runDepartureChanged(); + void runArrivalChanged(); + void runWaypointsChanged(); + void runCurrentWaypointChanged(); + + friend class FlightPlan; + + Delegate* _inner; + }; + + Leg* insertWayptAtIndex(Waypt* aWpt, int aIndex); + void insertWayptsAtIndex(const WayptVec& wps, int aIndex); + + void deleteIndex(int index); + void clear(); + int clearWayptsWithFlag(WayptFlag flag); + + int currentIndex() const + { return _currentIndex; } + + void setCurrentIndex(int index); + + Leg* currentLeg() const; + Leg* nextLeg() const; + Leg* previousLeg() const; + + int numLegs() const + { return _legs.size(); } + + Leg* legAtIndex(int index) const; + int findLegIndex(const Leg* l) const; + + int findWayptIndex(const SGGeod& aPos) const; + + bool load(const SGPath& p); + bool save(const SGPath& p); + + FGAirportRef departureAirport() const + { return _departure; } + + FGAirportRef destinationAirport() const + { return _destination; } + + FGRunway* departureRunway() const + { return _departureRunway; } + + FGRunway* destinationRunway() const + { return _destinationRunway; } + + Approach* approach() const + { return _approach; } + + void setDeparture(FGAirport* apt); + void setDeparture(FGRunway* rwy); + + SID* sid() const + { return _sid; } + + Transition* sidTransition() const; + + void setSID(SID* sid, const std::string& transition = std::string()); + + void setSID(Transition* sidWithTrans); + + void setDestination(FGAirport* apt); + void setDestination(FGRunway* rwy); + + /** + * note setting an approach will implicitly update the destination + * airport and runway to match + */ + void setApproach(Approach* app); + + STAR* star() const + { return _star; } + + Transition* starTransition() const; + + void setSTAR(STAR* star, const std::string& transition = std::string()); + + void setSTAR(Transition* starWithTrans); + + double totalDistanceNm() const + { return _totalDistance; } + + /** + * Create a WayPoint from a string in the following format: + * - simple identifier + * - decimal-lon,decimal-lat + * - airport-id/runway-id + * - navaid/radial-deg/offset-nm + */ + WayptRef waypointFromString(const std::string& target); + + void setDelegate(Delegate* d); + void removeDelegate(Delegate* d); +private: + + bool loadPlainTextRoute(const SGPath& path); + + void loadVersion1XMLRoute(SGPropertyNode_ptr routeData); + void loadVersion2XMLRoute(SGPropertyNode_ptr routeData); + void loadXMLRouteHeader(SGPropertyNode_ptr routeData); + WayptRef parseVersion1XMLWaypt(SGPropertyNode* aWP); + + double magvarDegAt(const SGGeod& pos) const; + + std::string _ident; + int _currentIndex; + + FGAirportRef _departure, _destination; + FGRunway* _departureRunway, *_destinationRunway; + SID* _sid; + STAR* _star; + Approach* _approach; + std::string _sidTransition, _starTransition; + + double _totalDistance; + void rebuildLegData(); + + typedef std::vector LegVec; + LegVec _legs; + + Delegate* _delegate; +}; + } // of namespace flightgear #endif // of FG_ROUTE_HXX diff --git a/src/Navaids/routePath.cxx b/src/Navaids/routePath.cxx index ee67effb5..85fae3026 100644 --- a/src/Navaids/routePath.cxx +++ b/src/Navaids/routePath.cxx @@ -53,11 +53,24 @@ double pointsKnownDistanceFromGC(const SGGeoc& a, const SGGeoc&b, const SGGeoc& RoutePath::RoutePath(const flightgear::WayptVec& wpts) : _waypts(wpts) +{ + commonInit(); +} + +RoutePath::RoutePath(const flightgear::FlightPlan* fp) +{ + for (int l=0; lnumLegs(); ++l) { + _waypts.push_back(fp->legAtIndex(l)->waypoint()); + } + commonInit(); +} + +void RoutePath::commonInit() { _pathClimbFPM = 1200; _pathDescentFPM = 800; _pathIAS = 190; - _pathTurnRate = 3.0; // 3 deg/sec = 180def/min = standard rate turn + _pathTurnRate = 3.0; // 3 deg/sec = 180def/min = standard rate turn } SGGeodVec RoutePath::pathForIndex(int index) const diff --git a/src/Navaids/routePath.hxx b/src/Navaids/routePath.hxx index d04c03d08..cd35642a8 100644 --- a/src/Navaids/routePath.hxx +++ b/src/Navaids/routePath.hxx @@ -37,12 +37,15 @@ class RoutePath { public: RoutePath(const flightgear::WayptVec& wpts); - + RoutePath(const flightgear::FlightPlan* fp); + SGGeodVec pathForIndex(int index) const; SGGeod positionForIndex(int index) const; private: + void commonInit(); + class PathCtx; SGGeodVec pathForHold(flightgear::Hold* hold) const; diff --git a/src/Navaids/waypoint.cxx b/src/Navaids/waypoint.cxx index 7327107b0..dd1ae7a96 100644 --- a/src/Navaids/waypoint.cxx +++ b/src/Navaids/waypoint.cxx @@ -34,7 +34,7 @@ using std::string; namespace flightgear { -BasicWaypt::BasicWaypt(const SGGeod& aPos, const string& aIdent, Route* aOwner) : +BasicWaypt::BasicWaypt(const SGGeod& aPos, const string& aIdent, RouteBase* aOwner) : Waypt(aOwner), _pos(aPos), _ident(aIdent) @@ -44,14 +44,14 @@ BasicWaypt::BasicWaypt(const SGGeod& aPos, const string& aIdent, Route* aOwner) } } -BasicWaypt::BasicWaypt(const SGWayPoint& aWP, Route* aOwner) : +BasicWaypt::BasicWaypt(const SGWayPoint& aWP, RouteBase* aOwner) : Waypt(aOwner), _pos(aWP.get_target()), _ident(aWP.get_id()) { } -BasicWaypt::BasicWaypt(Route* aOwner) : +BasicWaypt::BasicWaypt(RouteBase* aOwner) : Waypt(aOwner) { } @@ -81,7 +81,7 @@ void BasicWaypt::writeToProperties(SGPropertyNode_ptr aProp) const ////////////////////////////////////////////////////////////////////////////// -NavaidWaypoint::NavaidWaypoint(FGPositioned* aPos, Route* aOwner) : +NavaidWaypoint::NavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner) : Waypt(aOwner), _navaid(aPos) { @@ -90,7 +90,7 @@ NavaidWaypoint::NavaidWaypoint(FGPositioned* aPos, Route* aOwner) : } } -NavaidWaypoint::NavaidWaypoint(Route* aOwner) : +NavaidWaypoint::NavaidWaypoint(RouteBase* aOwner) : Waypt(aOwner) { } @@ -142,7 +142,7 @@ void NavaidWaypoint::writeToProperties(SGPropertyNode_ptr aProp) const aProp->setDoubleValue("lat", _navaid->geod().getLatitudeDeg()); } -OffsetNavaidWaypoint::OffsetNavaidWaypoint(FGPositioned* aPos, Route* aOwner, +OffsetNavaidWaypoint::OffsetNavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner, double aRadial, double aDistNm) : NavaidWaypoint(aPos, aOwner), _radial(aRadial), @@ -151,7 +151,7 @@ OffsetNavaidWaypoint::OffsetNavaidWaypoint(FGPositioned* aPos, Route* aOwner, init(); } -OffsetNavaidWaypoint::OffsetNavaidWaypoint(Route* aOwner) : +OffsetNavaidWaypoint::OffsetNavaidWaypoint(RouteBase* aOwner) : NavaidWaypoint(aOwner) { } @@ -186,13 +186,13 @@ void OffsetNavaidWaypoint::writeToProperties(SGPropertyNode_ptr aProp) const ///////////////////////////////////////////////////////////////////////////// -RunwayWaypt::RunwayWaypt(FGRunway* aPos, Route* aOwner) : +RunwayWaypt::RunwayWaypt(FGRunway* aPos, RouteBase* aOwner) : Waypt(aOwner), _runway(aPos) { } -RunwayWaypt::RunwayWaypt(Route* aOwner) : +RunwayWaypt::RunwayWaypt(RouteBase* aOwner) : Waypt(aOwner) { } @@ -239,7 +239,7 @@ void RunwayWaypt::writeToProperties(SGPropertyNode_ptr aProp) const ///////////////////////////////////////////////////////////////////////////// -Hold::Hold(const SGGeod& aPos, const string& aIdent, Route* aOwner) : +Hold::Hold(const SGGeod& aPos, const string& aIdent, RouteBase* aOwner) : BasicWaypt(aPos, aIdent, aOwner), _righthanded(true), _isDistance(false) @@ -247,7 +247,7 @@ Hold::Hold(const SGGeod& aPos, const string& aIdent, Route* aOwner) : setFlag(WPT_DYNAMIC); } -Hold::Hold(Route* aOwner) : +Hold::Hold(RouteBase* aOwner) : BasicWaypt(aOwner), _righthanded(true), _isDistance(false) @@ -308,7 +308,7 @@ void Hold::writeToProperties(SGPropertyNode_ptr aProp) const ///////////////////////////////////////////////////////////////////////////// -HeadingToAltitude::HeadingToAltitude(Route* aOwner, const string& aIdent, +HeadingToAltitude::HeadingToAltitude(RouteBase* aOwner, const string& aIdent, double aMagHdg) : Waypt(aOwner), _ident(aIdent), @@ -317,7 +317,7 @@ HeadingToAltitude::HeadingToAltitude(Route* aOwner, const string& aIdent, setFlag(WPT_DYNAMIC); } -HeadingToAltitude::HeadingToAltitude(Route* aOwner) : +HeadingToAltitude::HeadingToAltitude(RouteBase* aOwner) : Waypt(aOwner) { } @@ -343,7 +343,7 @@ void HeadingToAltitude::writeToProperties(SGPropertyNode_ptr aProp) const ///////////////////////////////////////////////////////////////////////////// -DMEIntercept::DMEIntercept(Route* aOwner, const string& aIdent, const SGGeod& aPos, +DMEIntercept::DMEIntercept(RouteBase* aOwner, const string& aIdent, const SGGeod& aPos, double aCourseDeg, double aDistanceNm) : Waypt(aOwner), _ident(aIdent), @@ -354,7 +354,7 @@ DMEIntercept::DMEIntercept(Route* aOwner, const string& aIdent, const SGGeod& aP setFlag(WPT_DYNAMIC); } -DMEIntercept::DMEIntercept(Route* aOwner) : +DMEIntercept::DMEIntercept(RouteBase* aOwner) : Waypt(aOwner) { } @@ -388,7 +388,7 @@ void DMEIntercept::writeToProperties(SGPropertyNode_ptr aProp) const ///////////////////////////////////////////////////////////////////////////// -RadialIntercept::RadialIntercept(Route* aOwner, const string& aIdent, const SGGeod& aPos, +RadialIntercept::RadialIntercept(RouteBase* aOwner, const string& aIdent, const SGGeod& aPos, double aCourseDeg, double aRadial) : Waypt(aOwner), _ident(aIdent), @@ -399,7 +399,7 @@ RadialIntercept::RadialIntercept(Route* aOwner, const string& aIdent, const SGGe setFlag(WPT_DYNAMIC); } -RadialIntercept::RadialIntercept(Route* aOwner) : +RadialIntercept::RadialIntercept(RouteBase* aOwner) : Waypt(aOwner) { } @@ -433,7 +433,7 @@ void RadialIntercept::writeToProperties(SGPropertyNode_ptr aProp) const ///////////////////////////////////////////////////////////////////////////// -ATCVectors::ATCVectors(Route* aOwner, FGAirport* aFacility) : +ATCVectors::ATCVectors(RouteBase* aOwner, FGAirport* aFacility) : Waypt(aOwner), _facility(aFacility) { @@ -444,7 +444,7 @@ ATCVectors::~ATCVectors() { } -ATCVectors::ATCVectors(Route* aOwner) : +ATCVectors::ATCVectors(RouteBase* aOwner) : Waypt(aOwner) { } diff --git a/src/Navaids/waypoint.hxx b/src/Navaids/waypoint.hxx index ece8cadf5..47dd78105 100644 --- a/src/Navaids/waypoint.hxx +++ b/src/Navaids/waypoint.hxx @@ -36,11 +36,11 @@ class BasicWaypt : public Waypt { public: - BasicWaypt(const SGGeod& aPos, const std::string& aIdent, Route* aOwner); + BasicWaypt(const SGGeod& aPos, const std::string& aIdent, RouteBase* aOwner); - BasicWaypt(const SGWayPoint& aWP, Route* aOwner); + BasicWaypt(const SGWayPoint& aWP, RouteBase* aOwner); - BasicWaypt(Route* aOwner); + BasicWaypt(RouteBase* aOwner); virtual SGGeod position() const { return _pos; } @@ -67,9 +67,9 @@ protected: class NavaidWaypoint : public Waypt { public: - NavaidWaypoint(FGPositioned* aPos, Route* aOwner); + NavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner); - NavaidWaypoint(Route* aOwner); + NavaidWaypoint(RouteBase* aOwner); virtual SGGeod position() const; @@ -90,9 +90,9 @@ protected: class OffsetNavaidWaypoint : public NavaidWaypoint { public: - OffsetNavaidWaypoint(FGPositioned* aPos, Route* aOwner, double aRadial, double aDistNm); + OffsetNavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner, double aRadial, double aDistNm); - OffsetNavaidWaypoint(Route* aOwner); + OffsetNavaidWaypoint(RouteBase* aOwner); virtual SGGeod position() const { return _geod; } @@ -120,9 +120,9 @@ private: class RunwayWaypt : public Waypt { public: - RunwayWaypt(FGRunway* aPos, Route* aOwner); + RunwayWaypt(FGRunway* aPos, RouteBase* aOwner); - RunwayWaypt(Route* aOwner); + RunwayWaypt(RouteBase* aOwner); virtual SGGeod position() const; @@ -148,9 +148,9 @@ private: class Hold : public BasicWaypt { public: - Hold(const SGGeod& aPos, const std::string& aIdent, Route* aOwner); + Hold(const SGGeod& aPos, const std::string& aIdent, RouteBase* aOwner); - Hold(Route* aOwner); + Hold(RouteBase* aOwner); void setHoldRadial(double aInboundRadial); void setHoldDistance(double aDistanceNm); @@ -190,9 +190,9 @@ private: class HeadingToAltitude : public Waypt { public: - HeadingToAltitude(Route* aOwner, const std::string& aIdent, double aMagHdg); + HeadingToAltitude(RouteBase* aOwner, const std::string& aIdent, double aMagHdg); - HeadingToAltitude(Route* aOwner); + HeadingToAltitude(RouteBase* aOwner); virtual void initFromProperties(SGPropertyNode_ptr aProp); virtual void writeToProperties(SGPropertyNode_ptr aProp) const; @@ -222,10 +222,10 @@ private: class DMEIntercept : public Waypt { public: - DMEIntercept(Route* aOwner, const std::string& aIdent, const SGGeod& aPos, + DMEIntercept(RouteBase* aOwner, const std::string& aIdent, const SGGeod& aPos, double aCourseDeg, double aDistanceNm); - DMEIntercept(Route* aOwner); + DMEIntercept(RouteBase* aOwner); virtual void initFromProperties(SGPropertyNode_ptr aProp); virtual void writeToProperties(SGPropertyNode_ptr aProp) const; @@ -257,10 +257,10 @@ private: class RadialIntercept : public Waypt { public: - RadialIntercept(Route* aOwner, const std::string& aIdent, const SGGeod& aPos, + RadialIntercept(RouteBase* aOwner, const std::string& aIdent, const SGGeod& aPos, double aCourseDeg, double aRadialDeg); - RadialIntercept(Route* aOwner); + RadialIntercept(RouteBase* aOwner); virtual void initFromProperties(SGPropertyNode_ptr aProp); virtual void writeToProperties(SGPropertyNode_ptr aProp) const; @@ -295,10 +295,10 @@ private: class ATCVectors : public Waypt { public: - ATCVectors(Route* aOwner, FGAirport* aFacility); + ATCVectors(RouteBase* aOwner, FGAirport* aFacility); virtual ~ATCVectors(); - ATCVectors(Route* aOwner); + ATCVectors(RouteBase* aOwner); virtual void initFromProperties(SGPropertyNode_ptr aProp); virtual void writeToProperties(SGPropertyNode_ptr aProp) const; diff --git a/src/Scripting/NasalPositioned.cxx b/src/Scripting/NasalPositioned.cxx index 8842c6899..208c34e8f 100644 --- a/src/Scripting/NasalPositioned.cxx +++ b/src/Scripting/NasalPositioned.cxx @@ -27,6 +27,7 @@ #include "NasalPositioned.hxx" #include +#include #include #include @@ -48,8 +49,13 @@ #include #include +using namespace flightgear; + static void positionedGhostDestroy(void* g); static void wayptGhostDestroy(void* g); +static void legGhostDestroy(void* g); +static void routeBaseGhostDestroy(void* g); + naGhostType PositionedGhostType = { positionedGhostDestroy, "positioned" }; static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRef* out); @@ -62,12 +68,32 @@ static const char* runwayGhostGetMember(naContext c, void* g, naRef field, naRef naGhostType RunwayGhostType = { positionedGhostDestroy, "runway", runwayGhostGetMember, 0 }; static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out); - naGhostType WayptGhostType = { wayptGhostDestroy, "waypoint", wayptGhostGetMember, 0}; +static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* out); +naGhostType FPLegGhostType = { legGhostDestroy, + "flightplan-leg", + legGhostGetMember, + 0}; + +static const char* flightplanGhostGetMember(naContext c, void* g, naRef field, naRef* out); +static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef value); + +naGhostType FlightPlanGhostType = { routeBaseGhostDestroy, + "flightplan", + flightplanGhostGetMember, + flightplanGhostSetMember +}; + +static const char* procedureGhostGetMember(naContext c, void* g, naRef field, naRef* out); +naGhostType ProcedureGhostType = { routeBaseGhostDestroy, + "procedure", + procedureGhostGetMember, + 0}; + static void hashset(naContext c, naRef hash, const char* key, naRef val) { naRef s = naNewString(c); @@ -117,24 +143,58 @@ static void positionedGhostDestroy(void* g) delete pos; } -static flightgear::Waypt* wayptGhost(naRef r) +static Waypt* wayptGhost(naRef r) { if (naGhost_type(r) == &WayptGhostType) - return (flightgear::Waypt*) naGhost_ptr(r); + return (Waypt*) naGhost_ptr(r); return 0; } static void wayptGhostDestroy(void* g) { - flightgear::Waypt* wpt = (flightgear::Waypt*)g; - if (!flightgear::Waypt::put(wpt)) // unref + Waypt* wpt = (Waypt*)g; + if (!Waypt::put(wpt)) // unref delete wpt; } +static void legGhostDestroy(void* g) +{ + // nothing for now +} + + +static FlightPlan::Leg* fpLegGhost(naRef r) +{ + if (naGhost_type(r) == &FPLegGhostType) + return (FlightPlan::Leg*) naGhost_ptr(r); + return 0; +} + +static Procedure* procedureGhost(naRef r) +{ + if (naGhost_type(r) == &ProcedureGhostType) + return (Procedure*) naGhost_ptr(r); + return 0; +} + +static FlightPlan* flightplanGhost(naRef r) +{ + if (naGhost_type(r) == &FlightPlanGhostType) + return (FlightPlan*) naGhost_ptr(r); + return 0; +} + +static void routeBaseGhostDestroy(void* g) +{ + // nothing for now +} + static naRef airportPrototype; -static naRef routePrototype; +static naRef flightplanPrototype; static naRef waypointPrototype; static naRef geoCoordClass; +static naRef fpLegPrototype; +static naRef procedurePrototype; naRef ghostForPositioned(naContext c, const FGPositioned* pos) { @@ -176,16 +236,43 @@ naRef ghostForRunway(naContext c, const FGRunway* r) return naNewGhost2(c, &RunwayGhostType, (void*) r); } -naRef ghostForWaypt(naContext c, const flightgear::Waypt* wpt) +naRef ghostForWaypt(naContext c, const Waypt* wpt) { if (!wpt) { return naNil(); } - flightgear::Waypt::get(wpt); // take a ref + Waypt::get(wpt); // take a ref return naNewGhost2(c, &WayptGhostType, (void*) wpt); } +naRef ghostForLeg(naContext c, const FlightPlan::Leg* leg) +{ + if (!leg) { + return naNil(); + } + + return naNewGhost2(c, &FPLegGhostType, (void*) leg); +} + +naRef ghostForFlightPlan(naContext c, const FlightPlan* fp) +{ + if (!fp) { + return naNil(); + } + + return naNewGhost2(c, &FlightPlanGhostType, (void*) fp); +} + +naRef ghostForProcedure(naContext c, const Procedure* proc) +{ + if (!proc) { + return naNil(); + } + + return naNewGhost2(c, &ProcedureGhostType, (void*) proc); +} + static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRef* out) { const char* fieldName = naStr_data(field); @@ -218,43 +305,299 @@ static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRe return ""; } -static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out) +static const char* waypointCommonGetMember(naContext c, Waypt* wpt, const char* fieldName, naRef* out) { - const char* fieldName = naStr_data(field); - flightgear::Waypt* wpt = (flightgear::Waypt*) g; - - if (!strcmp(fieldName, "parents")) { - *out = naNewVector(c); - naVec_append(*out, waypointPrototype); - } else if (!strcmp(fieldName, "wp_name")) *out =stringToNasal(c, wpt->ident()); + if (!strcmp(fieldName, "wp_name")) *out = stringToNasal(c, wpt->ident()); else if (!strcmp(fieldName, "wp_type")) *out = stringToNasal(c, wpt->type()); else if (!strcmp(fieldName, "wp_lat")) *out = naNum(wpt->position().getLatitudeDeg()); else if (!strcmp(fieldName, "wp_lon")) *out = naNum(wpt->position().getLongitudeDeg()); else if (!strcmp(fieldName, "wp_parent_name")) { - flightgear::Procedure* proc = dynamic_cast(wpt->owner()); + Procedure* proc = dynamic_cast(wpt->owner()); *out = proc ? stringToNasal(c, proc->ident()) : naNil(); + } else if (!strcmp(fieldName, "wp_parent")) { + Procedure* proc = dynamic_cast(wpt->owner()); + *out = ghostForProcedure(c, proc); } else if (!strcmp(fieldName, "fly_type")) { if (wpt->type() == "hold") { *out = stringToNasal(c, "Hold"); } else { - *out = stringToNasal(c, wpt->flag(flightgear::WPT_OVERFLIGHT) ? "flyOver" : "flyBy"); + *out = stringToNasal(c, wpt->flag(WPT_OVERFLIGHT) ? "flyOver" : "flyBy"); } - } else if (!strcmp(fieldName, "alt_cstr")) *out = naNum(wpt->altitudeFt()); - else if (!strcmp(fieldName, "speed_cstr")) { - double s = (wpt->speedRestriction() == flightgear::SPEED_RESTRICT_MACH) - ? wpt->speedMach() : wpt->speedKts(); - *out = naNum(s); - } else if (!strcmp(fieldName, "leg_distance")) { - return "please implement me"; - } else if (!strcmp(fieldName, "leg_bearing")) { - return "please implement me"; } else { return NULL; // member not found } + + return ""; +} + +static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out) +{ + const char* fieldName = naStr_data(field); + Waypt* wpt = (flightgear::Waypt*) g; + return waypointCommonGetMember(c, wpt, fieldName, out); +} + +static RouteRestriction routeRestrictionFromString(const char* s) +{ + string u(s); + boost::to_lower(u); + if (u == "computed") return RESTRICT_COMPUTED; + if (u == "at") return RESTRICT_AT; + if (u == "mach") return SPEED_RESTRICT_MACH; + if (u == "computed-mach") return SPEED_COMPUTED_MACH; + if (u == "delete") return RESTRICT_DELETE; + return RESTRICT_NONE; +}; + +naRef routeRestrictionToNasal(naContext c, RouteRestriction rr) +{ + switch (rr) { + case RESTRICT_NONE: return naNil(); + case RESTRICT_AT: return stringToNasal(c, "at"); + case RESTRICT_ABOVE: return stringToNasal(c, "above"); + case RESTRICT_BELOW: return stringToNasal(c, "below"); + case SPEED_RESTRICT_MACH: return stringToNasal(c, "mach"); + case RESTRICT_COMPUTED: return stringToNasal(c, "computed"); + case SPEED_COMPUTED_MACH: return stringToNasal(c, "computed-mach"); + case RESTRICT_DELETE: return stringToNasal(c, "delete"); + } + + return naNil(); +} + +static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* out) +{ + const char* fieldName = naStr_data(field); + FlightPlan::Leg* leg = (FlightPlan::Leg*) g; + Waypt* wpt = leg->waypoint(); + + if (!strcmp(fieldName, "parents")) { + *out = naNewVector(c); + naVec_append(*out, fpLegPrototype); + } else if (!strcmp(fieldName, "alt_cstr")) { + *out = naNum(leg->altitudeFt()); + } else if (!strcmp(fieldName, "alt_cstr_type")) { + *out = routeRestrictionToNasal(c, leg->altitudeRestriction()); + } else if (!strcmp(fieldName, "speed_cstr")) { + double s = isMachRestrict(leg->speedRestriction()) ? leg->speedMach() : leg->speedKts(); + *out = naNum(s); + } else if (!strcmp(fieldName, "speed_cstr_type")) { + *out = routeRestrictionToNasal(c, leg->speedRestriction()); + } else if (!strcmp(fieldName, "leg_distance")) { + *out = naNum(leg->distanceNm()); + } else if (!strcmp(fieldName, "leg_bearing")) { + *out = naNum(leg->courseDeg()); + } else if (!strcmp(fieldName, "distance_along_route")) { + *out = naNum(leg->distanceAlongRoute()); + } else { // check for fields defined on the underlying waypoint + return waypointCommonGetMember(c, wpt, fieldName, out); + } return ""; // success } +static const char* flightplanGhostGetMember(naContext c, void* g, naRef field, naRef* out) +{ + const char* fieldName = naStr_data(field); + FlightPlan* fp = (FlightPlan*) g; + + if (!strcmp(fieldName, "parents")) { + *out = naNewVector(c); + naVec_append(*out, flightplanPrototype); + } else if (!strcmp(fieldName, "id")) *out = stringToNasal(c, fp->ident()); + else if (!strcmp(fieldName, "departure")) *out = ghostForAirport(c, fp->departureAirport()); + else if (!strcmp(fieldName, "destination")) *out = ghostForAirport(c, fp->destinationAirport()); + else if (!strcmp(fieldName, "departure_runway")) *out = ghostForRunway(c, fp->departureRunway()); + else if (!strcmp(fieldName, "destination_runway")) *out = ghostForRunway(c, fp->destinationRunway()); + else if (!strcmp(fieldName, "sid")) *out = ghostForProcedure(c, fp->sid()); + else if (!strcmp(fieldName, "sid_trans")) *out = ghostForProcedure(c, fp->sidTransition()); + else if (!strcmp(fieldName, "star")) *out = ghostForProcedure(c, fp->star()); + else if (!strcmp(fieldName, "star_trans")) *out = ghostForProcedure(c, fp->starTransition()); + else if (!strcmp(fieldName, "approach")) *out = ghostForProcedure(c, fp->approach()); + else if (!strcmp(fieldName, "current")) *out = naNum(fp->currentIndex()); + else { + return 0; + } + + return ""; +} + +static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef value) +{ + const char* fieldName = naStr_data(field); + FlightPlan* fp = (FlightPlan*) g; + + if (!strcmp(fieldName, "id")) { + if (!naIsString(value)) naRuntimeError(c, "flightplan.id must be a string"); + fp->setIdent(naStr_data(value)); + } else if (!strcmp(fieldName, "current")) { + int index = value.num; + if ((index < 0) || (index >= fp->numLegs())) { + return; + } + fp->setCurrentIndex(index); + } else if (!strcmp(fieldName, "departure")) { + FGAirport* apt = airportGhost(value); + if (apt) { + fp->setDeparture(apt); + return; + } + + FGRunway* rwy = runwayGhost(value); + if (rwy){ + fp->setDeparture(rwy); + return; + } + + naRuntimeError(c, "bad argument type setting departure"); + } else if (!strcmp(fieldName, "destination")) { + FGAirport* apt = airportGhost(value); + if (apt) { + fp->setDestination(apt); + return; + } + + FGRunway* rwy = runwayGhost(value); + if (rwy){ + fp->setDestination(rwy); + return; + } + + naRuntimeError(c, "bad argument type setting destination"); + } else if (!strcmp(fieldName, "departure_runway")) { + FGRunway* rwy = runwayGhost(value); + if (rwy){ + fp->setDeparture(rwy); + return; + } + + naRuntimeError(c, "bad argument type setting departure"); + } else if (!strcmp(fieldName, "destination_runway")) { + FGRunway* rwy = runwayGhost(value); + if (rwy){ + fp->setDestination(rwy); + return; + } + + naRuntimeError(c, "bad argument type setting departure"); + } else if (!strcmp(fieldName, "sid")) { + Procedure* proc = procedureGhost(value); + if (proc && (proc->type() == PROCEDURE_SID)) { + fp->setSID((SID*) proc); + return; + } + // allow a SID transition to be set, implicitly include the SID itself + if (proc && (proc->type() == PROCEDURE_TRANSITION)) { + fp->setSID((Transition*) proc); + return; + } + + if (naIsString(value)) { + FGAirport* apt = fp->departureAirport(); + fp->setSID(apt->findSIDWithIdent(naStr_data(value))); + return; + } + + naRuntimeError(c, "bad argument type setting SID"); + } else if (!strcmp(fieldName, "star")) { + Procedure* proc = procedureGhost(value); + if (proc && (proc->type() == PROCEDURE_STAR)) { + fp->setSTAR((STAR*) proc); + return; + } + + if (proc && (proc->type() == PROCEDURE_TRANSITION)) { + fp->setSTAR((Transition*) proc); + return; + } + + if (naIsString(value)) { + FGAirport* apt = fp->destinationAirport(); + fp->setSTAR(apt->findSTARWithIdent(naStr_data(value))); + return; + } + + naRuntimeError(c, "bad argument type setting STAR"); + } else if (!strcmp(fieldName, "approach")) { + Procedure* proc = procedureGhost(value); + if (proc && Approach::isApproach(proc->type())) { + fp->setApproach((Approach*) proc); + return; + } + + if (naIsString(value)) { + FGAirport* apt = fp->destinationAirport(); + fp->setApproach(apt->findApproachWithIdent(naStr_data(value))); + return; + } + + naRuntimeError(c, "bad argument type setting approach"); + } +} + + +static naRef procedureTpType(naContext c, ProcedureType ty) +{ + switch (ty) { + case PROCEDURE_SID: return stringToNasal(c, "sid"); + case PROCEDURE_STAR: return stringToNasal(c, "star"); + case PROCEDURE_APPROACH_VOR: + case PROCEDURE_APPROACH_ILS: + case PROCEDURE_APPROACH_RNAV: + case PROCEDURE_APPROACH_NDB: + return stringToNasal(c, "IAP"); + default: + return naNil(); + } +} + +static naRef procedureRadioType(naContext c, ProcedureType ty) +{ + switch (ty) { + case PROCEDURE_APPROACH_VOR: return stringToNasal(c, "VOR"); + case PROCEDURE_APPROACH_ILS: return stringToNasal(c, "ILS"); + case PROCEDURE_APPROACH_RNAV: return stringToNasal(c, "RNAV"); + case PROCEDURE_APPROACH_NDB: return stringToNasal(c, "NDB"); + default: + return naNil(); + } +} + +static const char* procedureGhostGetMember(naContext c, void* g, naRef field, naRef* out) +{ + const char* fieldName = naStr_data(field); + Procedure* proc = (Procedure*) g; + + if (!strcmp(fieldName, "parents")) { + *out = naNewVector(c); + naVec_append(*out, procedurePrototype); + } else if (!strcmp(fieldName, "id")) *out = stringToNasal(c, proc->ident()); + else if (!strcmp(fieldName, "airport")) *out = ghostForAirport(c, proc->airport()); + else if (!strcmp(fieldName, "tp_type")) *out = procedureTpType(c, proc->type()); + else if (!strcmp(fieldName, "radio")) *out = procedureRadioType(c, proc->type()); + else if (!strcmp(fieldName, "runways")) { + *out = naNewVector(c); + BOOST_FOREACH(FGRunwayPtr rwy, proc->runways()) { + naVec_append(*out, stringToNasal(c, rwy->ident())); + } + } else if (!strcmp(fieldName, "transitions")) { + if ((proc->type() != PROCEDURE_SID) && (proc->type() != PROCEDURE_STAR)) { + *out = naNil(); + return ""; + } + + ArrivalDeparture* ad = static_cast(proc); + *out = naNewVector(c); + BOOST_FOREACH(string id, ad->transitionIdents()) { + naVec_append(*out, stringToNasal(c, id)); + } + } else { + return 0; + } + + return ""; +} + static const char* runwayGhostGetMember(naContext c, void* g, naRef field, naRef* out) { const char* fieldName = naStr_data(field); @@ -664,6 +1007,7 @@ static naRef f_airport_runway(naContext c, naRef me, int argc, naRef* args) } std::string ident(naStr_data(args[0])); + boost::to_upper(ident); if (!apt->hasRunwayWithIdent(ident)) { return naNil(); } @@ -680,12 +1024,18 @@ static naRef f_airport_sids(naContext c, naRef me, int argc, naRef* args) naRef sids = naNewVector(c); + FGRunway* rwy = NULL; if (argc > 0 && naIsString(args[0])) { if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) { return naNil(); } - FGRunway* rwy = apt->getRunwayByIdent(naStr_data(args[0])); + rwy = apt->getRunwayByIdent(naStr_data(args[0])); + } else if (argc > 0) { + rwy = runwayGhost(args[0]); + } + + if (rwy) { BOOST_FOREACH(flightgear::SID* sid, rwy->getSIDs()) { naRef procId = stringToNasal(c, sid->ident()); naVec_append(sids, procId); @@ -710,12 +1060,18 @@ static naRef f_airport_stars(naContext c, naRef me, int argc, naRef* args) naRef stars = naNewVector(c); + FGRunway* rwy = NULL; if (argc > 0 && naIsString(args[0])) { if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) { return naNil(); } - FGRunway* rwy = apt->getRunwayByIdent(naStr_data(args[0])); + rwy = apt->getRunwayByIdent(naStr_data(args[0])); + } else if (argc > 0) { + rwy = runwayGhost(args[0]); + } + + if (rwy) { BOOST_FOREACH(flightgear::STAR* s, rwy->getSTARs()) { naRef procId = stringToNasal(c, s->ident()); naVec_append(stars, procId); @@ -731,6 +1087,61 @@ static naRef f_airport_stars(naContext c, naRef me, int argc, naRef* args) return stars; } +static naRef f_airport_approaches(naContext c, naRef me, int argc, naRef* args) +{ + FGAirport* apt = airportGhost(me); + if (!apt) { + naRuntimeError(c, "airport.getApproachList called on non-airport object"); + } + + naRef approaches = naNewVector(c); + + ProcedureType ty = PROCEDURE_INVALID; + if ((argc > 1) && naIsString(args[1])) { + std::string u(naStr_data(args[1])); + boost::to_upper(u); + if (u == "NDB") ty = PROCEDURE_APPROACH_NDB; + if (u == "VOR") ty = PROCEDURE_APPROACH_VOR; + if (u == "ILS") ty = PROCEDURE_APPROACH_ILS; + if (u == "RNAV") ty = PROCEDURE_APPROACH_RNAV; + } + + FGRunway* rwy = NULL; + if (argc > 0 && (rwy = runwayGhost(args[0]))) { + // ok + } else if (argc > 0 && naIsString(args[0])) { + if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) { + return naNil(); + } + + rwy = apt->getRunwayByIdent(naStr_data(args[0])); + } + + if (rwy) { + BOOST_FOREACH(Approach* s, rwy->getApproaches()) { + if ((ty != PROCEDURE_INVALID) && (s->type() != ty)) { + continue; + } + + naRef procId = stringToNasal(c, s->ident()); + naVec_append(approaches, procId); + } + } else { + // no runway specified, report them all + for (unsigned int s=0; snumApproaches(); ++s) { + Approach* app = apt->getApproachByIndex(s); + if ((ty != PROCEDURE_INVALID) && (app->type() != ty)) { + continue; + } + + naRef procId = stringToNasal(c, app->ident()); + naVec_append(approaches, procId); + } + } + + return approaches; +} + static naRef f_airport_parking(naContext c, naRef me, int argc, naRef* args) { FGAirport* apt = airportGhost(me); @@ -769,6 +1180,51 @@ static naRef f_airport_parking(naContext c, naRef me, int argc, naRef* args) return r; } +static naRef f_airport_getSid(naContext c, naRef me, int argc, naRef* args) +{ + FGAirport* apt = airportGhost(me); + if (!apt) { + naRuntimeError(c, "airport.getSid called on non-airport object"); + } + + if ((argc != 1) || !naIsString(args[0])) { + naRuntimeError(c, "airport.getSid passed invalid argument"); + } + + string ident = naStr_data(args[0]); + return ghostForProcedure(c, apt->findSIDWithIdent(ident)); +} + +static naRef f_airport_getStar(naContext c, naRef me, int argc, naRef* args) +{ + FGAirport* apt = airportGhost(me); + if (!apt) { + naRuntimeError(c, "airport.getStar called on non-airport object"); + } + + if ((argc != 1) || !naIsString(args[0])) { + naRuntimeError(c, "airport.getStar passed invalid argument"); + } + + string ident = naStr_data(args[0]); + return ghostForProcedure(c, apt->findSTARWithIdent(ident)); +} + +static naRef f_airport_getApproach(naContext c, naRef me, int argc, naRef* args) +{ + FGAirport* apt = airportGhost(me); + if (!apt) { + naRuntimeError(c, "airport.getIAP called on non-airport object"); + } + + if ((argc != 1) || !naIsString(args[0])) { + naRuntimeError(c, "airport.getIAP passed invalid argument"); + } + + string ident = naStr_data(args[0]); + return ghostForProcedure(c, apt->findApproachWithIdent(ident)); +} + // Returns vector of data hash for navaid of a , nil on error // navaids sorted by ascending distance // navinfo([,],[],[]) @@ -1030,62 +1486,233 @@ static naRef f_tileIndex(naContext c, naRef me, int argc, naRef* args) static naRef f_route(naContext c, naRef me, int argc, naRef* args) { - naRef route = naNewHash(c); + if (argc == 0) { + FGRouteMgr* rm = static_cast(globals->get_subsystem("route-manager")); + return ghostForFlightPlan(c, rm->flightPlan()); + } - // return active route hash by default, - // other routes in the future + if ((argc > 0) && naIsString(args[0])) { + flightgear::FlightPlan* fp = new flightgear::FlightPlan; + SGPath path(naStr_data(args[0])); + if (!path.exists()) { + naRuntimeError(c, "flightplan, no file at path %s", path.c_str()); + } + + if (!fp->load(path)) { + SG_LOG(SG_NASAL, SG_WARN, "failed to load flight-plan from " << path); + delete fp; + return naNil(); + } + + return ghostForFlightPlan(c, fp); + } - naRef parents = naNewVector(c); - naVec_append(parents, routePrototype); - hashset(c, route, "parents", parents); - - return route; + naRuntimeError(c, "bad arguments to flightplan()"); + return naNil(); } -static naRef f_route_getWP(naContext c, naRef me, int argc, naRef* args) +static naRef f_flightplan_getWP(naContext c, naRef me, int argc, naRef* args) { - FGRouteMgr* rm = static_cast(globals->get_subsystem("route-manager")); - + FlightPlan* fp = flightplanGhost(me); + if (!fp) { + naRuntimeError(c, "flightplan.getWP called on non-flightplan object"); + } + int index; if (argc == 0) { - index = rm->currentIndex(); + index = fp->currentIndex(); } else { index = (int) naNumValue(args[0]).num; } - if ((index < 0) || (index >= rm->numWaypts())) { + if ((index < 0) || (index >= fp->numLegs())) { return naNil(); } - return ghostForWaypt(c, rm->wayptAtIndex(index)); + return ghostForLeg(c, fp->legAtIndex(index)); } -static naRef f_route_currentWP(naContext c, naRef me, int argc, naRef* args) +static naRef f_flightplan_currentWP(naContext c, naRef me, int argc, naRef* args) { - FGRouteMgr* rm = static_cast(globals->get_subsystem("route-manager")); - return ghostForWaypt(c, rm->currentWaypt()); -} - -static naRef f_route_nextWP(naContext c, naRef me, int argc, naRef* args) -{ - FGRouteMgr* rm = static_cast(globals->get_subsystem("route-manager")); - flightgear::WayptRef wp = rm->nextWaypt(); - if (!wp) { - return naNil(); + FlightPlan* fp = flightplanGhost(me); + if (!fp) { + naRuntimeError(c, "flightplan.currentWP called on non-flightplan object"); } - return ghostForWaypt(c, wp); + return ghostForLeg(c, fp->currentLeg()); } -static naRef f_route_currentIndex(naContext c, naRef me, int argc, naRef* args) +static naRef f_flightplan_nextWP(naContext c, naRef me, int argc, naRef* args) { - FGRouteMgr* rm = static_cast(globals->get_subsystem("route-manager")); - return naNum(rm->currentIndex()); + FlightPlan* fp = flightplanGhost(me); + if (!fp) { + naRuntimeError(c, "flightplan.nextWP called on non-flightplan object"); + } + return ghostForLeg(c, fp->nextLeg()); } -static naRef f_route_numWaypoints(naContext c, naRef me, int argc, naRef* args) +static naRef f_flightplan_numWaypoints(naContext c, naRef me, int argc, naRef* args) { - FGRouteMgr* rm = static_cast(globals->get_subsystem("route-manager")); - return naNum(rm->numWaypts()); + FlightPlan* fp = flightplanGhost(me); + if (!fp) { + naRuntimeError(c, "flightplan.numWaypoints called on non-flightplan object"); + } + return naNum(fp->numLegs()); +} + +static naRef f_flightplan_appendWP(naContext c, naRef me, int argc, naRef* args) +{ + FlightPlan* fp = flightplanGhost(me); + if (!fp) { + naRuntimeError(c, "flightplan.appendWP called on non-flightplan object"); + } + + WayptRef wp = wayptGhost(args[0]); + int index = fp->numLegs(); + fp->insertWayptAtIndex(wp.get(), index); + return naNum(index); +} + +static naRef f_flightplan_insertWP(naContext c, naRef me, int argc, naRef* args) +{ + FlightPlan* fp = flightplanGhost(me); + if (!fp) { + naRuntimeError(c, "flightplan.insertWP called on non-flightplan object"); + } + + WayptRef wp = wayptGhost(args[0]); + int index = -1; // append + if ((argc > 1) && naIsNum(args[1])) { + index = (int) args[1].num; + } + + fp->insertWayptAtIndex(wp.get(), index); + return naNil(); +} + +static naRef f_flightplan_insertWPAfter(naContext c, naRef me, int argc, naRef* args) +{ + FlightPlan* fp = flightplanGhost(me); + if (!fp) { + naRuntimeError(c, "flightplan.insertWPAfter called on non-flightplan object"); + } + + WayptRef wp = wayptGhost(args[0]); + int index = -1; // append + if ((argc > 1) && naIsNum(args[1])) { + index = (int) args[1].num; + } + + fp->insertWayptAtIndex(wp.get(), index + 1); + return naNil(); +} + +static naRef f_flightplan_insertWaypoints(naContext c, naRef me, int argc, naRef* args) +{ + FlightPlan* fp = flightplanGhost(me); + if (!fp) { + naRuntimeError(c, "flightplan.insertWaypoints called on non-flightplan object"); + } + + WayptVec wps; + if (!naIsVector(args[0])) { + naRuntimeError(c, "flightplan.insertWaypoints expects vector as first arg"); + } + + int count = naVec_size(args[0]); + for (int i=0; i 1) && naIsNum(args[1])) { + index = (int) args[1].num; + } + + fp->insertWayptsAtIndex(wps, index); + return naNil(); +} + +static naRef f_flightplan_clearPlan(naContext c, naRef me, int argc, naRef* args) +{ + FlightPlan* fp = flightplanGhost(me); + if (!fp) { + naRuntimeError(c, "flightplan.clearPlan called on non-flightplan object"); + } + + fp->clear(); + return naNil(); +} + +static WayptFlag wayptFlagFromString(const char* s) +{ + if (!strcmp(s, "sid")) return WPT_DEPARTURE; + if (!strcmp(s, "star")) return WPT_ARRIVAL; + if (!strcmp(s, "approach")) return WPT_APPROACH; + if (!strcmp(s, "missed")) return WPT_MISS; + if (!strcmp(s, "pseudo")) return WPT_PSEUDO; + + return (WayptFlag) 0; +} + +static naRef f_flightplan_clearWPType(naContext c, naRef me, int argc, naRef* args) +{ + FlightPlan* fp = flightplanGhost(me); + if (!fp) { + naRuntimeError(c, "flightplan.clearWPType called on non-flightplan object"); + } + + if (argc < 1) { + naRuntimeError(c, "insufficent args to flightplan.clearWPType"); + } + + WayptFlag flag = wayptFlagFromString(naStr_data(args[0])); + fp->clearWayptsWithFlag(flag); + return naNil(); +} + +static naRef f_flightplan_clone(naContext c, naRef me, int argc, naRef* args) +{ + FlightPlan* fp = flightplanGhost(me); + if (!fp) { + naRuntimeError(c, "flightplan.clone called on non-flightplan object"); + } + + return ghostForFlightPlan(c, fp->clone()); +} + +static naRef f_leg_setSpeed(naContext c, naRef me, int argc, naRef* args) +{ + FlightPlan::Leg* leg = fpLegGhost(me); + if (!leg) { + naRuntimeError(c, "leg.setSpeed called on non-flightplan-leg object"); + } + + if (argc < 2) { + naRuntimeError(c, "bad arguments to leg.setSpeed"); + } + + RouteRestriction rr = routeRestrictionFromString(naStr_data(args[1])); + leg->setSpeed(rr, args[0].num); + return naNil(); +} + +static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args) +{ + FlightPlan::Leg* leg = fpLegGhost(me); + if (!leg) { + naRuntimeError(c, "leg.setAltitude called on non-flightplan-leg object"); + } + + if (argc < 2) { + naRuntimeError(c, "bad arguments to leg.setAltitude"); + } + + RouteRestriction rr = routeRestrictionFromString(naStr_data(args[1])); + leg->setAltitude(rr, args[0].num); + return naNil(); } static naRef f_waypoint_navaid(naContext c, naRef me, int argc, naRef* args) @@ -1147,6 +1774,23 @@ static naRef f_waypoint_runway(naContext c, naRef me, int argc, naRef* args) return ghostForRunway(c, (FGRunway*) pos); } +static naRef f_procedure_transition(naContext c, naRef me, int argc, naRef* args) +{ + Procedure* proc = procedureGhost(me); + if (!proc) { + naRuntimeError(c, "procedure.transition called on non-procedure object"); + } + + if ((proc->type() != PROCEDURE_SID) && (proc->type() != PROCEDURE_STAR)) { + naRuntimeError(c, "procedure.transition called on non-SID or -STAR"); + } + + ArrivalDeparture* ad = (ArrivalDeparture*) proc; + Transition* trans = ad->findTransitionByName(naStr_data(args[0])); + + return ghostForProcedure(c, trans); +} + // Table of extension functions. Terminate with zeros. static struct { const char* name; naCFunction func; } funcs[] = { { "carttogeod", f_carttogeod }, @@ -1160,7 +1804,7 @@ static struct { const char* name; naCFunction func; } funcs[] = { { "findNavaidByFrequency", f_findNavaidByFrequency }, { "findNavaidsByFrequency", f_findNavaidsByFrequency }, { "findNavaidsByID", f_findNavaidsByIdent }, - { "route", f_route }, + { "flightplan", f_route }, { "magvar", f_magvar }, { "courseAndDistance", f_courseAndDistance }, { "greatCircleMove", f_greatCircleMove }, @@ -1180,17 +1824,27 @@ naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave) hashset(c, airportPrototype, "comms", naNewFunc(c, naNewCCode(c, f_airport_comms))); hashset(c, airportPrototype, "sids", naNewFunc(c, naNewCCode(c, f_airport_sids))); hashset(c, airportPrototype, "stars", naNewFunc(c, naNewCCode(c, f_airport_stars))); + hashset(c, airportPrototype, "getApproachList", naNewFunc(c, naNewCCode(c, f_airport_approaches))); hashset(c, airportPrototype, "parking", naNewFunc(c, naNewCCode(c, f_airport_parking))); + hashset(c, airportPrototype, "getSid", naNewFunc(c, naNewCCode(c, f_airport_getSid))); + hashset(c, airportPrototype, "getStar", naNewFunc(c, naNewCCode(c, f_airport_getStar))); + hashset(c, airportPrototype, "getIAP", naNewFunc(c, naNewCCode(c, f_airport_getApproach))); - routePrototype = naNewHash(c); - hashset(c, gcSave, "routeProto", routePrototype); + flightplanPrototype = naNewHash(c); + hashset(c, gcSave, "flightplanProto", flightplanPrototype); - hashset(c, routePrototype, "getWP", naNewFunc(c, naNewCCode(c, f_route_getWP))); - hashset(c, routePrototype, "currentWP", naNewFunc(c, naNewCCode(c, f_route_currentWP))); - hashset(c, routePrototype, "nextWP", naNewFunc(c, naNewCCode(c, f_route_nextWP))); - hashset(c, routePrototype, "currentIndex", naNewFunc(c, naNewCCode(c, f_route_currentIndex))); - hashset(c, routePrototype, "getPlanSize", naNewFunc(c, naNewCCode(c, f_route_numWaypoints))); - + hashset(c, flightplanPrototype, "getWP", naNewFunc(c, naNewCCode(c, f_flightplan_getWP))); + hashset(c, flightplanPrototype, "currentWP", naNewFunc(c, naNewCCode(c, f_flightplan_currentWP))); + hashset(c, flightplanPrototype, "nextWP", naNewFunc(c, naNewCCode(c, f_flightplan_nextWP))); + hashset(c, flightplanPrototype, "getPlanSize", naNewFunc(c, naNewCCode(c, f_flightplan_numWaypoints))); + hashset(c, flightplanPrototype, "appendWP", naNewFunc(c, naNewCCode(c, f_flightplan_appendWP))); + hashset(c, flightplanPrototype, "insertWP", naNewFunc(c, naNewCCode(c, f_flightplan_insertWP))); + hashset(c, flightplanPrototype, "insertWPAfter", naNewFunc(c, naNewCCode(c, f_flightplan_insertWPAfter))); + hashset(c, flightplanPrototype, "insertWaypoints", naNewFunc(c, naNewCCode(c, f_flightplan_insertWaypoints))); + hashset(c, flightplanPrototype, "cleanPlan", naNewFunc(c, naNewCCode(c, f_flightplan_clearPlan))); + hashset(c, flightplanPrototype, "clearWPType", naNewFunc(c, naNewCCode(c, f_flightplan_clearWPType))); + hashset(c, flightplanPrototype, "clone", naNewFunc(c, naNewCCode(c, f_flightplan_clone))); + waypointPrototype = naNewHash(c); hashset(c, gcSave, "wayptProto", waypointPrototype); @@ -1198,6 +1852,17 @@ naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave) hashset(c, waypointPrototype, "runway", naNewFunc(c, naNewCCode(c, f_waypoint_runway))); hashset(c, waypointPrototype, "airport", naNewFunc(c, naNewCCode(c, f_waypoint_airport))); + procedurePrototype = naNewHash(c); + hashset(c, gcSave, "procedureProto", procedurePrototype); + // hashset(c, procedurePrototype, "runwayTransition", naNewFunc(c, naNewCCode(c, f_procedure_runwayTransition))); + hashset(c, procedurePrototype, "transition", naNewFunc(c, naNewCCode(c, f_procedure_transition))); + // hashset(c, procedurePrototype, "buildPath", naNewFunc(c, naNewCCode(c, f_procedure_build))); + + fpLegPrototype = naNewHash(c); + hashset(c, gcSave, "fpLegProto", fpLegPrototype); + hashset(c, fpLegPrototype, "setSpeed", naNewFunc(c, naNewCCode(c, f_leg_setSpeed))); + hashset(c, fpLegPrototype, "setAltitude", naNewFunc(c, naNewCCode(c, f_leg_setAltitude))); + for(int i=0; funcs[i].name; i++) { hashset(c, globals, funcs[i].name, naNewFunc(c, naNewCCode(c, funcs[i].func)));