]> git.mxchange.org Git - flightgear.git/blobdiff - src/Autopilot/route_mgr.cxx
Merge branch 'jmt/gpswidget'
[flightgear.git] / src / Autopilot / route_mgr.cxx
index 9e8d3c23ae753229e13b88fb54192ff084044b52..64263dc25749eedfc0913bef2722a6548293da71 100644 (file)
@@ -92,15 +92,18 @@ void FGRouteMgr::init() {
   magvar = fgGetNode("/environment/magnetic-variation-deg", true);
      
   departure = fgGetNode(RM "departure", true);
+  departure->tie("airport", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
+    &FGRouteMgr::getDepartureICAO, &FGRouteMgr::setDepartureICAO));
+  departure->tie("name", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
+    &FGRouteMgr::getDepartureName, NULL));
+    
 // init departure information from current location
   SGGeod pos = SGGeod::fromDegFt(lon->getDoubleValue(), lat->getDoubleValue(), alt->getDoubleValue());
-  FGAirport* apt = FGAirport::findClosest(pos, 20.0);
-  if (apt) {
-    departure->setStringValue("airport", apt->ident().c_str());
-    FGRunway* active = apt->getActiveRunwayForUsage();
+  _departure = FGAirport::findClosest(pos, 20.0);
+  if (_departure) {
+    FGRunway* active = _departure->getActiveRunwayForUsage();
     departure->setStringValue("runway", active->ident().c_str());
   } else {
-    departure->setStringValue("airport", "");
     departure->setStringValue("runway", "");
   }
   
@@ -109,6 +112,12 @@ void FGRouteMgr::init() {
 
   destination = fgGetNode(RM "destination", true);
   destination->getChild("airport", 0, true);
+  
+  destination->tie("airport", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
+    &FGRouteMgr::getDestinationICAO, &FGRouteMgr::setDestinationICAO));
+  destination->tie("name", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
+    &FGRouteMgr::getDestinationName, NULL));
+    
   destination->getChild("runway", 0, true);
   destination->getChild("eta", 0, true);
   destination->getChild("touchdown-time", 0, true);
@@ -139,8 +148,12 @@ void FGRouteMgr::init() {
   airborne = fgGetNode(RM "airborne", true);
   airborne->setBoolValue(false);
     
-  currentWp = fgGetNode(RM "current-wp", true);
-  currentWp->setIntValue(_route->current_index());
+  _edited = fgGetNode(RM "signals/edited", true);
+  _finished = fgGetNode(RM "signals/finished", true);
+  
+  _currentWpt = fgGetNode(RM "current-wp", true);
+  _currentWpt->tie(SGRawValueMethods<FGRouteMgr, int>
+    (*this, &FGRouteMgr::currentWaypoint, &FGRouteMgr::jumpToIndex));
       
   // temporary distance / eta calculations, for backward-compatability
   wp0 = fgGetNode(RM "wp", 0, true);
@@ -159,6 +172,7 @@ void FGRouteMgr::init() {
   wpn->getChild("eta", 0, true);
   
   _route->clear();
+  _route->set_current(0);
   update_mirror();
   
   _pathNode = fgGetNode(RM "file-path", 0, true);
@@ -212,12 +226,8 @@ void FGRouteMgr::update( double dt ) {
     }
     
   // basic course/distance information
-    double inboundCourse, dummy, wp_course, wp_distance;
+    double wp_course, wp_distance;
     SGWayPoint wp = _route->get_current();
-  
-    wp.CourseAndDistance(_route->get_waypoint(_route->current_index() - 1), 
-      &inboundCourse, &dummy);
-    
     wp.CourseAndDistance( lon->getDoubleValue(), lat->getDoubleValue(),
                           alt->getDoubleValue(), &wp_course, &wp_distance );
 
@@ -276,24 +286,51 @@ void FGRouteMgr::setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDi
     aProp->setStringValue( eta_str );
 }
 
-void FGRouteMgr::add_waypoint( const SGWayPoint& wp, int n ) {
-    _route->add_waypoint( wp, n );
-    update_mirror();
+void FGRouteMgr::add_waypoint( const SGWayPoint& wp, int n )
+{
+  _route->add_waypoint( wp, n );
+    
+  if ((n >= 0) && (_route->current_index() > n)) {
+    _route->set_current(_route->current_index() + 1);
+  }
+  
+  waypointsChanged();
 }
 
+void FGRouteMgr::waypointsChanged()
+{
+  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));
+  }
+
+  update_mirror();
+  _edited->fireValueChanged();
+  checkFinished();
+}
 
 SGWayPoint FGRouteMgr::pop_waypoint( int n ) {
-    SGWayPoint wp;
-
-    if ( _route->size() > 0 ) {
-        if ( n < 0 )
-            n = _route->size() - 1;
-        wp = _route->get_waypoint(n);
-        _route->delete_waypoint(n);
-    }
+  if ( _route->size() <= 0 ) {
+    return SGWayPoint();
+  }
+  
+  if ( n < 0 ) {
+    n = _route->size() - 1;
+  }
+  
+  if (_route->current_index() > n) {
+    _route->set_current(_route->current_index() - 1);
+  }
 
-    update_mirror();
-    return wp;
+  SGWayPoint wp = _route->get_waypoint(n);
+  _route->delete_waypoint(n);
+    
+  waypointsChanged();
+  return wp;
 }
 
 
@@ -310,19 +347,15 @@ void FGRouteMgr::new_waypoint( const string& target, int n ) {
     
     add_waypoint( *wp, n );
     delete wp;
-
-    if ( !near_ground() ) {
-        fgSetString( "/autopilot/locks/heading", "true-heading-hold" );
-    }
 }
 
 
 SGWayPoint* FGRouteMgr::make_waypoint(const string& tgt ) {
     string target(boost::to_upper_copy(tgt));
     
-    // extract altitude
-    double alt = cruise->getDoubleValue("altitude-ft") * SG_FEET_TO_METER;
     
+    double alt = -9999.0;
+    // extract altitude
     size_t pos = target.find( '@' );
     if ( pos != string::npos ) {
         alt = atof( target.c_str() + pos + 1 );
@@ -336,7 +369,11 @@ SGWayPoint* FGRouteMgr::make_waypoint(const string& tgt ) {
     if ( pos != string::npos ) {
         double lon = atof( target.substr(0, pos).c_str());
         double lat = atof( target.c_str() + pos + 1);
-        return new SGWayPoint( lon, lat, alt, SGWayPoint::WGS84, target );
+        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;
@@ -420,20 +457,6 @@ void FGRouteMgr::update_mirror() {
     mirror->setIntValue("num", _route->size());
 }
 
-
-bool FGRouteMgr::near_ground() {
-    SGPropertyNode *gear = fgGetNode( "/gear/gear/wow", false );
-    if ( !gear || gear->getType() == simgear::props::NONE )
-        return fgGetBool( "/sim/presets/onground", true );
-
-    if ( fgGetDouble("/position/altitude-agl-ft", 300.0)
-            < fgGetDouble("/autopilot/route-manager/min-lock-altitude-agl-ft") )
-        return true;
-
-    return gear->getBoolValue();
-}
-
-
 // command interface /autopilot/route-manager/input:
 //
 //   @CLEAR             ... clear route
@@ -445,6 +468,10 @@ bool FGRouteMgr::near_ground() {
 void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop)
 {
     const char *s = prop->getStringValue();
+    if (strlen(s) == 0) {
+      return;
+    }
+    
     if (!strcmp(s, "@CLEAR"))
         mgr->init();
     else if (!strcmp(s, "@ACTIVATE"))
@@ -453,9 +480,15 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop)
       mgr->loadRoute();
     } else if (!strcmp(s, "@SAVE")) {
       mgr->saveRoute();
-    } else if (!strcmp(s, "@POP"))
-        mgr->pop_waypoint(0);
-    else if (!strncmp(s, "@DELETE", 7))
+    } else if (!strcmp(s, "@POP")) {
+      SG_LOG(SG_AUTOPILOT, SG_WARN, "route-manager @POP command is deprecated");
+    } else if (!strcmp(s, "@NEXT")) {
+      mgr->jumpToIndex(mgr->currentWaypoint() + 1);
+    } else if (!strcmp(s, "@PREVIOUS")) {
+      mgr->jumpToIndex(mgr->currentWaypoint() - 1);
+    } else if (!strncmp(s, "@JUMP", 5)) {
+      mgr->jumpToIndex(atoi(s + 5));
+    } else if (!strncmp(s, "@DELETE", 7))
         mgr->pop_waypoint(atoi(s + 7));
     else if (!strncmp(s, "@INSERT", 7)) {
         char *r;
@@ -476,62 +509,46 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop)
 
 bool FGRouteMgr::activate()
 {
-  const FGAirport* depApt = fgFindAirportID(departure->getStringValue("airport"));
-  if (!depApt) {
-    SG_LOG(SG_AUTOPILOT, SG_ALERT, 
-      "unable to activate route: departure airport is invalid:" 
-        << departure->getStringValue("airport") );
+  if (isRouteActive()) {
+    SG_LOG(SG_AUTOPILOT, SG_WARN, "duplicate route-activation, no-op");
     return false;
   }
-  
-  string runwayId(departure->getStringValue("runway"));
-  FGRunway* runway = NULL;
-  if (depApt->hasRunwayWithIdent(runwayId)) {
-    runway = depApt->getRunwayByIdent(runwayId);
-  } else {
-    SG_LOG(SG_AUTOPILOT, SG_INFO, 
-      "route-manager, departure runway not found:" << runwayId);
-    runway = depApt->getActiveRunwayForUsage();
+
+  // 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);
   }
   
-  SGWayPoint swp(runway->threshold(), 
-    depApt->ident() + "-" + runway->ident(), runway->name());
-  add_waypoint(swp, 0);
-  
-  const FGAirport* destApt = fgFindAirportID(destination->getStringValue("airport"));
-  if (!destApt) {
-    SG_LOG(SG_AUTOPILOT, SG_ALERT, 
-      "unable to activate route: destination airport is invalid:" 
-        << destination->getStringValue("airport") );
-    return false;
+  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()));
+    }
   }
 
-  runwayId = (destination->getStringValue("runway"));
-  if (destApt->hasRunwayWithIdent(runwayId)) {
-    FGRunway* runway = depApt->getRunwayByIdent(runwayId);
-    SGWayPoint swp(runway->end(), 
-      destApt->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(destApt->geod(), destApt->ident(), destApt->name()));
-  }
-  
   _route->set_current(0);
-  
-  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));
-  }
-  
   active->setBoolValue(true);
-  sequence(); // sequence will sync up wp0, wp1 and current-wp
   SG_LOG(SG_AUTOPILOT, SG_INFO, "route-manager, activate route ok");
   return true;
 }
@@ -544,24 +561,30 @@ void FGRouteMgr::sequence()
     return;
   }
   
-  if (_route->current_index() == _route->size()) {
-    SG_LOG(SG_AUTOPILOT, SG_INFO, "reached end of active route");
-    // what now?
-    active->setBoolValue(false);
+  if (checkFinished()) {
     return;
   }
   
   _route->increment_current();
   currentWaypointChanged();
+  _currentWpt->fireValueChanged();
 }
 
-void FGRouteMgr::jumpToIndex(int index)
+bool FGRouteMgr::checkFinished()
 {
-  if (!active->getBoolValue()) {
-    SG_LOG(SG_AUTOPILOT, SG_ALERT, "trying to sequence waypoints with no active route");
-    return;
+  int lastWayptIndex = _route->size() - 1;
+  if (_route->current_index() < lastWayptIndex) {
+    return false;
   }
+  
+  SG_LOG(SG_AUTOPILOT, SG_INFO, "reached end of active route");
+  _finished->fireValueChanged();
+  active->setBoolValue(false);
+  return true;
+}
 
+void FGRouteMgr::jumpToIndex(int index)
+{
   if ((index < 0) || (index >= _route->size())) {
     SG_LOG(SG_AUTOPILOT, SG_ALERT, "passed invalid index (" << 
       index << ") to FGRouteMgr::jumpToIndex");
@@ -588,7 +611,6 @@ void FGRouteMgr::currentWaypointChanged()
     wp1->getChild("id")->setStringValue("");
   }
   
-  currentWp->setIntValue(_route->current_index());
   SG_LOG(SG_AUTOPILOT, SG_INFO, "route manager, current-wp is now " << _route->current_index());
 }
 
@@ -619,6 +641,16 @@ int FGRouteMgr::currentWaypoint() const
   return _route->current_index();
 }
 
+void FGRouteMgr::setWaypointTargetAltitudeFt(unsigned int index, int altFt)
+{
+  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();
+}
+
 void FGRouteMgr::saveRoute()
 {
   SGPath path(_pathNode->getStringValue());
@@ -650,21 +682,17 @@ void FGRouteMgr::loadRoute()
     }
     
     string depIdent = dep->getStringValue("airport");
-    const FGAirport* depApt = fgFindAirportID(depIdent);
-    if (!depApt) {
-      throw sg_io_exception("bad route file, unknown airport:" + depIdent);
-    }
-    
-    departure->setStringValue("runway", dep->getStringValue("runway"));
-    
+    _departure = (FGAirport*) fgFindAirportID(depIdent);
+
+        
   // destination
     SGPropertyNode* dst = routeData->getChild("destination");
     if (!dst) {
       throw sg_io_exception("malformed route file, no destination node");
     }
     
-    destination->setStringValue("airport", dst->getStringValue("airport"));
-    destination->setStringValue("runay", dst->getStringValue("runway"));
+    _destination = (FGAirport*) fgFindAirportID(dst->getStringValue("airport"));
+    destination->setStringValue("runway", dst->getStringValue("runway"));
 
   // alternate
     SGPropertyNode* alt = routeData->getChild("alternate");
@@ -681,7 +709,7 @@ void FGRouteMgr::loadRoute()
   // route nodes
     _route->clear();
     SGPropertyNode_ptr _route = routeData->getChild("route", 0);
-    SGGeod lastPos(depApt->geod());
+    SGGeod lastPos = (_departure ? _departure->geod() : SGGeod());
     
     for (int i=0; i<_route->nChildren(); ++i) {
       SGPropertyNode_ptr wp = _route->getChild("wp", i);
@@ -706,20 +734,16 @@ void FGRouteMgr::parseRouteWaypoint(SGPropertyNode* aWP)
   }
 
   SGPropertyNode_ptr altProp = aWP->getChild("altitude-ft");
-  double alt = -9999.0;
+  double altM = cruise->getDoubleValue("altitude-ft") * SG_FEET_TO_METER;
   if (altProp) {
-    alt = altProp->getDoubleValue();
+    altM = altProp->getDoubleValue() * SG_FEET_TO_METER;
   }
       
   string ident(aWP->getStringValue("ident"));
   if (aWP->hasChild("longitude-deg")) {
     // explicit longitude/latitude
-    if (alt < -9990.0) {
-      alt = 0.0; // don't export wyapoints with invalid altitude
-    }
-    
     SGWayPoint swp(aWP->getDoubleValue("longitude-deg"),
-      aWP->getDoubleValue("latitude-deg"), alt, 
+      aWP->getDoubleValue("latitude-deg"), altM
       SGWayPoint::WGS84, ident, aWP->getStringValue("name"));
     add_waypoint(swp);
   } else if (aWP->hasChild("navid")) {
@@ -740,11 +764,7 @@ void FGRouteMgr::parseRouteWaypoint(SGPropertyNode* aWP)
       SGGeodesy::direct(p->geod(), radialDeg, offsetNm * SG_NM_TO_METER, pos, az2);
     }
     
-    if (alt < -9990.0) {
-      alt = p->elevation();
-    }
-    
-    SGWayPoint swp(pos.getLongitudeDeg(), pos.getLatitudeDeg(), alt, 
+    SGWayPoint swp(pos.getLongitudeDeg(), pos.getLatitudeDeg(), altM, 
       SGWayPoint::WGS84, ident, "");
     add_waypoint(swp);
   } else {
@@ -754,12 +774,63 @@ void FGRouteMgr::parseRouteWaypoint(SGPropertyNode* aWP)
       throw sg_io_exception("bad route file, unknown waypoint:" + ident);
     }
     
-    if (alt < -9990.0) {
-      alt = p->elevation();
-    }
-    
-    SGWayPoint swp(p->longitude(), p->latitude(), alt, 
+    SGWayPoint swp(p->longitude(), p->latitude(), altM, 
       SGWayPoint::WGS84, p->ident(), p->name());
     add_waypoint(swp);
   }
 }
+
+const char* FGRouteMgr::getDepartureICAO() const
+{
+  if (!_departure) {
+    return "";
+  }
+  
+  return _departure->ident().c_str();
+}
+
+const char* FGRouteMgr::getDepartureName() const
+{
+  if (!_departure) {
+    return "";
+  }
+  
+  return _departure->name().c_str();
+}
+
+void FGRouteMgr::setDepartureICAO(const char* aIdent)
+{
+  if ((aIdent == NULL) || (strlen(aIdent) < 4)) {
+    _departure = NULL;
+  } else {
+    _departure = FGAirport::findByIdent(aIdent);
+  }
+}
+
+const char* FGRouteMgr::getDestinationICAO() const
+{
+  if (!_destination) {
+    return "";
+  }
+  
+  return _destination->ident().c_str();
+}
+
+const char* FGRouteMgr::getDestinationName() const
+{
+  if (!_destination) {
+    return "";
+  }
+  
+  return _destination->name().c_str();
+}
+
+void FGRouteMgr::setDestinationICAO(const char* aIdent)
+{
+  if ((aIdent == NULL) || (strlen(aIdent) < 4)) {
+    _destination = NULL;
+  } else {
+    _destination = FGAirport::findByIdent(aIdent);
+  }
+}
+