]> git.mxchange.org Git - flightgear.git/blobdiff - src/Airports/groundnetwork.cxx
Update FGRunway to process information from threshold.xml files.
[flightgear.git] / src / Airports / groundnetwork.cxx
index 2333729933e265c8521ece8d9076dfb74859fea8..f1e0e086faa92073e0bcc06ef35825d113f49bc5 100644 (file)
 #include <math.h>
 #include <algorithm>
 
-//#include <plib/sg.h>
-//#include <plib/ul.h>
-
-//#include <Environment/environment_mgr.hxx>
-//#include <Environment/environment.hxx>
-//#include <simgear/misc/sg_path.hxx>
-//#include <simgear/props/props.hxx>
-//#include <simgear/structure/subsystem_mgr.hxx>
 #include <simgear/debug/logstream.hxx>
 #include <simgear/route/waypoint.hxx>
-//#include <Main/globals.hxx>
-//#include <Main/fg_props.hxx>
-//#include <Airports/runways.hxx>
 
-#include <AIModel/AIFlightPlan.hxx>
+#include <Airports/dynamics.hxx>
 
-//#include STL_STRING
+#include <AIModel/AIFlightPlan.hxx>
 
 #include "groundnetwork.hxx"
 
-SG_USING_STD(sort);
-
-
-
-/**************************************************************************
- * FGTaxiNode
- *************************************************************************/
-FGTaxiNode::FGTaxiNode()
-{
-}
-
-
-bool compare_nodes(FGTaxiNode *a, FGTaxiNode *b) {
-return (*a) < (*b);
-}
-
 /***************************************************************************
  * FGTaxiSegment
  **************************************************************************/
-FGTaxiSegment::FGTaxiSegment()
-{
-  oppositeDirection = 0;
-}
 
 void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
 {
   FGTaxiNodeVectorIterator i = nodes->begin();
   while (i != nodes->end())
     {
+      //cerr << "Scanning start node index" << (*i)->getIndex() << endl;
       if ((*i)->getIndex() == startNode)
        {
          start = (*i)->getAddress();
@@ -84,6 +54,7 @@ void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
        }
       i++;
     }
+  SG_LOG(SG_GENERAL, SG_ALERT,  "Could not find start node " << startNode << endl);
 }
 
 void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
@@ -91,6 +62,7 @@ void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
   FGTaxiNodeVectorIterator i = nodes->begin();
   while (i != nodes->end())
     {
+      //cerr << "Scanning end node index" << (*i)->getIndex() << endl;
       if ((*i)->getIndex() == endNode)
        {
          end = (*i)->getAddress();
@@ -98,26 +70,28 @@ void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
        }
       i++;
     }
+  SG_LOG(SG_GENERAL, SG_ALERT,  "Could not find end node " << endNode << endl);
 }
 
+
+
 // There is probably a computationally cheaper way of 
 // doing this.
 void FGTaxiSegment::setTrackDistance()
 {
-  double course;
-  SGWayPoint first  (start->getLongitude(),
-                    start->getLatitude(),
-                    0);
-  SGWayPoint second (end->getLongitude(),
-                    end->getLatitude(),
-                    0);
-  first.CourseAndDistance(second, &course, &length);
+  length = SGGeodesy::distanceM(start->getGeod(), end->getGeod());
 }
 
-bool compare_segments(FGTaxiSegment *a, FGTaxiSegment *b) {
-return (*a) < (*b);
+
+void FGTaxiSegment::setCourseDiff(double crse)
+{
+  headingDiff = fabs(course-crse);
+  
+  if (headingDiff > 180)
+    headingDiff = fabs(headingDiff - 360);
 }
 
+
 /***************************************************************************
  * FGTaxiRoute
  **************************************************************************/
@@ -197,6 +171,13 @@ void FGTaxiRoute::rewind(int route)
 /***************************************************************************
  * FGGroundNetwork()
  **************************************************************************/
+bool compare_nodes(FGTaxiNode *a, FGTaxiNode *b) {
+return (*a) < (*b);
+}
+
+bool compare_segments(FGTaxiSegment *a, FGTaxiSegment *b) {
+return (*a) < (*b);
+}
 
 FGGroundNetwork::FGGroundNetwork()
 {
@@ -204,6 +185,8 @@ FGGroundNetwork::FGGroundNetwork()
   foundRoute = false;
   totalDistance = 0;
   maxDistance = 0;
+  //maxDepth    = 1000;
+  count       = 0;
   currTraffic = activeTraffic.begin();
 
 }
@@ -217,6 +200,7 @@ FGGroundNetwork::~FGGroundNetwork()
       delete (*node);
     }
   nodes.clear();
+  pushBackNodes.clear();
   for (FGTaxiSegmentVectorIterator seg = segments.begin();
        seg != segments.end();
        seg++)
@@ -261,16 +245,21 @@ void FGGroundNetwork::init()
   //sort(segments.begin(), segments.end(), compare_segments());
   FGTaxiSegmentVectorIterator i = segments.begin();
   while(i != segments.end()) {
-    //cerr << "initializing node " << i->getIndex() << endl;
     (*i)->setStart(&nodes);
     (*i)->setEnd  (&nodes);
     (*i)->setTrackDistance();
     (*i)->setIndex(index);
-    //cerr << "Track distance = " << i->getLength() << endl;
-    //cerr << "Track ends at"      << i->getEnd()->getIndex() << endl;
+    if ((*i)->isPushBack()) {
+          pushBackNodes.push_back((*i)->getEnd());
+    }
+    //SG_LOG(SG_GENERAL, SG_BULK,  "initializing segment " << (*i)->getIndex() << endl);
+    //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = "     << (*i)->getLength() << endl);
+    //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from "      << (*i)->getStart()->getIndex() << " to "
+    //                                                    << (*i)->getEnd()->getIndex() << endl);
     i++;
     index++;
   }
   i = segments.begin();
   while(i != segments.end()) {
     FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute(); 
@@ -278,51 +267,56 @@ void FGGroundNetwork::init()
       {
        if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex())
          {
-           int start1 = (*i)->getStart()->getIndex();
-           int end1   = (*i)->getEnd()  ->getIndex();
-           int start2 = (*j)->getStart()->getIndex();
-           int end2   = (*j)->getEnd()->getIndex();
-           int oppIndex = (*j)->getIndex();
-//         cerr << "Opposite of  " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
-//              << "happens to be " << oppIndex      << " (" << start2 << "," << end2 << ") " << endl;
+//         int start1 = (*i)->getStart()->getIndex();
+//         int end1   = (*i)->getEnd()  ->getIndex();
+//         int start2 = (*j)->getStart()->getIndex();
+//         int end2   = (*j)->getEnd()->getIndex();
+//         int oppIndex = (*j)->getIndex();
+           //cerr << "Opposite of  " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
+           //   << "happens to be " << oppIndex      << " (" << start2 << "," << end2 << ") " << endl;
+           (*i)->setOpposite(*j);
            break;
          }
          j++;
       }
     i++;
   }
+  //FGTaxiNodeVectorIterator j = nodes.begin();
+  //while (j != nodes.end()) {
+  //    if ((*j)->getHoldPointType() == 3) {
+  //        pushBackNodes.push_back((*j));
+  //    }
+  //    j++;
+  //}
+  //cerr << "Done initializing ground network" << endl;
   //exit(1);
 }
 
-int FGGroundNetwork::findNearestNode(double lat, double lon)
+int FGGroundNetwork::findNearestNode(const SGGeod& aGeod)
 {
   double minDist = HUGE_VAL;
-  double dist;
-  int index;
-  SGWayPoint first  (lon,
-                    lat,
-                    0);
+  int index = -1;
   
-  for (FGTaxiNodeVectorIterator 
-        itr = nodes.begin();
-       itr != nodes.end(); itr++)
+  for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end(); itr++)
+  {
+    double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod());
+    if (d < minDist)
     {
-      double course;
-      SGWayPoint second ((*itr)->getLongitude(),
-                        (*itr)->getLatitude(),
-                        0);
-      first.CourseAndDistance(second, &course, &dist);
-      if (dist < minDist)
-       {
-         minDist = dist;
-         index = (*itr)->getIndex();
-         //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
-       }
+      minDist = d;
+      index = (*itr)->getIndex();
+      //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
     }
+  }
+  
   return index;
 }
 
-FGTaxiNode *FGGroundNetwork::findNode(int idx)
+int FGGroundNetwork::findNearestNode(double lat, double lon)
+{
+  return findNearestNode(SGGeod::fromDeg(lon, lat));
+}
+
+FGTaxiNode *FGGroundNetwork::findNode(unsigned idx)
 { /*
     for (FGTaxiNodeVectorIterator 
     itr = nodes.begin();
@@ -338,7 +332,7 @@ FGTaxiNode *FGGroundNetwork::findNode(int idx)
     return 0;
 }
 
-FGTaxiSegment *FGGroundNetwork::findSegment(int idx)
+FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx)
 {/*
   for (FGTaxiSegmentVectorIterator 
         itr = segments.begin();
@@ -357,179 +351,113 @@ FGTaxiSegment *FGGroundNetwork::findSegment(int idx)
     }
 }
 
-FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end) 
-{
-  foundRoute = false;
-  totalDistance = 0;
-  FGTaxiNode *firstNode = findNode(start);
-  FGTaxiNode *lastNode  = findNode(end);
-  //prevNode = prevPrevNode = -1;
-  //prevNode = start;
-  routes.clear();
-  nodesStack.clear();
-  routesStack.clear();
-  //cerr << "Begin of Trace " << endl;
-  trace(firstNode, end, 0, 0);
-  //cerr << "End of Trace" << endl;
-  FGTaxiRoute empty;
-  
-  if (!foundRoute)
-    {
-      SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find route from waypoint " << start << " to " << end );
-      exit(1);
-    }
-  sort(routes.begin(), routes.end());
-  //for (intVecIterator i = route.begin(); i != route.end(); i++)
-  //  {
-  //    rte->push_back(*i);
-  //  }
-  
-  if (routes.begin() != routes.end())
-    return *(routes.begin());
-  else
-    return empty;
-}
 
-
-void FGGroundNetwork::trace(FGTaxiNode *currNode, int end, int depth, double distance)
+FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, bool fullSearch) 
 {
-  // Just check some preconditions of the trace algorithm
-  if (nodesStack.size() != routesStack.size()) 
-    {
-      SG_LOG(SG_GENERAL, SG_ALERT, "size of nodesStack and routesStack is not equal. NodesStack :" 
-            << nodesStack.size() << ". RoutesStack : " << routesStack.size());
-    }
-  nodesStack.push_back(currNode->getIndex());
-  totalDistance += distance;
-  //cerr << "Starting trace " << depth << " total distance: " << totalDistance<< " "
-  //     << currNode->getIndex() << endl;
-
-  // If the current route matches the required end point we found a valid route
-  // So we can add this to the routing table
-  if (currNode->getIndex() == end)
-    {
-      //cerr << "Found route : " <<  totalDistance << "" << " " << *(nodesStack.end()-1) << endl;
-      routes.push_back(FGTaxiRoute(nodesStack,routesStack,totalDistance));
-      if (nodesStack.empty() || routesStack.empty())
-       {
-         printRoutingError(string("while finishing route"));
-       }
-      nodesStack.pop_back();
-      routesStack.pop_back();
-      if (!(foundRoute))
-       maxDistance = totalDistance;
-      else
-       if (totalDistance < maxDistance)
-         maxDistance = totalDistance;
-      foundRoute = true;
-      totalDistance -= distance;
-      return;
+//implements Dijkstra's algorithm to find shortest distance route from start to end
+//taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
+
+    //double INFINITE = 100000000000.0;
+    // initialize scoring values
+    int nParkings = parent->getDynamics()->getNrOfParkings();
+    FGTaxiNodeVector *currNodesSet;
+    if (fullSearch) {
+         currNodesSet = &nodes;
+    } else {
+         currNodesSet = &pushBackNodes;
     }
 
-  // search if the currentNode has been encountered before
-  // if so, we should step back one level, because it is
-  // rather rediculous to proceed further from here. 
-  // if the current node has not been encountered before,
-  // i should point to nodesStack.end()-1; and we can continue
-  // if i is not nodesStack.end, the previous node was found, 
-  // and we should return. 
-  // This only works at trace levels of 1 or higher though
-  if (depth > 0) {
-    intVecIterator i = nodesStack.begin();
-    while ((*i) != currNode->getIndex()) {
-      //cerr << "Route so far : " << (*i) << endl;
-      i++;
-    }
-    if (i != nodesStack.end()-1) {
-      if (nodesStack.empty() || routesStack.empty())
-       {
-         printRoutingError(string("while returning from an already encountered node"));
-       }
-      nodesStack.pop_back();
-      routesStack.pop_back();
-      totalDistance -= distance;
-      return;
-    }
-    // If the total distance from start to the current waypoint
-    // is longer than that of a route we can also stop this trace 
-    // and go back one level. 
-    if ((totalDistance > maxDistance) && foundRoute)
-      {
-       //cerr << "Stopping rediculously long trace: " << totalDistance << endl;
-       if (nodesStack.empty() || routesStack.empty())
-       {
-         printRoutingError(string("while returning from finding a rediculously long route"));
-       }
-       nodesStack.pop_back();
-       routesStack.pop_back();
-       totalDistance -= distance;
-       return;
-      }
-  }
-  
-  //cerr << "2" << endl;
-  if (currNode->getBeginRoute() != currNode->getEndRoute())
-    {
-      //cerr << "3" << endl;
-      for (FGTaxiSegmentVectorIterator 
-            i = currNode->getBeginRoute();
-          i != currNode->getEndRoute();
-          i++)
-       {
-         //cerr << (*i)->getLength() << endl;
-         //cerr << (*i)->getIndex() << endl;
-         int idx = (*i)->getIndex();
-         routesStack.push_back((*i)->getIndex());
-         trace((*i)->getEnd(), end, depth+1, (*i)->getLength());
-       //  {
-       //      // cerr << currNode -> getIndex() << " ";
-       //      route.push_back(currNode->getIndex());
-       //      return true;
-       //    }
-       }
+    for (FGTaxiNodeVectorIterator
+         itr = currNodesSet->begin();
+         itr != currNodesSet->end(); itr++) {
+            (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
+            (*itr)->setPreviousNode(0); //
+            (*itr)->setPreviousSeg (0); //
+         }
+
+    FGTaxiNode *firstNode = findNode(start);
+    firstNode->setPathScore(0);
+
+    FGTaxiNode *lastNode  = findNode(end);
+
+    FGTaxiNodeVector unvisited(*currNodesSet); // working copy
+
+    while (!unvisited.empty()) {
+        FGTaxiNode* best = *(unvisited.begin());
+        for (FGTaxiNodeVectorIterator
+             itr = unvisited.begin();
+             itr != unvisited.end(); itr++) {
+                 if ((*itr)->getPathScore() < best->getPathScore())
+                     best = (*itr);
+        }
+
+        FGTaxiNodeVectorIterator newend = remove(unvisited.begin(), unvisited.end(), best);
+        unvisited.erase(newend, unvisited.end());
+        
+        if (best == lastNode) { // found route or best not connected
+            break;
+        } else {
+            for (FGTaxiSegmentVectorIterator
+                 seg = best->getBeginRoute();
+                 seg != best->getEndRoute(); seg++) {
+                if (fullSearch || (*seg)->isPushBack()) {
+                    FGTaxiNode* tgt = (*seg)->getEnd();
+                    double alt = best->getPathScore() + (*seg)->getLength() + (*seg)->getPenalty(nParkings);
+                    if (alt < tgt->getPathScore()) {              // Relax (u,v)
+                        tgt->setPathScore(alt);
+                        tgt->setPreviousNode(best);
+                        tgt->setPreviousSeg(*seg); //
+                   }
+                } else {
+                //   // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
+                }
+            }
+        }
     }
-  else
-    {
-      //SG_LOG( SG_GENERAL, SG_DEBUG, "4" );
-    }
-  if (nodesStack.empty())
-    {
-      printRoutingError(string("while finishing trace"));
-    }
-  nodesStack.pop_back();
-  // Make sure not to dump the level-zero routesStack entry, because that was never created.
-  if (depth)
-    {
-      routesStack.pop_back();
-      //cerr << "leaving trace " << routesStack.size() << endl;
+
+    if (lastNode->getPathScore() == HUGE_VAL) {
+        // no valid route found
+       if (fullSearch) {
+            SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find route from waypoint " << start << " to " << end << " at " <<
+                    parent->getId());
+        }
+       FGTaxiRoute empty;
+       return empty;
+        //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
+    } else {
+        // assemble route from backtrace information
+        intVec nodes, routes;
+        FGTaxiNode* bt = lastNode;
+        while (bt->getPreviousNode() != 0) {
+            nodes.push_back(bt->getIndex());
+            routes.push_back(bt->getPreviousSegment()->getIndex());
+            bt = bt->getPreviousNode();
+        }
+        nodes.push_back(start);
+        reverse(nodes.begin(), nodes.end());
+        reverse(routes.begin(), routes.end());
+
+        return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
     }
-  totalDistance -= distance;
-  return;
 }
 
-void FGGroundNetwork::printRoutingError(string mess)
-{
-  SG_LOG(SG_GENERAL, SG_ALERT,  "Error in ground network trace algorithm " << mess);
-  if (nodesStack.empty())
-    {
-      SG_LOG(SG_GENERAL, SG_ALERT, " nodesStack is empty. Dumping routesStack");
-      for (intVecIterator i = routesStack.begin() ; i != routesStack.end(); i++)
-       SG_LOG(SG_GENERAL, SG_ALERT, "Route " << (*i));
-    }
-  if (routesStack.empty())
-    {
-      SG_LOG(SG_GENERAL, SG_ALERT, " routesStack is empty. Dumping nodesStack"); 
-      for (intVecIterator i = nodesStack.begin() ; i != nodesStack.end(); i++)
-       SG_LOG(SG_GENERAL, SG_ALERT, "Node " << (*i));
-    }
-  //exit(1);
+int FGTaxiSegment::getPenalty(int nGates) {
+     int penalty = 0;
+     if (end->getIndex() < nGates) {
+         penalty += 10000;
+     }
+     if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
+         penalty += 1000;
+     }
+     return penalty;
 }
 
+/* ATC Related Functions */
 
 void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
                                       double lat, double lon, double heading, 
-                                      double speed, double alt, double radius, int leg)
+                                      double speed, double alt, double radius, int leg,
+                                      FGAIAircraft *aircraft)
 {
    TrafficVectorIterator i = activeTraffic.begin();
    // Search search if the current id alread has an entry
@@ -550,6 +478,7 @@ void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, in
      rec.setPositionAndIntentions(currentPosition, intendedRoute);
      rec.setPositionAndHeading(lat, lon, heading, speed, alt);
      rec.setRadius(radius); // only need to do this when creating the record.
+     rec.setAircraft(aircraft);
      activeTraffic.push_back(rec);
    } else {
      i->setPositionAndIntentions(currentPosition, intendedRoute); 
@@ -608,23 +537,38 @@ void FGGroundNetwork::update(int id, double lat, double lon, double heading, dou
    //  return;
    //else
    //  setDt(0);
+   current->clearResolveCircularWait();
+   current->setWaitsForId(0);
    checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
    checkHoldPosition   (id, lat, lon, heading, speed, alt);
+   if (checkForCircularWaits(id)) {
+       i->setResolveCircularWait();
+   }
 }
 
+/**
+   Scan for a speed adjustment change. Find the nearest aircraft that is in front
+   and adjust speed when we get too close. Only do this when current position and/or
+   intentions of the current aircraft match current taxiroute position of the proximate
+   aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
+   instruction. See below for the hold position instruction.
+
+   Note that there currently still is one flaw in the logic that needs to be addressed. 
+   can be situations where one aircraft is in front of the current aircraft, on a separate
+   route, but really close after an intersection coming off the current route. This
+   aircraft is still close enough to block the current aircraft. This situation is currently
+   not addressed yet, but should be.
+*/
+
 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat, 
                                           double lon, double heading, 
                                           double speed, double alt)
 {
   
-  // Scan for a speed adjustment change. Find the nearest aircraft that is in front
-  // and adjust speed when we get too close. Only do this when current position and/or
-  // intentions of the current aircraft match current taxiroute position of the proximate
-  // aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
-  // instruction. See below for the hold position instruction.
   TrafficVectorIterator current, closest;
   TrafficVectorIterator i = activeTraffic.begin();
   bool otherReasonToSlowDown = false;
+  bool previousInstruction;
   if (activeTraffic.size()) 
     {
       //while ((i->getId() != id) && (i != activeTraffic.end()))
@@ -643,27 +587,27 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
   }
   current = i;
-  double mindist = HUGE;
+  //closest = current;
+  
+  previousInstruction = current->getSpeedAdjustment();
+  double mindist = HUGE_VAL;
   if (activeTraffic.size()) 
     {
-      double course, dist, bearing, minbearing;
-      SGWayPoint curr  (lon,
-                       lat,
-                       alt);
+      double course, dist, bearing, minbearing, az2;
+      SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
       //TrafficVector iterator closest;
       closest = current;
       for (TrafficVectorIterator i = activeTraffic.begin(); 
           i != activeTraffic.end(); i++)
        {
-         if (i != current) {
-           //SGWayPoint curr  (lon,
-           //        lat,
-           //        alt);
-           SGWayPoint other    (i->getLongitude  (),
-                                i->getLatitude (),
-                                i->getAltitude  ());
-           other.CourseAndDistance(curr, &course, &dist);
-           bearing = fabs(heading-course);
+         if (i == current) {
+        continue;
+      }
+      
+        SGGeod other(SGGeod::fromDegM(i->getLongitude(),
+          i->getLatitude(), i->getAltitude()));
+        SGGeodesy::inverse(other, curr, course, az2, dist);
+        bearing = fabs(heading-course);
            if (bearing > 180)
              bearing = 360-bearing;
            if ((dist < mindist) && (bearing < 60.0))
@@ -672,7 +616,6 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
                closest = i;
                minbearing = bearing;
              }
-         }
        }
       //Check traffic at the tower controller
       if (towerController->hasActiveTraffic())
@@ -680,34 +623,28 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
          for (TrafficVectorIterator i = towerController->getActiveTraffic().begin(); 
               i != towerController->getActiveTraffic().end(); i++)
            {
-             if (i != current) {
-               //SGWayPoint curr  (lon,
-               //                lat,
-               //                alt);
-               SGWayPoint other    (i->getLongitude  (),
-                                    i->getLatitude (),
-                                    i->getAltitude  ());
-               other.CourseAndDistance(curr, &course, &dist);
-               bearing = fabs(heading-course);
-               if (bearing > 180)
-                 bearing = 360-bearing;
-               if ((dist < mindist) && (bearing < 60.0))
-                 {
-                   mindist = dist;
-                   closest = i;
-                   minbearing = bearing;
-                   otherReasonToSlowDown = true;
-                 }
-             }
+             //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
+             SGGeod other(SGGeod::fromDegM(i->getLongitude(),
+          i->getLatitude(), i->getAltitude()));
+        SGGeodesy::inverse(other, curr, course, az2, dist);
+        bearing = fabs(heading-course);
+             if (bearing > 180)
+          bearing = 360-bearing;
+             if ((dist < mindist) && (bearing < 60.0))
+               {
+                 mindist = dist;
+                 closest = i;
+                 minbearing = bearing;
+                 otherReasonToSlowDown = true;
+               }
            }
        }
       // Finally, check UserPosition
       double userLatitude  = fgGetDouble("/position/latitude-deg");
       double userLongitude = fgGetDouble("/position/longitude-deg");
-      SGWayPoint user    (userLongitude,
-                         userLatitude,
-                         alt); // Alt is not really important here. 
-      user.CourseAndDistance(curr, &course, &dist);
+      SGGeod user(SGGeod::fromDeg(userLatitude,userLongitude));
+      SGGeodesy::inverse(user, curr, course, az2, dist);
+      
       bearing = fabs(heading-course);
       if (bearing > 180)
        bearing = 360-bearing;
@@ -726,81 +663,46 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
       //cerr << "Distance : " << dist << " bearing : " << bearing << " heading : " << heading 
       //   << " course : " << course << endl;
       current->clearSpeedAdjustment();
-      // Only clear the heading adjustment at positive speeds, otherwise the waypoint following
-      // code wreaks havoc
-      if (speed > 0.2)
-       current->clearHeadingAdjustment();
-      // All clear
-      if (mindist > 100)
-       {
-         //current->clearSpeedAdjustment();
-         //current->clearHeadingAdjustment();
-       } 
-      else
+    
+      if (current->checkPositionAndIntentions(*closest) || otherReasonToSlowDown) 
        {
-         
-         if (current->getId() == closest->getWaitsForId())
-           return;
-         else 
-           current->setWaitsForId(closest->getId());
-         
-         
-         // Getting close: Slow down to a bit less than the other aircraft
-         double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
-         if (mindist > maxAllowableDistance)
-           {
-             if (current->checkPositionAndIntentions(*closest) || otherReasonToSlowDown) 
-               {
-                 // Adjust speed, but don't let it drop to below 1 knots
-                 //if (fabs(speed) > 1)
-                 if (!(current->hasHeadingAdjustment())) 
-                   {
-                     if (closest != current)
-                       current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
-                     else
-                       current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
-                     //cerr << "Adjusting speed to " << closest->getSpeed() * (mindist / 100) << " " 
-                     //         << "Bearing = " << minbearing << " Distance = " << mindist
-                     //         << " Latitude = " <<lat << " longitude = " << lon << endl;
-                     //<< " Latitude = " <<closest->getLatitude() 
-                     //<< " longitude = " << closest->getLongitude() 
-                     //  << endl;
-                   }
-                 else
-                   {
-                     double newSpeed = (maxAllowableDistance-mindist);
-                     current->setSpeedAdjustment(newSpeed);
-                   } 
-               }
-           }
-         else
-           { 
-             if (!(current->hasHeadingAdjustment())) 
-               {
-                 double newSpeed;
-                 if (mindist > 10) {
-                   //  newSpeed = 0.01;
-                   //  current->setSpeedAdjustment(newSpeed);
-                 } else {
-                   newSpeed = -1 * (maxAllowableDistance-mindist);
-                   current->setSpeedAdjustment(newSpeed);
-                   current->setHeadingAdjustment(heading);
-                 }
-               }
-           }
+          double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
+          if (mindist < 2*maxAllowableDistance)
+            {
+              if (current->getId() == closest->getWaitsForId())
+                return;
+              else 
+                current->setWaitsForId(closest->getId());
+              if (closest->getId() != current->getId())
+                current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
+              else
+                current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
+              if (mindist < maxAllowableDistance)
+                {
+                  //double newSpeed = (maxAllowableDistance-mindist);
+                  //current->setSpeedAdjustment(newSpeed);
+                  //if (mindist < 0.5* maxAllowableDistance)
+                  //  {
+                      current->setSpeedAdjustment(0);
+                      //  }
+                }
+            }
        }
     }
 }
 
+/**
+   Check for "Hold position instruction".
+   The hold position should be issued under the following conditions:
+   1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
+   2) For taxiing aircraft that use one taxiway in opposite directions
+   3) For crossing or merging taxiroutes.
+*/
+
 void FGGroundNetwork::checkHoldPosition(int id, double lat, 
                                        double lon, double heading, 
                                        double speed, double alt)
 {
-  // Check for "Hold position instruction".
-  // The hold position should be issued under the following conditions:
-  // 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
-  // 2) For taxiing aircraft that use one taxiway in opposite directions
-  // 3) For crossing or merging taxiroutes.
   
   TrafficVectorIterator current;
   TrafficVectorIterator i = activeTraffic.begin();
@@ -823,37 +725,32 @@ void FGGroundNetwork::checkHoldPosition(int id, double lat,
   }
   current = i;
   current->setHoldPosition(false);
-  SGWayPoint curr  (lon,
-                   lat,
-                   alt);
-  double course, dist, bearing, minbearing;
+  SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
+  
   for (i = activeTraffic.begin(); 
        i != activeTraffic.end(); i++)
     {
-      if (i != current
+      if (i->getId() != current->getId()
        {
          int node = current->crosses(this, *i);
          if (node != -1)
            {
+          FGTaxiNode* taxiNode = findNode(node);
+          
              // Determine whether it's save to continue or not. 
              // If we have a crossing route, there are two possibilities:
              // 1) This is an interestion
              // 2) This is oncoming two-way traffic, using the same taxiway.
              //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
-             SGWayPoint nodePos(findNode(node)->getLongitude  (),
-                                findNode(node)->getLatitude   (),
-                                alt);
-             
-             
-             SGWayPoint other    (i->getLongitude  (),
-                                  i->getLatitude (),
-                                  i->getAltitude  ());
-             //other.CourseAndDistance(curr, &course, &dist);
-             bool needsToWait;
+            
+        SGGeod other(SGGeod::fromDegM(i->getLongitude(), i->getLatitude(), i->getAltitude()));
+        bool needsToWait;
+             bool opposing;
              if (current->isOpposing(this, *i, node))
                {
                  needsToWait = true;
-                 //cerr << "Hold check 2 : " << id << "  has opposing segment " << endl;
+                 opposing    = true;
+                 //cerr << "Hold check 2 : " << node << "  has opposing segment " << endl;
                  // issue a "Hold Position" as soon as we're close to the offending node
                  // For now, I'm doing this as long as the other aircraft doesn't
                  // have a hold instruction as soon as we're within a reasonable 
@@ -864,8 +761,8 @@ void FGGroundNetwork::checkHoldPosition(int id, double lat,
                } 
              else 
                {
-                 other.CourseAndDistance(nodePos, &course, &dist);
-                 if (dist > 2.0*i->getRadius())
+                 opposing = false;
+                 if (SGGeodesy::distanceM(other, taxiNode->getGeod()) > 200) // 2.0*i->getRadius())
                    {
                      needsToWait = false; 
                      //cerr << "Hold check 3 : " << id <<"  Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue " 
@@ -874,21 +771,34 @@ void FGGroundNetwork::checkHoldPosition(int id, double lat,
                  else 
                    {
                      needsToWait = true;
-                     //cerr << "Hold check 4: " << id << "  Would need to wait for other aircraft : distance = " << dist << " nm" << endl;
+                     //cerr << "Hold check 4: " << id << "  Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
                    }
                }
-             curr.CourseAndDistance(nodePos, &course, &dist);
-             if (!(i->hasHoldPosition()))
+      
+      double dist = SGGeodesy::distanceM(curr, taxiNode->getGeod());
+      if (!(i->hasHoldPosition()))
                {
                  
-                 if ((dist < 2.5*current->getRadius()) && 
-                     (needsToWait) && 
-                     (!(current->getId() == i->getWaitsForId())) &&
-                     (!(current->getSpeedAdjustment())))
+                 if ((dist < 200) && //2.5*current->getRadius()) && 
+                     (needsToWait) &&
+                     (i->onRoute(this, *current)) &&
+                     //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
+                     (!(current->getId() == i->getWaitsForId())))
+                     //(!(i->getSpeedAdjustment()))) // &&
+                     //(!(current->getSpeedAdjustment())))
                    
                    {
                      current->setHoldPosition(true);
-                     //cerr << "Hold check 5: " << id <<"  Setting Hold Position: distance to node : " << dist << " nm"<< endl;
+                     current->setWaitsForId(i->getId());
+                     //cerr << "Hold check 5: " << current->getCallSign() <<"  Setting Hold Position: distance to node ("  << node << ") "
+                     //           << dist << " meters. Waiting for " << i->getCallSign();
+                     //if (opposing)
+                     //cerr <<" [opposing] " << endl;
+                     //else
+                     //        cerr << "[non-opposing] " << endl;
+                     //if (i->hasSpeefAdjustment())
+                     //        {
+                     //          cerr << " (which in turn waits for ) " << i->
                    }
                  else
                    {
@@ -900,6 +810,113 @@ void FGGroundNetwork::checkHoldPosition(int id, double lat,
     }
 }
 
+/**
+ * Check whether situations occur where the current aircraft is waiting for itself
+ * due to higher order interactions. 
+ * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
+ * for a. Ideally each aircraft only waits for one other aircraft, so by tracing 
+ * through this list of waiting aircraft, we can check if we'd eventually end back 
+ * at the current aircraft.
+ *
+ * Note that we should consider the situation where we are actually checking aircraft
+ * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
+ * the looping aircraft. If we don't check for that, this function will get stuck into
+ * endless loop.
+ */
+
+bool FGGroundNetwork::checkForCircularWaits(int id)
+{  
+   //cerr << "Performing Wait check " << id << endl;
+   int target = 0;
+   TrafficVectorIterator current, other;
+   TrafficVectorIterator i = activeTraffic.begin();
+   int trafficSize = activeTraffic.size();
+   if (trafficSize)  {
+        while (i != activeTraffic.end()) {
+        if (i->getId() == id) {
+           break;
+       }
+       i++;
+    }
+  }
+  else {
+      return false;
+  }
+  if (i == activeTraffic.end() || (trafficSize == 0)) {
+    SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
+  }
+   
+   current = i;
+   target = current->getWaitsForId();
+   //bool printed = false; // Note that this variable is for debugging purposes only.
+   int counter = 0;
+
+   if (id == target) {
+       //cerr << "aircraft waits for user" << endl;
+       return false;
+   }
+
+
+   while ((target > 0) && (target != id) && counter++ < trafficSize) {
+    //printed = true;
+     TrafficVectorIterator i = activeTraffic.begin();
+     if (trafficSize)  {
+          //while ((i->getId() != id) && i != activeTraffic.end()) 
+          while (i != activeTraffic.end()) {
+          if (i->getId() == target) {
+             break;
+         }
+         i++;
+        }
+      }
+      else {
+        return false;
+    } 
+    if (i == activeTraffic.end() || (trafficSize == 0)) {
+       //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
+      // The target id is not found on the current network, which means it's at the tower
+      //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
+      return false;
+    }
+    other = i;
+    target = other->getWaitsForId();
+
+    // actually this trap isn't as impossible as it first seemed:
+    // the setWaitsForID(id) is set to current when the aircraft
+    // is waiting for the user controlled aircraft. 
+    //if (current->getId() == other->getId()) {
+    //    cerr << "Caught the impossible trap" << endl;
+    //    cerr << "Current = " << current->getId() << endl;
+    //    cerr << "Other   = " << other  ->getId() << endl;
+    //    for (TrafficVectorIterator at = activeTraffic.begin();
+    //          at != activeTraffic.end();
+    //          at++) {
+    //        cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
+    //    }
+    //    exit(1);
+    if (current->getId() == other->getId())
+       return false;
+    //}
+    //cerr << current->getCallSign() << " (" << current->getId()  << ") " << " -> " << other->getCallSign() 
+    //     << " (" << other->getId()  << "); " << endl;;
+    //current = other;
+   }
+
+
+
+
+
+
+   //if (printed)
+   //   cerr << "[done] " << endl << endl;;
+   if (id == target) {
+       SG_LOG(SG_GENERAL, SG_WARN, "Detected circular wait condition: Id = " << id << "target = " << target);
+       return true;
+   } else {
+   return false;
+   }
+}
+
 // Note that this function is probably obsolete...
 bool FGGroundNetwork::hasInstruction(int id)
 {
@@ -946,3 +963,4 @@ FGATCInstruction FGGroundNetwork::getInstruction(int id)
   return FGATCInstruction();
 }
 
+