#include <boost/algorithm/string/case_conv.hpp>
#include <boost/tuple/tuple.hpp>
+#include <boost/foreach.hpp>
#include <simgear/misc/strutils.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/structure/commands.hxx>
-#include <simgear/misc/sgstream.hxx>
-
-#include <simgear/props/props_io.hxx>
#include <simgear/misc/sg_path.hxx>
-#include <simgear/route/route.hxx>
#include <simgear/sg_inlines.h>
#include "Main/fg_props.hxx"
#include "Navaids/positioned.hxx"
#include <Navaids/waypoint.hxx>
-#include <Navaids/airways.hxx>
#include <Navaids/procedure.hxx>
#include "Airports/simple.hxx"
#include "Airports/runways.hxx"
-
-#define RM "/autopilot/route-manager/"
-
#include <GUI/new_gui.hxx>
#include <GUI/dialog.hxx>
+#define RM "/autopilot/route-manager/"
+
using namespace flightgear;
+using std::string;
static bool commandLoadFlightPlan(const SGPropertyNode* arg)
{
if (activate) {
self->activate();
} else {
-
+ self->deactivate();
}
return true;
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);
wpn->getChild("eta", 0, true);
_pathNode = fgGetNode(RM "file-path", 0, true);
- setFlightPlan(new FlightPlan());
}
void FGRouteMgr::postinit()
{
+ setFlightPlan(new FlightPlan());
+
SGPath path(_pathNode->getStringValue());
if (!path.isNull()) {
SG_LOG(SG_AUTOPILOT, SG_INFO, "loading flight-plan from: " << path.str());
}
if (_plan) {
+ _plan->removeDelegate(this);
delete _plan;
active->setBoolValue(false);
}
_plan = plan;
- _plan->setDelegate(this);
+ _plan->addDelegate(this);
_flightplanChanged->fireValueChanged();
}
if (checkFinished()) {
- // maybe we're done
+ endOfRoute();
}
// basic course/distance information
Waypt* FGRouteMgr::wayptAtIndex(int index) const
{
- if (_plan) {
- FlightPlan::Leg* leg = _plan->legAtIndex(index);
- if (leg) {
- return leg->waypoint();
- }
+ if (!_plan) {
+ throw sg_range_exception("wayptAtindex: no flightplan");
}
- return NULL;
+ return _plan->legAtIndex(index)->waypoint();
}
int FGRouteMgr::numLegs() const
void FGRouteMgr::setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDistance)
{
- double speed = fgGetDouble("/velocities/groundspeed-kt", 0.0);
+ double speed = groundSpeed->getDoubleValue();
if (speed < 1.0) {
aProp->setStringValue("--:--");
return;
_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 (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);
- }
-
- if (index >= numLegs()) {
- if (!_plan->destinationAirport()) {
- SG_LOG(SG_AUTOPILOT, SG_WARN, "routeToIndex: no destination set");
- return false;
- }
-
- 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);
-}
-
-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::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();
+ _edited->fireValueChanged();
+
+// removing waypoints, deactivate if we hit the end.
+ if (currentIndex() >= numLegs()) {
+ endOfRoute();
+ }
}
// mirror internal route to the property system for inspection by other subsystems
r++;
if (*r)
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
return true;
}
+void FGRouteMgr::deactivate()
+{
+ if (!isRouteActive()) {
+ return;
+ }
+
+ SG_LOG(SG_AUTOPILOT, SG_INFO, "deactivating flight plan");
+ active->setBoolValue(false);
+}
void FGRouteMgr::sequence()
{
return;
}
- if (checkFinished()) {
+ int nextIndex = _plan->currentIndex() + 1;
+ if (nextIndex >= _plan->numLegs()) {
+ SG_LOG(SG_AUTOPILOT, SG_INFO, "sequenced on final leg, deactivating route");
+ endOfRoute();
return;
}
-
- _plan->setCurrentIndex(_plan->currentIndex() + 1);
+
+ _plan->setCurrentIndex(nextIndex);
+}
+
+void FGRouteMgr::endOfRoute()
+{
+ SG_LOG(SG_AUTOPILOT, SG_INFO, "reached end of active route");
+ _finished->fireValueChanged();
+ active->setBoolValue(false);
}
bool FGRouteMgr::checkFinished()
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);
+ SG_LOG(SG_AUTOPILOT, SG_INFO, "checkFinished: on the ground on destination runway, we're done");
}
return done;
return "";
}
+static double headingDiffDeg(double a, double b)
+{
+ double rawDiff = b - a;
+ SG_NORMALIZE_RANGE(rawDiff, -180.0, 180.0);
+ return rawDiff;
+}
+
+flightgear::SID* createDefaultSID(FGRunway* aRunway, double enrouteCourse)
+{
+ if (!aRunway) {
+ return NULL;
+ }
+
+ double runwayElevFt = aRunway->end().getElevationFt();
+ WayptVec wpts;
+ std::ostringstream ss;
+ ss << aRunway->ident() << "-3";
+
+ SGGeod p = aRunway->pointOnCenterline(aRunway->lengthM() + (3.0 * SG_NM_TO_METER));
+ WayptRef w = new BasicWaypt(p, ss.str(), NULL);
+ w->setAltitude(runwayElevFt + 3000.0, RESTRICT_AT);
+ wpts.push_back(w);
+
+ ss.str("");
+ ss << aRunway->ident() << "-6";
+ p = aRunway->pointOnCenterline(aRunway->lengthM() + (6.0 * SG_NM_TO_METER));
+ w = new BasicWaypt(p, ss.str(), NULL);
+ w->setAltitude(runwayElevFt + 6000.0, RESTRICT_AT);
+ wpts.push_back(w);
+
+ if (enrouteCourse >= 0.0) {
+ // valid enroute course
+ int index = 3;
+ double course = aRunway->headingDeg();
+ double diff;
+ while (fabs(diff = headingDiffDeg(course, enrouteCourse)) > 45.0) {
+ // turn in the sign of the heading change 45 degrees
+ course += copysign(45.0, diff);
+ ss.str("");
+ ss << "DEP-" << index++;
+ SGGeod pos = wpts.back()->position();
+ pos = SGGeodesy::direct(pos, course, 3.0 * SG_NM_TO_METER);
+ w = new BasicWaypt(pos, ss.str(), NULL);
+ wpts.push_back(w);
+ }
+ } else {
+ // no enroute course, just keep runway heading
+ ss.str("");
+ ss << aRunway->ident() << "-9";
+ p = aRunway->pointOnCenterline(aRunway->lengthM() + (9.0 * SG_NM_TO_METER));
+ w = new BasicWaypt(p, ss.str(), NULL);
+ w->setAltitude(runwayElevFt + 9000.0, RESTRICT_AT);
+ wpts.push_back(w);
+ }
+
+ BOOST_FOREACH(Waypt* w, wpts) {
+ w->setFlag(WPT_DEPARTURE);
+ w->setFlag(WPT_GENERATED);
+ }
+
+ return flightgear::SID::createTempSID("DEFAULT", aRunway, wpts);
+}
+
void FGRouteMgr::setSID(const char* aIdent)
{
FGAirport* apt = _plan->departureAirport();
return;
}
+ if (!strcmp(aIdent, "DEFAULT")) {
+ double enrouteCourse = -1.0;
+ if (_plan->destinationAirport()) {
+ enrouteCourse = SGGeodesy::courseDeg(apt->geod(), _plan->destinationAirport()->geod());
+ }
+
+ _plan->setSID(createDefaultSID(_plan->departureRunway(), enrouteCourse));
+ return;
+ }
+
string ident(aIdent);
size_t hyphenPos = ident.find('-');
if (hyphenPos != string::npos) {
return "";
}
+flightgear::Approach* createDefaultApproach(FGRunway* aRunway, double aEnrouteCourse)
+{
+ if (!aRunway) {
+ return NULL;
+ }
+
+ double thresholdElevFt = aRunway->threshold().getElevationFt();
+ const double approachHeightFt = 2000.0;
+ double glideslopeDistanceM = (approachHeightFt * SG_FEET_TO_METER) /
+ tan(3.0 * SG_DEGREES_TO_RADIANS);
+
+ std::ostringstream ss;
+ ss << aRunway->ident() << "-12";
+ WayptVec wpts;
+ SGGeod p = aRunway->pointOnCenterline(-12.0 * SG_NM_TO_METER);
+ WayptRef w = new BasicWaypt(p, ss.str(), NULL);
+ w->setAltitude(thresholdElevFt + 4000, RESTRICT_AT);
+ wpts.push_back(w);
+
+// work back form the first point on the centerline
+
+ if (aEnrouteCourse >= 0.0) {
+ // valid enroute course
+ int index = 4;
+ double course = aRunway->headingDeg();
+ double diff;
+ while (fabs(diff = headingDiffDeg(aEnrouteCourse, course)) > 45.0) {
+ // turn in the sign of the heading change 45 degrees
+ course -= copysign(45.0, diff);
+ ss.str("");
+ ss << "APP-" << index++;
+ SGGeod pos = wpts.front()->position();
+ pos = SGGeodesy::direct(pos, course + 180.0, 3.0 * SG_NM_TO_METER);
+ w = new BasicWaypt(pos, ss.str(), NULL);
+ wpts.insert(wpts.begin(), w);
+ }
+ }
+
+ p = aRunway->pointOnCenterline(-8.0 * SG_NM_TO_METER);
+ ss.str("");
+ ss << aRunway->ident() << "-8";
+ w = new BasicWaypt(p, ss.str(), NULL);
+ w->setAltitude(thresholdElevFt + approachHeightFt, RESTRICT_AT);
+ wpts.push_back(w);
+
+ p = aRunway->pointOnCenterline(-glideslopeDistanceM);
+ ss.str("");
+ ss << aRunway->ident() << "-GS";
+ w = new BasicWaypt(p, ss.str(), NULL);
+ w->setAltitude(thresholdElevFt + approachHeightFt, RESTRICT_AT);
+ wpts.push_back(w);
+
+ BOOST_FOREACH(Waypt* w, wpts) {
+ w->setFlag(WPT_APPROACH);
+ w->setFlag(WPT_GENERATED);
+ }
+
+ return Approach::createTempApproach("DEFAULT", aRunway, wpts);
+}
+
void FGRouteMgr::setApproach(const char* aIdent)
{
FGAirport* apt = _plan->destinationAirport();
+ if (!strcmp(aIdent, "DEFAULT")) {
+ double enrouteCourse = -1.0;
+ if (_plan->departureAirport()) {
+ enrouteCourse = SGGeodesy::courseDeg(_plan->departureAirport()->geod(), apt->geod());
+ }
+
+ _plan->setApproach(createDefaultApproach(_plan->destinationRunway(), enrouteCourse));
+ return;
+ }
+
if (!apt || (aIdent == NULL)) {
_plan->setApproach(NULL);
} else {