]> git.mxchange.org Git - flightgear.git/blob - src/Airports/groundnetwork.cxx
Merge branch 'vivian/mp'
[flightgear.git] / src / Airports / groundnetwork.cxx
1 // groundnet.cxx - Implimentation of the FlightGear airport ground handling code
2 //
3 // Written by Durk Talsma, started June 2005.
4 //
5 // Copyright (C) 2004 Durk Talsma.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #include <math.h>
28 #include <algorithm>
29
30 #include <simgear/debug/logstream.hxx>
31 #include <simgear/route/waypoint.hxx>
32
33 #include <Airports/dynamics.hxx>
34
35 #include <AIModel/AIFlightPlan.hxx>
36
37 #include "groundnetwork.hxx"
38
39 /***************************************************************************
40  * FGTaxiSegment
41  **************************************************************************/
42
43 void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
44 {
45   FGTaxiNodeVectorIterator i = nodes->begin();
46   while (i != nodes->end())
47     {
48       //cerr << "Scanning start node index" << (*i)->getIndex() << endl;
49       if ((*i)->getIndex() == startNode)
50         {
51           start = (*i)->getAddress();
52           (*i)->addSegment(this);
53           return;
54         }
55       i++;
56     }
57   SG_LOG(SG_GENERAL, SG_ALERT,  "Could not find start node " << startNode << endl);
58 }
59
60 void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
61 {
62   FGTaxiNodeVectorIterator i = nodes->begin();
63   while (i != nodes->end())
64     {
65       //cerr << "Scanning end node index" << (*i)->getIndex() << endl;
66       if ((*i)->getIndex() == endNode)
67         {
68           end = (*i)->getAddress();
69           return;
70         }
71       i++;
72     }
73   SG_LOG(SG_GENERAL, SG_ALERT,  "Could not find end node " << endNode << endl);
74 }
75
76
77
78 // There is probably a computationally cheaper way of 
79 // doing this.
80 void FGTaxiSegment::setTrackDistance()
81 {
82   length = SGGeodesy::distanceM(start->getGeod(), end->getGeod());
83 }
84
85
86 void FGTaxiSegment::setCourseDiff(double crse)
87 {
88   headingDiff = fabs(course-crse);
89   
90   if (headingDiff > 180)
91     headingDiff = fabs(headingDiff - 360);
92 }
93
94
95 /***************************************************************************
96  * FGTaxiRoute
97  **************************************************************************/
98 bool FGTaxiRoute::next(int *nde) 
99
100   //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
101   //  cerr << "FGTaxiRoute contains : " << *(i) << endl;
102   //cerr << "Offset from end: " << nodes.end() - currNode << endl;
103   //if (currNode != nodes.end())
104   //  cerr << "true" << endl;
105   //else
106   //  cerr << "false" << endl;
107   //if (nodes.size() != (routes.size()) +1)
108   //  cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
109       
110   if (currNode == nodes.end())
111     return false;
112   *nde = *(currNode); 
113   if (currNode != nodes.begin()) // make sure route corresponds to the end node
114     currRoute++;
115   currNode++;
116   return true;
117 };
118
119 bool FGTaxiRoute::next(int *nde, int *rte) 
120
121   //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
122   //  cerr << "FGTaxiRoute contains : " << *(i) << endl;
123   //cerr << "Offset from end: " << nodes.end() - currNode << endl;
124   //if (currNode != nodes.end())
125   //  cerr << "true" << endl;
126   //else
127   //  cerr << "false" << endl;
128   if (nodes.size() != (routes.size()) +1) {
129     SG_LOG(SG_GENERAL, SG_ALERT, "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size());
130     exit(1);
131   }
132   if (currNode == nodes.end())
133     return false;
134   *nde = *(currNode); 
135   //*rte = *(currRoute);
136   if (currNode != nodes.begin()) // Make sure route corresponds to the end node
137     {
138       *rte = *(currRoute);
139       currRoute++;
140     }
141   else
142     {
143       // If currNode points to the first node, this means the aircraft is not on the taxi node
144       // yet. Make sure to return a unique identifyer in this situation though, because otherwise
145       // the speed adjust AI code may be unable to resolve whether two aircraft are on the same 
146       // taxi route or not. the negative of the preceding route seems a logical choice, as it is 
147       // unique for any starting location. 
148       // Note that this is probably just a temporary fix until I get Parking / tower control working.
149       *rte = -1 * *(currRoute); 
150     }
151   currNode++;
152   return true;
153 };
154
155 void FGTaxiRoute::rewind(int route)
156 {
157   int currPoint;
158   int currRoute;
159   first();
160   do {
161     if (!(next(&currPoint, &currRoute))) { 
162       SG_LOG(SG_GENERAL,SG_ALERT, "Error in rewinding TaxiRoute: current" << currRoute 
163              << " goal " << route);
164     }
165   } while (currRoute != route);
166 }
167
168
169
170
171 /***************************************************************************
172  * FGGroundNetwork()
173  **************************************************************************/
174 bool compare_nodes(FGTaxiNode *a, FGTaxiNode *b) {
175 return (*a) < (*b);
176 }
177
178 bool compare_segments(FGTaxiSegment *a, FGTaxiSegment *b) {
179 return (*a) < (*b);
180 }
181
182 FGGroundNetwork::FGGroundNetwork()
183 {
184   hasNetwork = false;
185   foundRoute = false;
186   totalDistance = 0;
187   maxDistance = 0;
188   //maxDepth    = 1000;
189   count       = 0;
190   currTraffic = activeTraffic.begin();
191
192 }
193
194 FGGroundNetwork::~FGGroundNetwork()
195 {
196   for (FGTaxiNodeVectorIterator node = nodes.begin();
197        node != nodes.end();
198        node++)
199     {
200       delete (*node);
201     }
202   nodes.clear();
203   pushBackNodes.clear();
204   for (FGTaxiSegmentVectorIterator seg = segments.begin();
205        seg != segments.end();
206        seg++)
207     {
208       delete (*seg);
209     }
210   segments.clear();
211 }
212
213 void FGGroundNetwork::addSegment(const FGTaxiSegment &seg)
214 {
215   segments.push_back(new FGTaxiSegment(seg));
216 }
217
218 void FGGroundNetwork::addNode(const FGTaxiNode &node)
219 {
220   nodes.push_back(new FGTaxiNode(node));
221 }
222
223 void FGGroundNetwork::addNodes(FGParkingVec *parkings)
224 {
225   FGTaxiNode n;
226   FGParkingVecIterator i = parkings->begin();
227   while (i != parkings->end())
228     {
229       n.setIndex(i->getIndex());
230       n.setLatitude(i->getLatitude());
231       n.setLongitude(i->getLongitude());
232       nodes.push_back(new FGTaxiNode(n));
233
234       i++;
235     }
236 }
237
238
239
240 void FGGroundNetwork::init()
241 {
242   hasNetwork = true;
243   int index = 1;
244   sort(nodes.begin(), nodes.end(), compare_nodes);
245   //sort(segments.begin(), segments.end(), compare_segments());
246   FGTaxiSegmentVectorIterator i = segments.begin();
247   while(i != segments.end()) {
248     (*i)->setStart(&nodes);
249     (*i)->setEnd  (&nodes);
250     (*i)->setTrackDistance();
251     (*i)->setIndex(index);
252     if ((*i)->isPushBack()) {
253           pushBackNodes.push_back((*i)->getEnd());
254     }
255     //SG_LOG(SG_GENERAL, SG_BULK,  "initializing segment " << (*i)->getIndex() << endl);
256     //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = "     << (*i)->getLength() << endl);
257     //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from "      << (*i)->getStart()->getIndex() << " to "
258     //                                                    << (*i)->getEnd()->getIndex() << endl);
259     i++;
260     index++;
261   }
262  
263   i = segments.begin();
264   while(i != segments.end()) {
265     FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute(); 
266     while (j != (*i)->getEnd()->getEndRoute())
267       {
268         if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex())
269           {
270 //          int start1 = (*i)->getStart()->getIndex();
271 //          int end1   = (*i)->getEnd()  ->getIndex();
272 //          int start2 = (*j)->getStart()->getIndex();
273 //          int end2   = (*j)->getEnd()->getIndex();
274 //          int oppIndex = (*j)->getIndex();
275             //cerr << "Opposite of  " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
276             //   << "happens to be " << oppIndex      << " (" << start2 << "," << end2 << ") " << endl;
277             (*i)->setOpposite(*j);
278             break;
279           }
280           j++;
281       }
282     i++;
283   }
284   //FGTaxiNodeVectorIterator j = nodes.begin();
285   //while (j != nodes.end()) {
286   //    if ((*j)->getHoldPointType() == 3) {
287   //        pushBackNodes.push_back((*j));
288   //    }
289   //    j++;
290   //}
291   //cerr << "Done initializing ground network" << endl;
292   //exit(1);
293 }
294
295 int FGGroundNetwork::findNearestNode(const SGGeod& aGeod)
296 {
297   double minDist = HUGE_VAL;
298   int index = -1;
299   
300   for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end(); itr++)
301   {
302     double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod());
303     if (d < minDist)
304     {
305       minDist = d;
306       index = (*itr)->getIndex();
307       //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
308     }
309   }
310   
311   return index;
312 }
313
314 int FGGroundNetwork::findNearestNode(double lat, double lon)
315 {
316   return findNearestNode(SGGeod::fromDeg(lon, lat));
317 }
318
319 FGTaxiNode *FGGroundNetwork::findNode(unsigned idx)
320 { /*
321     for (FGTaxiNodeVectorIterator 
322     itr = nodes.begin();
323     itr != nodes.end(); itr++)
324     {
325     if (itr->getIndex() == idx)
326     return itr->getAddress();
327     }*/
328   
329   if ((idx >= 0) && (idx < nodes.size())) 
330     return nodes[idx]->getAddress();
331   else
332     return 0;
333 }
334
335 FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx)
336 {/*
337   for (FGTaxiSegmentVectorIterator 
338          itr = segments.begin();
339        itr != segments.end(); itr++)
340     {
341       if (itr->getIndex() == idx)
342         return itr->getAddress();
343     } 
344  */
345   if ((idx > 0) && (idx <= segments.size()))
346     return segments[idx-1]->getAddress();
347   else
348     {
349       //cerr << "Alert: trying to find invalid segment " << idx << endl;
350       return 0;
351     }
352 }
353
354
355 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, bool fullSearch) 
356 {
357 //implements Dijkstra's algorithm to find shortest distance route from start to end
358 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
359
360     //double INFINITE = 100000000000.0;
361     // initialize scoring values
362     int nParkings = parent->getDynamics()->getNrOfParkings();
363     FGTaxiNodeVector *currNodesSet;
364     if (fullSearch) {
365          currNodesSet = &nodes;
366     } else {
367          currNodesSet = &pushBackNodes;
368     }
369
370     for (FGTaxiNodeVectorIterator
371          itr = currNodesSet->begin();
372          itr != currNodesSet->end(); itr++) {
373             (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
374             (*itr)->setPreviousNode(0); //
375             (*itr)->setPreviousSeg (0); //
376          }
377
378     FGTaxiNode *firstNode = findNode(start);
379     firstNode->setPathScore(0);
380
381     FGTaxiNode *lastNode  = findNode(end);
382
383     FGTaxiNodeVector unvisited(*currNodesSet); // working copy
384
385     while (!unvisited.empty()) {
386         FGTaxiNode* best = *(unvisited.begin());
387         for (FGTaxiNodeVectorIterator
388              itr = unvisited.begin();
389              itr != unvisited.end(); itr++) {
390                  if ((*itr)->getPathScore() < best->getPathScore())
391                      best = (*itr);
392         }
393
394         FGTaxiNodeVectorIterator newend = remove(unvisited.begin(), unvisited.end(), best);
395         unvisited.erase(newend, unvisited.end());
396         
397         if (best == lastNode) { // found route or best not connected
398             break;
399         } else {
400             for (FGTaxiSegmentVectorIterator
401                  seg = best->getBeginRoute();
402                  seg != best->getEndRoute(); seg++) {
403                 if (fullSearch || (*seg)->isPushBack()) {
404                     FGTaxiNode* tgt = (*seg)->getEnd();
405                     double alt = best->getPathScore() + (*seg)->getLength() + (*seg)->getPenalty(nParkings);
406                     if (alt < tgt->getPathScore()) {              // Relax (u,v)
407                         tgt->setPathScore(alt);
408                         tgt->setPreviousNode(best);
409                         tgt->setPreviousSeg(*seg); //
410                    }
411                 } else {
412                 //   // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
413                 }
414             }
415         }
416     }
417
418     if (lastNode->getPathScore() == HUGE_VAL) {
419         // no valid route found
420         if (fullSearch) {
421             SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find route from waypoint " << start << " to " << end << " at " <<
422                     parent->getId());
423         }
424         FGTaxiRoute empty;
425         return empty;
426         //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
427     } else {
428         // assemble route from backtrace information
429         intVec nodes, routes;
430         FGTaxiNode* bt = lastNode;
431         while (bt->getPreviousNode() != 0) {
432             nodes.push_back(bt->getIndex());
433             routes.push_back(bt->getPreviousSegment()->getIndex());
434             bt = bt->getPreviousNode();
435         }
436         nodes.push_back(start);
437         reverse(nodes.begin(), nodes.end());
438         reverse(routes.begin(), routes.end());
439
440         return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
441     }
442 }
443
444 int FGTaxiSegment::getPenalty(int nGates) {
445      int penalty = 0;
446      if (end->getIndex() < nGates) {
447          penalty += 10000;
448      }
449      if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
450          penalty += 1000;
451      }
452      return penalty;
453 }
454
455 /* ATC Related Functions */
456
457 void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
458                                        double lat, double lon, double heading, 
459                                        double speed, double alt, double radius, int leg,
460                                        FGAIAircraft *aircraft)
461 {
462    TrafficVectorIterator i = activeTraffic.begin();
463    // Search search if the current id alread has an entry
464    // This might be faster using a map instead of a vector, but let's start by taking a safe route
465    if (activeTraffic.size()) {
466      //while ((i->getId() != id) && i != activeTraffic.end()) {
467      while (i != activeTraffic.end()) {
468        if (i->getId() == id) {
469          break;
470        }
471        i++;
472      }
473    }
474    // Add a new TrafficRecord if no one exsists for this aircraft.
475    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
476      FGTrafficRecord rec;
477      rec.setId(id);
478      rec.setPositionAndIntentions(currentPosition, intendedRoute);
479      rec.setPositionAndHeading(lat, lon, heading, speed, alt);
480      rec.setRadius(radius); // only need to do this when creating the record.
481      rec.setAircraft(aircraft);
482      activeTraffic.push_back(rec);
483    } else {
484      i->setPositionAndIntentions(currentPosition, intendedRoute); 
485      i->setPositionAndHeading(lat, lon, heading, speed, alt);
486    }
487 }
488
489 void FGGroundNetwork::signOff(int id) {
490   TrafficVectorIterator i = activeTraffic.begin();
491    // Search search if the current id alread has an entry
492    // This might be faster using a map instead of a vector, but let's start by taking a safe route
493    if (activeTraffic.size()) {
494      //while ((i->getId() != id) && i != activeTraffic.end()) {
495      while (i != activeTraffic.end()) {
496        if (i->getId() == id) {
497          break;
498        }
499        i++;
500      }
501    }
502    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
503      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off");
504    } else {
505        i = activeTraffic.erase(i);
506    }
507 }
508
509 void FGGroundNetwork::update(int id, double lat, double lon, double heading, double speed, double alt, 
510                              double dt) {
511    // Check whether aircraft are on hold due to a preceding pushback. If so, make sure to 
512    // Transmit air-to-ground "Ready to taxi request:
513    // Transmit ground to air approval / hold
514    // Transmit confirmation ... 
515    // Probably use a status mechanism similar to the Engine start procedure in the startup controller.
516
517
518    TrafficVectorIterator i = activeTraffic.begin();
519    // Search search if the current id has an entry
520    // This might be faster using a map instead of a vector, but let's start by taking a safe route
521    TrafficVectorIterator current, closest;
522    if (activeTraffic.size()) {
523      //while ((i->getId() != id) && i != activeTraffic.end()) {
524      while (i != activeTraffic.end()) {
525        if (i->getId() == id) {
526          break;
527        }
528        i++;
529      }
530    }
531    // update position of the current aircraft
532    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
533      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
534    } else {
535      i->setPositionAndHeading(lat, lon, heading, speed, alt);
536      current = i;
537    }
538   
539    setDt(getDt() + dt);
540   
541    // Update every three secs, but add some randomness
542    // to prevent all IA objects doing this in synchrony
543    //if (getDt() < (3.0) + (rand() % 10))
544    //  return;
545    //else
546    //  setDt(0);
547    current->clearResolveCircularWait();
548    current->setWaitsForId(0);
549    checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
550    checkHoldPosition   (id, lat, lon, heading, speed, alt);
551    if (checkForCircularWaits(id)) {
552        i->setResolveCircularWait();
553    }
554 }
555
556 /**
557    Scan for a speed adjustment change. Find the nearest aircraft that is in front
558    and adjust speed when we get too close. Only do this when current position and/or
559    intentions of the current aircraft match current taxiroute position of the proximate
560    aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
561    instruction. See below for the hold position instruction.
562
563    Note that there currently still is one flaw in the logic that needs to be addressed. 
564    There can be situations where one aircraft is in front of the current aircraft, on a separate
565    route, but really close after an intersection coming off the current route. This
566    aircraft is still close enough to block the current aircraft. This situation is currently
567    not addressed yet, but should be.
568 */
569
570 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat, 
571                                            double lon, double heading, 
572                                            double speed, double alt)
573 {
574   
575   TrafficVectorIterator current, closest;
576   TrafficVectorIterator i = activeTraffic.begin();
577   bool otherReasonToSlowDown = false;
578   bool previousInstruction;
579   if (activeTraffic.size()) 
580     {
581       //while ((i->getId() != id) && (i != activeTraffic.end()))
582       while (i != activeTraffic.end()) {
583         if (i->getId() == id) {
584           break;
585         }
586         i++;
587       }
588     }
589   else
590     {
591       return;
592     }
593   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
594     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
595   }
596   current = i;
597   //closest = current;
598   
599   previousInstruction = current->getSpeedAdjustment();
600   double mindist = HUGE_VAL;
601   if (activeTraffic.size()) 
602     {
603       double course, dist, bearing, minbearing, az2;
604       SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
605       //TrafficVector iterator closest;
606       closest = current;
607       for (TrafficVectorIterator i = activeTraffic.begin(); 
608            i != activeTraffic.end(); i++)
609         {
610           if (i == current) {
611         continue;
612       }
613       
614         SGGeod other(SGGeod::fromDegM(i->getLongitude(),
615           i->getLatitude(), i->getAltitude()));
616         SGGeodesy::inverse(curr, other, course, az2, dist);
617         bearing = fabs(heading-course);
618             if (bearing > 180)
619               bearing = 360-bearing;
620             if ((dist < mindist) && (bearing < 60.0))
621               {
622                 mindist = dist;
623                 closest = i;
624                 minbearing = bearing;
625               }
626         }
627       //Check traffic at the tower controller
628       if (towerController->hasActiveTraffic())
629         {
630           for (TrafficVectorIterator i = towerController->getActiveTraffic().begin(); 
631                i != towerController->getActiveTraffic().end(); i++)
632             {
633               //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
634               SGGeod other(SGGeod::fromDegM(i->getLongitude(),
635                                             i->getLatitude(), 
636                                             i->getAltitude()));
637         SGGeodesy::inverse(curr, other, course, az2, dist);
638         bearing = fabs(heading-course);
639               if (bearing > 180)
640           bearing = 360-bearing;
641               if ((dist < mindist) && (bearing < 60.0))
642                 {
643                   mindist = dist;
644                   closest = i;
645                   minbearing = bearing;
646                   otherReasonToSlowDown = true;
647                 }
648             }
649         }
650       // Finally, check UserPosition
651       double userLatitude  = fgGetDouble("/position/latitude-deg");
652       double userLongitude = fgGetDouble("/position/longitude-deg");
653       SGGeod user(SGGeod::fromDeg(userLongitude,userLatitude));
654       SGGeodesy::inverse(curr, user, course, az2, dist);
655
656       bearing = fabs(heading-course);
657       if (bearing > 180)
658       bearing = 360-bearing;
659       if ((dist < mindist) && (bearing < 60.0))
660         {
661           mindist = dist;
662           //closest = i;
663           minbearing = bearing;
664           otherReasonToSlowDown = true;
665         }
666
667       current->clearSpeedAdjustment();
668     
669       if (current->checkPositionAndIntentions(*closest) || otherReasonToSlowDown) 
670         {
671            double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
672            if (mindist < 2*maxAllowableDistance)
673              {
674                if (current->getId() == closest->getWaitsForId())
675                  return;
676                else 
677                  current->setWaitsForId(closest->getId());
678                if (closest->getId() != current->getId())
679                  current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
680                else
681                  current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
682                if (mindist < maxAllowableDistance)
683                  {
684                    //double newSpeed = (maxAllowableDistance-mindist);
685                    //current->setSpeedAdjustment(newSpeed);
686                    //if (mindist < 0.5* maxAllowableDistance)
687                    //  {
688                        current->setSpeedAdjustment(0);
689                        //  }
690                  }
691              }
692         }
693     }
694 }
695
696 /**
697    Check for "Hold position instruction".
698    The hold position should be issued under the following conditions:
699    1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
700    2) For taxiing aircraft that use one taxiway in opposite directions
701    3) For crossing or merging taxiroutes.
702 */
703
704 void FGGroundNetwork::checkHoldPosition(int id, double lat, 
705                                         double lon, double heading, 
706                                         double speed, double alt)
707 {
708   
709   TrafficVectorIterator current;
710   TrafficVectorIterator i = activeTraffic.begin();
711   if (activeTraffic.size()) 
712     {
713       //while ((i->getId() != id) && i != activeTraffic.end()) 
714       while (i != activeTraffic.end()) {
715         if (i->getId() == id) {
716           break;
717         }
718         i++;
719       }
720     }
721   else
722     {
723       return ;
724     } 
725   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
726     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
727   }
728   current = i;
729   current->setHoldPosition(false);
730   SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
731   
732   for (i = activeTraffic.begin(); 
733        i != activeTraffic.end(); i++)
734     {
735       if (i->getId() != current->getId()) 
736         {
737           int node = current->crosses(this, *i);
738           if (node != -1)
739             {
740           FGTaxiNode* taxiNode = findNode(node);
741           
742               // Determine whether it's save to continue or not. 
743               // If we have a crossing route, there are two possibilities:
744               // 1) This is an interestion
745               // 2) This is oncoming two-way traffic, using the same taxiway.
746               //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
747              
748         SGGeod other(SGGeod::fromDegM(i->getLongitude(), i->getLatitude(), i->getAltitude()));
749         bool needsToWait;
750               bool opposing;
751               if (current->isOpposing(this, *i, node))
752                 {
753                   needsToWait = true;
754                   opposing    = true;
755                   //cerr << "Hold check 2 : " << node << "  has opposing segment " << endl;
756                   // issue a "Hold Position" as soon as we're close to the offending node
757                   // For now, I'm doing this as long as the other aircraft doesn't
758                   // have a hold instruction as soon as we're within a reasonable 
759                   // distance from the offending node.
760                   // This may be a bit of a conservative estimate though, as it may
761                   // be well possible that both aircraft can both continue to taxi 
762                   // without crashing into each other.
763                 } 
764               else 
765                 {
766                   opposing = false;
767                   if (SGGeodesy::distanceM(other, taxiNode->getGeod()) > 200) // 2.0*i->getRadius())
768                     {
769                       needsToWait = false; 
770                       //cerr << "Hold check 3 : " << id <<"  Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue " 
771                       //           << endl;
772                     }
773                   else 
774                     {
775                       needsToWait = true;
776                       //cerr << "Hold check 4: " << id << "  Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
777                     }
778                 }
779       
780       double dist = SGGeodesy::distanceM(curr, taxiNode->getGeod());
781       if (!(i->hasHoldPosition()))
782                 {
783                   
784                   if ((dist < 200) && //2.5*current->getRadius()) && 
785                       (needsToWait) &&
786                       (i->onRoute(this, *current)) &&
787                       //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
788                       (!(current->getId() == i->getWaitsForId())))
789                       //(!(i->getSpeedAdjustment()))) // &&
790                       //(!(current->getSpeedAdjustment())))
791                     
792                     {
793                       current->setHoldPosition(true);
794                       current->setWaitsForId(i->getId());
795                       //cerr << "Hold check 5: " << current->getCallSign() <<"  Setting Hold Position: distance to node ("  << node << ") "
796                       //           << dist << " meters. Waiting for " << i->getCallSign();
797                       //if (opposing)
798                       //cerr <<" [opposing] " << endl;
799                       //else
800                       //        cerr << "[non-opposing] " << endl;
801                       //if (i->hasSpeefAdjustment())
802                       //        {
803                       //          cerr << " (which in turn waits for ) " << i->
804                     }
805                   else
806                     {
807                       //cerr << "Hold check 6: " << id << "  No need to hold yet: Distance to node : " << dist << " nm"<< endl;
808                     }
809                 }
810             }
811         }
812     }
813 }
814
815 /**
816  * Check whether situations occur where the current aircraft is waiting for itself
817  * due to higher order interactions. 
818  * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
819  * for a. Ideally each aircraft only waits for one other aircraft, so by tracing 
820  * through this list of waiting aircraft, we can check if we'd eventually end back 
821  * at the current aircraft.
822  *
823  * Note that we should consider the situation where we are actually checking aircraft
824  * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
825  * the looping aircraft. If we don't check for that, this function will get stuck into
826  * endless loop.
827  */
828
829 bool FGGroundNetwork::checkForCircularWaits(int id)
830 {  
831    //cerr << "Performing Wait check " << id << endl;
832    int target = 0;
833    TrafficVectorIterator current, other;
834    TrafficVectorIterator i = activeTraffic.begin();
835    int trafficSize = activeTraffic.size();
836    if (trafficSize)  {
837         while (i != activeTraffic.end()) {
838         if (i->getId() == id) {
839             break;
840         }
841         i++;
842     }
843   }
844   else {
845       return false;
846   }
847   if (i == activeTraffic.end() || (trafficSize == 0)) {
848     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
849   }
850    
851    current = i;
852    target = current->getWaitsForId();
853    //bool printed = false; // Note that this variable is for debugging purposes only.
854    int counter = 0;
855
856    if (id == target) {
857        //cerr << "aircraft waits for user" << endl;
858        return false;
859    }
860
861
862    while ((target > 0) && (target != id) && counter++ < trafficSize) {
863     //printed = true;
864      TrafficVectorIterator i = activeTraffic.begin();
865      if (trafficSize)  {
866           //while ((i->getId() != id) && i != activeTraffic.end()) 
867           while (i != activeTraffic.end()) {
868           if (i->getId() == target) {
869               break;
870           }
871           i++;
872         }
873       }
874       else {
875         return false;
876     } 
877     if (i == activeTraffic.end() || (trafficSize == 0)) {
878         //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
879       // The target id is not found on the current network, which means it's at the tower
880       //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
881       return false;
882     }
883     other = i;
884     target = other->getWaitsForId();
885
886     // actually this trap isn't as impossible as it first seemed:
887     // the setWaitsForID(id) is set to current when the aircraft
888     // is waiting for the user controlled aircraft. 
889     //if (current->getId() == other->getId()) {
890     //    cerr << "Caught the impossible trap" << endl;
891     //    cerr << "Current = " << current->getId() << endl;
892     //    cerr << "Other   = " << other  ->getId() << endl;
893     //    for (TrafficVectorIterator at = activeTraffic.begin();
894     //          at != activeTraffic.end();
895     //          at++) {
896     //        cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
897     //    }
898     //    exit(1);
899     if (current->getId() == other->getId())
900         return false;
901     //}
902     //cerr << current->getCallSign() << " (" << current->getId()  << ") " << " -> " << other->getCallSign() 
903     //     << " (" << other->getId()  << "); " << endl;;
904     //current = other;
905    }
906
907
908
909
910
911
912    //if (printed)
913    //   cerr << "[done] " << endl << endl;;
914    if (id == target) {
915        SG_LOG(SG_GENERAL, SG_WARN, "Detected circular wait condition: Id = " << id << "target = " << target);
916        return true;
917    } else {
918    return false;
919    }
920 }
921
922 // Note that this function is probably obsolete...
923 bool FGGroundNetwork::hasInstruction(int id)
924 {
925     TrafficVectorIterator i = activeTraffic.begin();
926    // Search search if the current id has an entry
927    // This might be faster using a map instead of a vector, but let's start by taking a safe route
928    if (activeTraffic.size()) 
929      {
930        //while ((i->getId() != id) && i != activeTraffic.end()) {
931        while (i != activeTraffic.end()) {
932          if (i->getId() == id) {
933            break;
934          }
935          i++;
936        }
937      }
938    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
939      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
940    } else {
941      return i->hasInstruction();
942    }
943   return false;
944 }
945
946 FGATCInstruction FGGroundNetwork::getInstruction(int id)
947 {
948   TrafficVectorIterator i = activeTraffic.begin();
949   // Search search if the current id has an entry
950    // This might be faster using a map instead of a vector, but let's start by taking a safe route
951    if (activeTraffic.size()) {
952      //while ((i->getId() != id) && i != activeTraffic.end()) {
953      while (i != activeTraffic.end()) {
954        if (i->getId() == id) {
955          break;
956        }
957        i++;
958      }
959    }
960    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
961      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
962    } else {
963      return i->getInstruction();
964    }
965   return FGATCInstruction();
966 }
967
968