X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FAutopilot%2Froute_mgr.cxx;h=6164c64740edccb7a932ebc1af3eeeb141d51bcd;hb=ff91fec1bb4b59dc2a7084de2a5ab0469abb1f3e;hp=967ca992a240e2f8d60550a2383f303f6d4f85a2;hpb=323fb790e7b1f0d72b5e524b5f59a6aaef69cebb;p=flightgear.git diff --git a/src/Autopilot/route_mgr.cxx b/src/Autopilot/route_mgr.cxx index 967ca992a..6164c6474 100644 --- a/src/Autopilot/route_mgr.cxx +++ b/src/Autopilot/route_mgr.cxx @@ -36,9 +36,11 @@ #include "route_mgr.hxx" #include +#include #include #include +#include #include #include @@ -48,66 +50,209 @@ #include "Main/fg_props.hxx" #include "Navaids/positioned.hxx" +#include +#include +#include #include "Airports/simple.hxx" #include "Airports/runways.hxx" -#include "FDM/flight.hxx" // for getting ground speed - #define RM "/autopilot/route-manager/" -static double get_ground_speed() { - // starts in ft/s so we convert to kts - static const SGPropertyNode * speedup_node = fgGetNode("/sim/speed-up"); +#include +#include + +using namespace flightgear; - double ft_s = cur_fdm_state->get_V_ground_speed() - * speedup_node->getIntValue(); - double kts = ft_s * SG_FEET_TO_METER * 3600 * SG_METER_TO_NM; - return kts; +static bool commandLoadFlightPlan(const SGPropertyNode* arg) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + SGPath path(arg->getStringValue("path")); + return self->loadRoute(path); } -FGRouteMgr::FGRouteMgr() : - _route( new SGRoute ), - input(fgGetNode( RM "input", true )), - mirror(fgGetNode( RM "route", true )) +static bool commandSaveFlightPlan(const SGPropertyNode* arg) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + SGPath path(arg->getStringValue("path")); + return self->saveRoute(path); +} + +static bool commandActivateFlightPlan(const SGPropertyNode* arg) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + bool activate = arg->getBoolValue("activate", true); + if (activate) { + self->activate(); + } else { + + } + + return true; +} + +static bool commandClearFlightPlan(const SGPropertyNode*) { - listener = new InputListener(this); - input->setStringValue(""); - input->addChangeListener(listener); + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + self->clearRoute(); + return true; } +static bool commandSetActiveWaypt(const SGPropertyNode* arg) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + int index = arg->getIntValue("index"); + if ((index < 0) || (index >= self->numLegs())) { + return false; + } + + self->jumpToIndex(index); + return true; +} -FGRouteMgr::~FGRouteMgr() { - input->removeChangeListener(listener); +static bool commandInsertWaypt(const SGPropertyNode* arg) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + int index = arg->getIntValue("index"); + std::string ident(arg->getStringValue("id")); + int alt = arg->getIntValue("altitude-ft", -999); + int ias = arg->getIntValue("speed-knots", -999); + + WayptRef wp; +// lat/lon may be supplied to narrow down navaid search, or to specify +// a raw waypoint + SGGeod pos; + if (arg->hasChild("longitude-deg")) { + pos = SGGeod::fromDeg(arg->getDoubleValue("longitude-deg"), + arg->getDoubleValue("latitude-deg")); + } + + if (arg->hasChild("navaid")) { + FGPositionedRef p = FGPositioned::findClosestWithIdent(arg->getStringValue("navaid"), pos); - delete listener; - delete _route; + if (arg->hasChild("navaid", 1)) { + // intersection of two radials + FGPositionedRef p2 = FGPositioned::findClosestWithIdent(arg->getStringValue("navaid[1]"), pos); + if (!p2) { + SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << arg->getStringValue("navaid[1]")); + return false; + } + + double r1 = arg->getDoubleValue("radial"), + r2 = arg->getDoubleValue("radial[1]"); + + 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:" << p->ident() + << "," << p2->ident()); + return false; + } + + std::string name = p->ident() + "-" + p2->ident(); + wp = new BasicWaypt(intersection, name, NULL); + } else if (arg->hasChild("offset-nm") && arg->hasChild("radial")) { + // offset radial from navaid + double radial = arg->getDoubleValue("radial"); + double distanceNm = arg->getDoubleValue("offset-nm"); + //radial += magvar->getDoubleValue(); // convert to true bearing + wp = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm); + } else { + wp = new NavaidWaypoint(p, NULL); + } + } else if (arg->hasChild("airport")) { + const FGAirport* apt = fgFindAirportID(arg->getStringValue("airport")); + if (!apt) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "no such airport" << arg->getStringValue("airport")); + return false; + } + + if (arg->hasChild("runway")) { + if (!apt->hasRunwayWithIdent(arg->getStringValue("runway"))) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "No runway: " << arg->getStringValue("runway") << " at " << apt->ident()); + return false; + } + + FGRunway* runway = apt->getRunwayByIdent(arg->getStringValue("runway")); + wp = new RunwayWaypt(runway, NULL); + } else { + wp = new NavaidWaypoint((FGAirport*) apt, NULL); + } + } else if (arg->hasChild("text")) { + wp = self->waypointFromString(arg->getStringValue("text")); + } else if (!(pos == SGGeod())) { + // just a raw lat/lon + wp = new BasicWaypt(pos, ident, NULL); + } else { + return false; // failed to build waypoint + } + + FlightPlan::Leg* leg = self->flightPlan()->insertWayptAtIndex(wp, index); + if (alt >= 0) { + leg->setAltitude(RESTRICT_AT, alt); + } + + if (ias > 0) { + leg->setSpeed(RESTRICT_AT, ias); + } + + return true; +} + +static bool commandDeleteWaypt(const SGPropertyNode* arg) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + int index = arg->getIntValue("index"); + self->removeLegAtIndex(index); + return true; +} + +///////////////////////////////////////////////////////////////////////////// + +FGRouteMgr::FGRouteMgr() : + _plan(NULL), + input(fgGetNode( RM "input", true )), + mirror(fgGetNode( RM "route", true )) +{ + listener = new InputListener(this); + input->setStringValue(""); + input->addChangeListener(listener); + + SGCommandMgr::instance()->addCommand("load-flightplan", commandLoadFlightPlan); + SGCommandMgr::instance()->addCommand("save-flightplan", commandSaveFlightPlan); + SGCommandMgr::instance()->addCommand("activate-flightplan", commandActivateFlightPlan); + SGCommandMgr::instance()->addCommand("clear-flightplan", commandClearFlightPlan); + SGCommandMgr::instance()->addCommand("set-active-waypt", commandSetActiveWaypt); + SGCommandMgr::instance()->addCommand("insert-waypt", commandInsertWaypt); + SGCommandMgr::instance()->addCommand("delete-waypt", commandDeleteWaypt); +} + + +FGRouteMgr::~FGRouteMgr() +{ + input->removeChangeListener(listener); + delete listener; } 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)); - -// init departure information from current location - SGGeod pos = SGGeod::fromDegFt(lon->getDoubleValue(), lat->getDoubleValue(), alt->getDoubleValue()); - _departure = FGAirport::findClosest(pos, 20.0); - if (_departure) { - FGRunway* active = _departure->getActiveRunwayForUsage(); - departure->setStringValue("runway", active->ident().c_str()); - } else { - departure->setStringValue("runway", ""); - } - + departure->tie("field-elevation-ft", SGRawValueMethods(*this, + &FGRouteMgr::getDestinationFieldElevation, NULL)); departure->getChild("etd", 0, true); departure->getChild("takeoff-time", 0, true); @@ -116,13 +261,24 @@ 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)); - - destination->getChild("runway", 0, true); + destination->tie("field-elevation-ft", SGRawValueMethods(*this, + &FGRouteMgr::getDestinationFieldElevation, NULL)); + destination->getChild("eta", 0, true); destination->getChild("touchdown-time", 0, true); - + alternate = fgGetNode(RM "alternate", true); alternate->getChild("airport", 0, true); alternate->getChild("runway", 0, true); @@ -134,8 +290,13 @@ void FGRouteMgr::init() { cruise->getChild("speed-kts", 0, true); cruise->setDoubleValue("speed-kts", 160.0); + _routingType = cruise->getChild("routing", 0, true); + _routingType->setIntValue(ROUTE_HIGH_AIRWAYS); + totalDistance = fgGetNode(RM "total-distance", true); totalDistance->setDoubleValue(0.0); + distanceToGo = fgGetNode(RM "distance-remaining-nm", true); + distanceToGo->setDoubleValue(0.0); ete = fgGetNode(RM "ete", true); ete->setDoubleValue(0.0); @@ -151,10 +312,11 @@ void FGRouteMgr::init() { _edited = fgGetNode(RM "signals/edited", true); _finished = fgGetNode(RM "signals/finished", true); + _flightplanChanged = fgGetNode(RM "signals/flightplan-changed", true); _currentWpt = fgGetNode(RM "current-wp", true); _currentWpt->tie(SGRawValueMethods - (*this, &FGRouteMgr::currentWaypoint, &FGRouteMgr::jumpToIndex)); + (*this, &FGRouteMgr::currentIndex, &FGRouteMgr::jumpToIndex)); // temporary distance / eta calculations, for backward-compatability wp0 = fgGetNode(RM "wp", 0, true); @@ -172,27 +334,41 @@ void FGRouteMgr::init() { wpn->getChild("dist", 0, true); wpn->getChild("eta", 0, true); - _route->clear(); - _route->set_current(0); - update_mirror(); - _pathNode = fgGetNode(RM "file-path", 0, true); + setFlightPlan(new FlightPlan()); } -void FGRouteMgr::postinit() { - string_list *waypoints = globals->get_initial_waypoints(); - if (waypoints) { - vector::iterator it; - for (it = waypoints->begin(); it != waypoints->end(); ++it) - new_waypoint(*it); +void FGRouteMgr::postinit() +{ + SGPath path(_pathNode->getStringValue()); + if (!path.isNull()) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "loading flight-plan from: " << path.str()); + loadRoute(path); + } + +// this code only matters for the --wp option now - perhaps the option +// should be deprecated in favour of an explicit flight-plan file? +// then the global initial waypoint list could die. + string_list *waypoints = globals->get_initial_waypoints(); + if (waypoints) { + string_list::iterator it; + for (it = waypoints->begin(); it != waypoints->end(); ++it) { + WayptRef w = waypointFromString(*it); + if (w) { + _plan->insertWayptAtIndex(w, -1); + } } - - weightOnWheels = fgGetNode("/gear/gear[0]/wow", false); - // check airbone flag agrees with presets -} + SG_LOG(SG_AUTOPILOT, SG_INFO, "loaded initial waypoints:" << numLegs()); + update_mirror(); + } + weightOnWheels = fgGetNode("/gear/gear[0]/wow", true); + groundSpeed = fgGetNode("/velocities/groundspeed-kt", true); + + // check airbone flag agrees with presets +} void FGRouteMgr::bind() { } void FGRouteMgr::unbind() { } @@ -202,260 +378,433 @@ bool FGRouteMgr::isRouteActive() const return active->getBoolValue(); } -void FGRouteMgr::update( double dt ) { - if (dt <= 0.0) { - // paused, nothing to do here - return; - } +bool FGRouteMgr::saveRoute(const SGPath& p) +{ + if (!_plan) { + return false; + } - if (!active->getBoolValue()) { - return; - } - - double groundSpeed = get_ground_speed(); - if (airborne->getBoolValue()) { - time_t now = time(NULL); - elapsedFlightTime->setDoubleValue(difftime(now, _takeoffTime)); - } else { // not airborne - if (weightOnWheels->getBoolValue() || (groundSpeed < 40)) { - return; - } - - airborne->setBoolValue(true); - _takeoffTime = time(NULL); // start the clock - departure->setIntValue("takeoff-time", _takeoffTime); - } - - // basic course/distance information - double wp_course, wp_distance; - SGWayPoint wp = _route->get_current(); - wp.CourseAndDistance( lon->getDoubleValue(), lat->getDoubleValue(), - alt->getDoubleValue(), &wp_course, &wp_distance ); - - // update wp0 / wp1 / wp-last for legacy users - wp0->setDoubleValue("dist", wp_distance * SG_METER_TO_NM); - wp_course -= magvar->getDoubleValue(); // expose magnetic bearing - wp0->setDoubleValue("bearing-deg", wp_course); - setETAPropertyFromDistance(wp0->getChild("eta"), wp_distance); - - if ((_route->current_index() + 1) < _route->size()) { - wp = _route->get_waypoint(_route->current_index() + 1); - double wp1_course, wp1_distance; - wp.CourseAndDistance(lon->getDoubleValue(), lat->getDoubleValue(), - alt->getDoubleValue(), &wp1_course, &wp1_distance); + 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); + + _flightplanChanged->fireValueChanged(); + +// fire all the callbacks! + departureChanged(); + arrivalChanged(); + waypointsChanged(); + currentWaypointChanged(); +} + +void FGRouteMgr::update( double dt ) +{ + if (dt <= 0.0) { + return; // paused, nothing to do here + } + + double gs = groundSpeed->getDoubleValue(); + if (airborne->getBoolValue()) { + time_t now = time(NULL); + elapsedFlightTime->setDoubleValue(difftime(now, _takeoffTime)); - wp1->setDoubleValue("dist", wp1_distance * SG_METER_TO_NM); - setETAPropertyFromDistance(wp1->getChild("eta"), wp1_distance); + if (weightOnWheels->getBoolValue()) { + // touch down + destination->setIntValue("touchdown-time", time(NULL)); + airborne->setBoolValue(false); } - - double totalDistanceRemaining = wp_distance; // distance to current waypoint - for (int i=_route->current_index() + 1; i<_route->size(); ++i) { - totalDistanceRemaining += _route->get_waypoint(i).get_distance(); + } else { // not airborne + if (weightOnWheels->getBoolValue() || (gs < 40)) { + return; } - wpn->setDoubleValue("dist", totalDistanceRemaining * SG_METER_TO_NM); - ete->setDoubleValue(totalDistanceRemaining * SG_METER_TO_NM / groundSpeed * 3600.0); - setETAPropertyFromDistance(wpn->getChild("eta"), totalDistanceRemaining); - - // get time now at destination tz as tm struct - // add ete seconds - // convert to string ... and stash in property - //destination->setDoubleValue("eta", eta); + airborne->setBoolValue(true); + _takeoffTime = time(NULL); // start the clock + departure->setIntValue("takeoff-time", _takeoffTime); + } + + if (!active->getBoolValue()) { + return; + } + + if (checkFinished()) { + // maybe we're done + } + +// basic course/distance information + SGGeod currentPos = globals->get_aircraft_position(); + + FlightPlan::Leg* leg = _plan ? _plan->currentLeg() : NULL; + if (!leg) { + return; + } + + double courseDeg; + double distanceM; + boost::tie(courseDeg, distanceM) = leg->waypoint()->courseAndDistanceFrom(currentPos); + +// update wp0 / wp1 / wp-last + wp0->setDoubleValue("dist", distanceM * SG_METER_TO_NM); + wp0->setDoubleValue("true-bearing-deg", courseDeg); + courseDeg -= magvar->getDoubleValue(); // expose magnetic bearing + wp0->setDoubleValue("bearing-deg", courseDeg); + setETAPropertyFromDistance(wp0->getChild("eta"), distanceM); + + 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 += (totalPathDistanceNm - leg->distanceAlongRoute()); + + wp0->setDoubleValue("distance-along-route-nm", + leg->distanceAlongRoute()); + wp0->setDoubleValue("remaining-distance-nm", + totalPathDistanceNm - leg->distanceAlongRoute()); + + 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); + wp1->setDoubleValue("distance-along-route-nm", + nextLeg->distanceAlongRoute()); + wp1->setDoubleValue("remaining-distance-nm", + totalPathDistanceNm - nextLeg->distanceAlongRoute()); + } + + distanceToGo->setDoubleValue(totalDistanceRemaining); + wpn->setDoubleValue("dist", totalDistanceRemaining); + ete->setDoubleValue(totalDistanceRemaining / gs * 3600.0); + setETAPropertyFromDistance(wpn->getChild("eta"), totalDistanceRemaining); } +void FGRouteMgr::clearRoute() +{ + if (_plan) { + _plan->clear(); + } +} -void FGRouteMgr::setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDistance) { - double speed = get_ground_speed(); - if (speed < 1.0) { - aProp->setStringValue("--:--"); - return; - } +Waypt* FGRouteMgr::currentWaypt() const +{ + if (_plan && _plan->currentLeg()) { + return _plan->currentLeg()->waypoint(); + } - char eta_str[64]; - double eta = aDistance * SG_METER_TO_NM / get_ground_speed(); - if ( eta >= 100.0 ) { - eta = 99.999; // clamp - } - - if ( eta < (1.0/6.0) ) { - eta *= 60.0; // within 10 minutes, bump up to min/secs + 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(); } - - int major = (int)eta, - minor = (int)((eta - (int)eta) * 60.0); - snprintf( eta_str, 64, "%d:%02d", major, minor ); - aProp->setStringValue( eta_str ); + } + + return NULL; } -void FGRouteMgr::add_waypoint( const SGWayPoint& wp, int n ) +int FGRouteMgr::numLegs() const { - _route->add_waypoint( wp, n ); - - if ((n >= 0) && (_route->current_index() > n)) { - _route->set_current(_route->current_index() + 1); + if (_plan) { + return _plan->numLegs(); } - waypointsChanged(); + return 0; } -void FGRouteMgr::waypointsChanged() +void FGRouteMgr::setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDistance) { - 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)); + double speed = fgGetDouble("/velocities/groundspeed-kt", 0.0); + if (speed < 1.0) { + aProp->setStringValue("--:--"); + return; } - update_mirror(); - _edited->fireValueChanged(); - checkFinished(); + char eta_str[64]; + double eta = aDistance * SG_METER_TO_NM / speed; + if ( eta >= 100.0 ) { + eta = 99.999; // clamp + } + + if ( eta < (1.0/6.0) ) { + eta *= 60.0; // within 10 minutes, bump up to min/secs + } + + int major = (int)eta, + minor = (int)((eta - (int)eta) * 60.0); + snprintf( eta_str, 64, "%d:%02d", major, minor ); + aProp->setStringValue( eta_str ); } -SGWayPoint FGRouteMgr::pop_waypoint( int n ) { - if ( _route->size() <= 0 ) { - return SGWayPoint(); +void FGRouteMgr::removeLegAtIndex(int aIndex) +{ + if (!_plan) { + return; } - if ( n < 0 ) { - n = _route->size() - 1; + _plan->deleteIndex(aIndex); +} + +/** + * route between index-1 and index, using airways. + */ +bool FGRouteMgr::routeToIndex(int index, RouteType aRouteType) +{ + WayptRef wp1; + WayptRef wp2; + + if (index == -1) { + index = numLegs(); } - if (_route->current_index() > n) { - _route->set_current(_route->current_index() - 1); + if (index == 0) { + if (!_plan->departureAirport()) { + SG_LOG(SG_AUTOPILOT, SG_WARN, "routeToIndex: no departure set"); + return false; + } + + wp1 = new NavaidWaypoint(_plan->departureAirport().get(), NULL); + } else { + wp1 = wayptAtIndex(index - 1); } - - SGWayPoint wp = _route->get_waypoint(n); - _route->delete_waypoint(n); + + if (index >= numLegs()) { + if (!_plan->destinationAirport()) { + SG_LOG(SG_AUTOPILOT, SG_WARN, "routeToIndex: no destination set"); + return false; + } - waypointsChanged(); - return wp; + wp2 = new NavaidWaypoint(_plan->destinationAirport().get(), NULL); + } else { + wp2 = wayptAtIndex(index); + } + + double distNm = SGGeodesy::distanceNm(wp1->position(), wp2->position()); + if (distNm < 100.0) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "routeToIndex: existing waypoints are nearby, direct route"); + return true; + } + + WayptVec r; + switch (aRouteType) { + case ROUTE_HIGH_AIRWAYS: + Airway::highLevel()->route(wp1, wp2, r); + break; + + case ROUTE_LOW_AIRWAYS: + Airway::lowLevel()->route(wp1, wp2, r); + break; + + case ROUTE_VOR: + throw sg_exception("VOR routing not supported yet"); + } + + if (r.empty()) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "routeToIndex: no route found"); + return false; + } + + _plan->insertWayptsAtIndex(r, index); + return true; } +void FGRouteMgr::departureChanged() +{ + _plan->clearWayptsWithFlag(WPT_DEPARTURE); + WayptRef enroute; + WayptVec wps; + buildDeparture(enroute, wps); + _plan->insertWayptsAtIndex(wps, 0); +} -bool FGRouteMgr::build() { - return true; +void FGRouteMgr::buildDeparture(WayptRef enroute, WayptVec& wps) +{ + if (!_plan->departureAirport()) { + return; + } + + 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; + } + + 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() +{ + _plan->clearWayptsWithFlag(WPT_ARRIVAL); + _plan->clearWayptsWithFlag(WPT_APPROACH); + WayptVec wps; + WayptRef enroute; + buildArrival(enroute, wps); + _plan->insertWayptsAtIndex(wps, -1); +} -void FGRouteMgr::new_waypoint( const string& target, int n ) { - SGWayPoint* wp = make_waypoint( target ); - if (!wp) { - return; - } - - add_waypoint( *wp, n ); - delete wp; +void FGRouteMgr::buildArrival(WayptRef enroute, WayptVec& wps) +{ + FGAirportRef apt = _plan->destinationAirport(); + if (!apt.valid()) { + return; + } + + if (!_plan->destinationRunway()) { + WayptRef w = new NavaidWaypoint(apt.ptr(), _plan); + w->setFlag(WPT_ARRIVAL); + wps.push_back(w); + return; + } + + if (_plan->star()) { + _plan->star()->route(_plan->destinationRunway(), _plan->starTransition(), wps); + } + + 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); + } } +void FGRouteMgr::waypointsChanged() +{ + update_mirror(); + _edited->fireValueChanged(); + checkFinished(); +} -SGWayPoint* FGRouteMgr::make_waypoint(const string& tgt ) { - string target(boost::to_upper_copy(tgt)); +// 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 = _plan->numLegs(); + for (int i = 0; i < num; i++) { + FlightPlan::Leg* leg = _plan->legAtIndex(i); + WayptRef wp = leg->waypoint(); + SGPropertyNode *prop = mirror->getChild("wp", i, 1); + + const SGGeod& pos(wp->position()); + prop->setStringValue("id", wp->ident().c_str()); + prop->setDoubleValue("longitude-deg", pos.getLongitudeDeg()); + prop->setDoubleValue("latitude-deg",pos.getLatitudeDeg()); + + // leg course+distance + + prop->setDoubleValue("leg-bearing-true-deg", leg->courseDeg()); + prop->setDoubleValue("leg-distance-nm", leg->distanceNm()); + prop->setDoubleValue("distance-along-route-nm", leg->distanceAlongRoute()); - double alt = -9999.0; - // extract altitude - size_t pos = target.find( '@' ); - if ( pos != string::npos ) { - alt = atof( target.c_str() + pos + 1 ); - target = target.substr( 0, pos ); - if ( !strcmp(fgGetString("/sim/startup/units"), "feet") ) - alt *= SG_FEET_TO_METER; - } - - // 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)); - return new SGWayPoint( lon, lat, alt, SGWayPoint::WGS84, buf); - } - - SGGeod basePosition; - if (_route->size() > 0) { - SGWayPoint wp = get_waypoint(_route->size()-1); - basePosition = wp.get_target(); + 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); } else { - // route is empty, use current position - basePosition = SGGeod::fromDeg( - fgGetNode("/position/longitude-deg")->getDoubleValue(), - fgGetNode("/position/latitude-deg")->getDoubleValue()); + prop->setDoubleValue("altitude-m", -9999.9); + prop->setDoubleValue("altitude-ft", -9999.9); } - vector 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 (leg->speedRestriction() == SPEED_RESTRICT_MACH) { + prop->setDoubleValue("speed-mach", leg->speedMach()); + } else if (leg->speedRestriction() != RESTRICT_NONE) { + prop->setDoubleValue("speed-kts", leg->speedKts()); } - SGGeod geod = SGGeod::fromGeodM(p->geod(), alt); - if (pieces.size() == 1) { - // simple case - return new SGWayPoint(geod, target, p->name()); - } - - if (pieces.size() == 3) { - // navaid/radial/distance-nm notation - double radial = atof(pieces[1].c_str()), - distanceNm = atof(pieces[2].c_str()), - az2; - radial += magvar->getDoubleValue(); // convert to true bearing - SGGeod offsetPos; - SGGeodesy::direct(geod, radial, distanceNm * SG_NM_TO_METER, offsetPos, az2); - offsetPos.setElevationM(alt); - - SG_LOG(SG_AUTOPILOT, SG_INFO, "final offset radial is " << radial); - return new SGWayPoint(offsetPos, p->ident() + pieces[2], target); + if (wp->flag(WPT_ARRIVAL)) { + prop->setBoolValue("arrival", true); } - 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]); - SGGeod t = runway->threshold(); - return new SGWayPoint(t.getLongitudeDeg(), t.getLatitudeDeg(), alt, SGWayPoint::WGS84, pieces[1]); + if (wp->flag(WPT_DEPARTURE)) { + prop->setBoolValue("departure", true); } - SG_LOG(SG_AUTOPILOT, SG_INFO, "Unable to parse waypoint:" << target); - return NULL; -} - - -// mirror internal route to the property system for inspection by other subsystems -void FGRouteMgr::update_mirror() { - mirror->removeChildren("wp"); - for (int i = 0; i < _route->size(); i++) { - SGWayPoint wp = _route->get_waypoint(i); - SGPropertyNode *prop = mirror->getChild("wp", i, 1); - - const SGGeod& pos(wp.get_target()); - prop->setStringValue("id", wp.get_id().c_str()); - prop->setStringValue("name", wp.get_name().c_str()); - prop->setDoubleValue("longitude-deg", pos.getLongitudeDeg()); - prop->setDoubleValue("latitude-deg",pos.getLatitudeDeg()); - prop->setDoubleValue("altitude-m", pos.getElevationM()); - prop->setDoubleValue("altitude-ft", pos.getElevationFt()); + if (wp->flag(WPT_MISS)) { + prop->setBoolValue("missed-approach", true); } - // set number as listener attachment point - mirror->setIntValue("num", _route->size()); + + prop->setBoolValue("generated", wp->flag(WPT_GENERATED)); + } // of waypoint iteration + + // set number as listener attachment point + mirror->setIntValue("num", _plan->numLegs()); + + if (rmDlg) { + rmDlg->updateValues(); + } + + totalDistance->setDoubleValue(_plan->totalDistanceNm()); } // command interface /autopilot/route-manager/input: @@ -474,23 +823,23 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop) } if (!strcmp(s, "@CLEAR")) - mgr->init(); + mgr->clearRoute(); else if (!strcmp(s, "@ACTIVATE")) mgr->activate(); else if (!strcmp(s, "@LOAD")) { - mgr->loadRoute(); + SGPath path(mgr->_pathNode->getStringValue()); + mgr->loadRoute(path); } else if (!strcmp(s, "@SAVE")) { - mgr->saveRoute(); - } else if (!strcmp(s, "@POP")) { - SG_LOG(SG_AUTOPILOT, SG_WARN, "route-manager @POP command is deprecated"); + SGPath path(mgr->_pathNode->getStringValue()); + mgr->saveRoute(path); } else if (!strcmp(s, "@NEXT")) { - mgr->jumpToIndex(mgr->currentWaypoint() + 1); + mgr->jumpToIndex(mgr->currentIndex() + 1); } else if (!strcmp(s, "@PREVIOUS")) { - mgr->jumpToIndex(mgr->currentWaypoint() - 1); + mgr->jumpToIndex(mgr->currentIndex() - 1); } else if (!strncmp(s, "@JUMP", 5)) { mgr->jumpToIndex(atoi(s + 5)); } else if (!strncmp(s, "@DELETE", 7)) - mgr->pop_waypoint(atoi(s + 7)); + mgr->removeLegAtIndex(atoi(s + 7)); else if (!strncmp(s, "@INSERT", 7)) { char *r; int pos = strtol(s + 7, &r, 10); @@ -499,56 +848,82 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop) while (isspace(*r)) r++; if (*r) - mgr->new_waypoint(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, "@POSINIT")) { + mgr->initAtPosition(); } else - mgr->new_waypoint(s); + mgr->flightPlan()->insertWayptAtIndex(mgr->waypointFromString(s), -1); } -// SGWayPoint( const double lon = 0.0, const double lat = 0.0, -// const double alt = 0.0, const modetype m = WGS84, -// const string& s = "", const string& n = "" ); +void FGRouteMgr::initAtPosition() +{ + if (isRouteActive()) { + return; // don't mess with the active route + } + + if (haveUserWaypoints()) { + // user has already defined, loaded or entered a route, again + // don't interfere with it + return; + } + + if (airborne->getBoolValue()) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "initAtPosition: airborne, clearing departure info"); + _plan->setDeparture((FGAirport*) NULL); + return; + } + +// on the ground + 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"); + return; + } + } + + std::string rwy = departure->getStringValue("runway"); + FGRunway* r = NULL; + if (!rwy.empty()) { + r = _plan->departureAirport()->getRunwayByIdent(rwy); + } else { + r = _plan->departureAirport()->findBestRunwayForPos(pos); + } + + if (!r) { + return; + } + + _plan->setDeparture(r); + SG_LOG(SG_AUTOPILOT, SG_INFO, "initAtPosition: starting at " + << _plan->departureAirport()->ident() << " on runway " << r->ident()); +} + +bool FGRouteMgr::haveUserWaypoints() const +{ + // 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; } - - // only add departure waypoint if we're not airborne, so that - // in-air route activation doesn't confuse matters. - if (weightOnWheels->getBoolValue() && _departure) { - string runwayId(departure->getStringValue("runway")); - FGRunway* runway = NULL; - if (_departure->hasRunwayWithIdent(runwayId)) { - runway = _departure->getRunwayByIdent(runwayId); - } else { - SG_LOG(SG_AUTOPILOT, SG_INFO, - "route-manager, departure runway not found:" << runwayId); - runway = _departure->getActiveRunwayForUsage(); - } - - SGWayPoint swp(runway->threshold(), - _departure->ident() + "-" + runway->ident(), runway->name()); - add_waypoint(swp, 0); - } - - if (_destination) { - string runwayId = (destination->getStringValue("runway")); - if (_destination->hasRunwayWithIdent(runwayId)) { - FGRunway* runway = _destination->getRunwayByIdent(runwayId); - SGWayPoint swp(runway->end(), - _destination->ident() + "-" + runway->ident(), runway->name()); - add_waypoint(swp); - } else { - // quite likely, since destination runway may not be known until enroute - // probably want a listener on the 'destination' node to allow an enroute - // update - add_waypoint(SGWayPoint(_destination->geod(), _destination->ident(), _destination->name())); - } - } - - _route->set_current(0); + + _plan->setCurrentIndex(0); active->setBoolValue(true); SG_LOG(SG_AUTOPILOT, SG_INFO, "route-manager, activate route ok"); return true; @@ -557,7 +932,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; } @@ -566,336 +941,260 @@ void FGRouteMgr::sequence() return; } - _route->increment_current(); - currentWaypointChanged(); - _currentWpt->fireValueChanged(); + _plan->setCurrentIndex(_plan->currentIndex() + 1); } bool FGRouteMgr::checkFinished() { - int lastWayptIndex = _route->size() - 1; - if (_route->current_index() < lastWayptIndex) { - return false; + if (!_plan) { + return true; } - SG_LOG(SG_AUTOPILOT, SG_INFO, "reached end of active route"); - _finished->fireValueChanged(); - active->setBoolValue(false); - return true; + bool done = false; +// done if we're stopped on the destination runway + if (_plan->currentLeg() && + (_plan->currentLeg()->waypoint()->source() == _plan->destinationRunway())) + { + double gs = groundSpeed->getDoubleValue(); + done = weightOnWheels->getBoolValue() && (gs < 25); + } + +// also done if we hit the final waypoint + if (_plan->currentIndex() >= _plan->numLegs()) { + done = true; + } + + if (done) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "reached end of active route"); + _finished->fireValueChanged(); + active->setBoolValue(false); + } + + return done; } void FGRouteMgr::jumpToIndex(int index) { - if ((index < 0) || (index >= _route->size())) { - SG_LOG(SG_AUTOPILOT, SG_ALERT, "passed invalid index (" << - index << ") to FGRouteMgr::jumpToIndex"); + if (!_plan) { return; } - - if (_route->current_index() == index) { - return; // no-op - } - _route->set_current(index); - currentWaypointChanged(); - _currentWpt->fireValueChanged(); + _plan->setCurrentIndex(index); } void FGRouteMgr::currentWaypointChanged() { - SGWayPoint previous = _route->get_previous(); - SGWayPoint cur = _route->get_current(); + Waypt* cur = currentWaypt(); + FlightPlan::Leg* next = _plan ? _plan->nextLeg() : NULL; + + wp0->getChild("id")->setStringValue(cur ? cur->ident() : ""); + wp1->getChild("id")->setStringValue(next ? next->waypoint()->ident() : ""); - wp0->getChild("id")->setStringValue(cur.get_id()); - if ((_route->current_index() + 1) < _route->size()) { - wp1->getChild("id")->setStringValue(_route->get_next().get_id()); - } else { - wp1->getChild("id")->setStringValue(""); + _currentWpt->fireValueChanged(); + SG_LOG(SG_AUTOPILOT, SG_INFO, "route manager, current-wp is now " << currentIndex()); +} + +const char* FGRouteMgr::getDepartureICAO() const +{ + if (!_plan || !_plan->departureAirport()) { + return ""; } - SG_LOG(SG_AUTOPILOT, SG_INFO, "route manager, current-wp is now " << _route->current_index()); + return _plan->departureAirport()->ident().c_str(); } -int FGRouteMgr::findWaypoint(const SGGeod& aPos) const -{ - for (int i=0; i<_route->size(); ++i) { - double d = SGGeodesy::distanceM(aPos, _route->get_waypoint(i).get_target()); - if (d < 200.0) { // 200 metres seems close enough - return i; - } +const char* FGRouteMgr::getDepartureName() const +{ + if (!_plan || !_plan->departureAirport()) { + return ""; } - return -1; + return _plan->departureAirport()->name().c_str(); } -SGWayPoint FGRouteMgr::get_waypoint( int i ) const +const char* FGRouteMgr::getDepartureRunway() const { - return _route->get_waypoint(i); + if (_plan && _plan->departureRunway()) { + return _plan->departureRunway()->ident().c_str(); + } + + return ""; } -int FGRouteMgr::size() const +void FGRouteMgr::setDepartureRunway(const char* aIdent) { - return _route->size(); + FGAirport* apt = _plan->departureAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setDeparture(apt); + } else if (apt->hasRunwayWithIdent(aIdent)) { + _plan->setDeparture(apt->getRunwayByIdent(aIdent)); + } } -int FGRouteMgr::currentWaypoint() const +void FGRouteMgr::setDepartureICAO(const char* aIdent) { - return _route->current_index(); + if ((aIdent == NULL) || (strlen(aIdent) < 4)) { + _plan->setDeparture((FGAirport*) NULL); + } else { + _plan->setDeparture(FGAirport::findByIdent(aIdent)); + } } -void FGRouteMgr::setWaypointTargetAltitudeFt(unsigned int index, int altFt) +const char* FGRouteMgr::getSID() const { - SGWayPoint wp = _route->get_waypoint(index); - wp.setTargetAltFt(altFt); - // simplest way to update a waypoint is to remove and re-add it - _route->delete_waypoint(index); - _route->add_waypoint(wp, index); - waypointsChanged(); + if (_plan && _plan->sid()) { + return _plan->sid()->ident().c_str(); + } + + return ""; } -void FGRouteMgr::saveRoute() +void FGRouteMgr::setSID(const char* aIdent) { - SGPath path(_pathNode->getStringValue()); - SG_LOG(SG_IO, SG_INFO, "Saving route to " << path.str()); - try { - SGPropertyNode_ptr d(new SGPropertyNode); - SGPath path(_pathNode->getStringValue()); - d->setIntValue("version", 1); - - 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")); - } + FGAirport* apt = _plan->departureAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setSID((flightgear::SID*) NULL); + return; + } - // route nodes - SGPropertyNode* routeNode = d->getChild("route", 0, true); - for (int i=0; i<_route->size(); ++i) { - SGPropertyNode* wpNode = routeNode->getChild("wp",i, true); - SGWayPoint wp(_route->get_waypoint(i)); - - wpNode->setStringValue("ident", wp.get_id()); - wpNode->setStringValue("name", wp.get_name()); - SGGeod geod(wp.get_target()); - - wpNode->setDoubleValue("longitude-deg", geod.getLongitudeDeg()); - wpNode->setDoubleValue("latitude-deg", geod.getLatitudeDeg()); - - if (geod.getElevationFt() > -9990.0) { - wpNode->setDoubleValue("altitude-ft", geod.getElevationFt()); - } - } // of waypoint iteration + string ident(aIdent); + size_t hyphenPos = ident.find('-'); + if (hyphenPos != string::npos) { + string sidIdent = ident.substr(0, hyphenPos); + string transIdent = ident.substr(hyphenPos + 1); - writeProperties(path.str(), d, true /* write-all */); - } catch (const sg_exception &e) { - SG_LOG(SG_IO, SG_WARN, "Error saving route:" << e.getMessage()); + flightgear::SID* sid = apt->findSIDWithIdent(sidIdent); + Transition* trans = sid ? sid->findTransitionByName(transIdent) : NULL; + _plan->setSID(trans); + } else { + _plan->setSID(apt->findSIDWithIdent(aIdent)); } } -void FGRouteMgr::loadRoute() +const char* FGRouteMgr::getDestinationICAO() const { - // deactivate route first - active->setBoolValue(false); - - SGPropertyNode_ptr routeData(new SGPropertyNode); - SGPath path(_pathNode->getStringValue()); - - SG_LOG(SG_IO, SG_INFO, "going to read flight-plan from:" << path.str()); - - try { - readProperties(path.str(), routeData); - } catch (sg_exception& e) { - // if XML parsing fails, the file might be simple textual list of waypoints - loadPlainTextRoute(path); - return; + if (!_plan || !_plan->destinationAirport()) { + return ""; } - try { - // departure nodes - SGPropertyNode* dep = routeData->getChild("departure"); - if (!dep) { - throw sg_io_exception("malformed route file, no departure node"); - } - - string depIdent = dep->getStringValue("airport"); - _departure = (FGAirport*) fgFindAirportID(depIdent); - - - // destination - SGPropertyNode* dst = routeData->getChild("destination"); - if (!dst) { - throw sg_io_exception("malformed route file, no destination node"); - } - - _destination = (FGAirport*) fgFindAirportID(dst->getStringValue("airport")); - destination->setStringValue("runway", dst->getStringValue("runway")); - - // 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 - - // route nodes - _route->clear(); - SGPropertyNode_ptr _route = routeData->getChild("route", 0); - SGGeod lastPos = (_departure ? _departure->geod() : SGGeod()); - - for (int i=0; i<_route->nChildren(); ++i) { - SGPropertyNode_ptr wp = _route->getChild("wp", i); - parseRouteWaypoint(wp); - } // of route iteration - } catch (sg_exception& e) { - SG_LOG(SG_IO, SG_WARN, "failed to load flight-plan (from '" << e.getOrigin() - << "'):" << e.getMessage()); + return _plan->destinationAirport()->ident().c_str(); +} + +const char* FGRouteMgr::getDestinationName() const +{ + if (!_plan || !_plan->destinationAirport()) { + return ""; } + + return _plan->destinationAirport()->name().c_str(); } -void FGRouteMgr::parseRouteWaypoint(SGPropertyNode* aWP) +void FGRouteMgr::setDestinationICAO(const char* aIdent) { - SGGeod lastPos; - if (_route->size() > 0) { - lastPos = get_waypoint(_route->size()-1).get_target(); + if ((aIdent == NULL) || (strlen(aIdent) < 4)) { + _plan->setDestination((FGAirport*) NULL); } else { - // route is empty, use departure airport position - const FGAirport* apt = fgFindAirportID(departure->getStringValue("airport")); - assert(apt); // shouldn't have got this far with an invalid airport - lastPos = apt->geod(); + _plan->setDestination(FGAirport::findByIdent(aIdent)); } +} - SGPropertyNode_ptr altProp = aWP->getChild("altitude-ft"); - double altM = -9999.0; - if (altProp) { - altM = altProp->getDoubleValue() * SG_FEET_TO_METER; +const char* FGRouteMgr::getDestinationRunway() const +{ + if (_plan && _plan->destinationRunway()) { + return _plan->destinationRunway()->ident().c_str(); } - - string ident(aWP->getStringValue("ident")); - if (aWP->hasChild("longitude-deg")) { - // explicit longitude/latitude - SGWayPoint swp(aWP->getDoubleValue("longitude-deg"), - aWP->getDoubleValue("latitude-deg"), altM, - SGWayPoint::WGS84, ident, aWP->getStringValue("name")); - add_waypoint(swp); - } else if (aWP->hasChild("navid")) { - // lookup by navid (possibly with offset) - string nid(aWP->getStringValue("navid")); - 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); - } - - SGWayPoint swp(pos.getLongitudeDeg(), pos.getLatitudeDeg(), altM, - SGWayPoint::WGS84, ident, ""); - add_waypoint(swp); - } else { - // lookup by ident (symbolic waypoint) - FGPositionedRef p = FGPositioned::findClosestWithIdent(ident, lastPos); - if (!p) { - throw sg_io_exception("bad route file, unknown waypoint:" + ident); - } - - SGWayPoint swp(p->longitude(), p->latitude(), altM, - SGWayPoint::WGS84, p->ident(), p->name()); - add_waypoint(swp); + + return ""; +} + +void FGRouteMgr::setDestinationRunway(const char* aIdent) +{ + FGAirport* apt = _plan->destinationAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setDestination(apt); + } else if (apt->hasRunwayWithIdent(aIdent)) { + _plan->setDestination(apt->getRunwayByIdent(aIdent)); } } -void FGRouteMgr::loadPlainTextRoute(const SGPath& path) +const char* FGRouteMgr::getApproach() const { - sg_gzifstream in(path.str().c_str()); - if (!in.is_open()) { - return; + if (_plan && _plan->approach()) { + return _plan->approach()->ident().c_str(); } - _route->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); - } - - new_waypoint(line, -1); - } // of line iteration + return ""; } -const char* FGRouteMgr::getDepartureICAO() const +void FGRouteMgr::setApproach(const char* aIdent) { - if (!_departure) { - return ""; + FGAirport* apt = _plan->destinationAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setApproach(NULL); + } else { + _plan->setApproach(apt->findApproachWithIdent(aIdent)); } - - return _departure->ident().c_str(); } -const char* FGRouteMgr::getDepartureName() const +const char* FGRouteMgr::getSTAR() const { - if (!_departure) { - return ""; + if (_plan && _plan->star()) { + return _plan->star()->ident().c_str(); } - return _departure->name().c_str(); + return ""; } -void FGRouteMgr::setDepartureICAO(const char* aIdent) +void FGRouteMgr::setSTAR(const char* aIdent) { - if ((aIdent == NULL) || (strlen(aIdent) < 4)) { - _departure = NULL; + 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 { - _departure = FGAirport::findByIdent(aIdent); + _plan->setSTAR(apt->findSTARWithIdent(aIdent)); } } -const char* FGRouteMgr::getDestinationICAO() const +WayptRef FGRouteMgr::waypointFromString(const std::string& target) { - if (!_destination) { - return ""; + return _plan->waypointFromString(target); +} + +double FGRouteMgr::getDepartureFieldElevation() const +{ + if (!_plan || !_plan->departureAirport()) { + return 0.0; } - return _destination->ident().c_str(); + return _plan->departureAirport()->elevation(); } -const char* FGRouteMgr::getDestinationName() const +double FGRouteMgr::getDestinationFieldElevation() const { - if (!_destination) { - return ""; + if (!_plan || !_plan->destinationAirport()) { + return 0.0; } - return _destination->name().c_str(); + return _plan->destinationAirport()->elevation(); } -void FGRouteMgr::setDestinationICAO(const char* aIdent) +SGPropertyNode_ptr FGRouteMgr::wayptNodeAtIndex(int index) const { - if ((aIdent == NULL) || (strlen(aIdent) < 4)) { - _destination = NULL; - } else { - _destination = FGAirport::findByIdent(aIdent); + if ((index < 0) || (index >= numWaypts())) { + throw sg_range_exception("waypt index out of range", "FGRouteMgr::wayptAtIndex"); } + + return mirror->getChild("wp", index); } -