]> git.mxchange.org Git - flightgear.git/blob - src/Airports/groundnetwork.cxx
Two minor (codewise) fixes to ensure everything works as advertised:
[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    TrafficVectorIterator i = activeTraffic.begin();
512    // Search search if the current id has an entry
513    // This might be faster using a map instead of a vector, but let's start by taking a safe route
514    TrafficVectorIterator current, closest;
515    if (activeTraffic.size()) {
516      //while ((i->getId() != id) && i != activeTraffic.end()) {
517      while (i != activeTraffic.end()) {
518        if (i->getId() == id) {
519          break;
520        }
521        i++;
522      }
523    }
524    // update position of the current aircraft
525    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
526      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
527    } else {
528      i->setPositionAndHeading(lat, lon, heading, speed, alt);
529      current = i;
530    }
531   
532    setDt(getDt() + dt);
533   
534    // Update every three secs, but add some randomness
535    // to prevent all IA objects doing this in synchrony
536    //if (getDt() < (3.0) + (rand() % 10))
537    //  return;
538    //else
539    //  setDt(0);
540    current->clearResolveCircularWait();
541    current->setWaitsForId(0);
542    checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
543    checkHoldPosition   (id, lat, lon, heading, speed, alt);
544    if (checkForCircularWaits(id)) {
545        i->setResolveCircularWait();
546    }
547 }
548
549 /**
550    Scan for a speed adjustment change. Find the nearest aircraft that is in front
551    and adjust speed when we get too close. Only do this when current position and/or
552    intentions of the current aircraft match current taxiroute position of the proximate
553    aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
554    instruction. See below for the hold position instruction.
555
556    Note that there currently still is one flaw in the logic that needs to be addressed. 
557    can be situations where one aircraft is in front of the current aircraft, on a separate
558    route, but really close after an intersection coming off the current route. This
559    aircraft is still close enough to block the current aircraft. This situation is currently
560    not addressed yet, but should be.
561 */
562
563 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat, 
564                                            double lon, double heading, 
565                                            double speed, double alt)
566 {
567   
568   TrafficVectorIterator current, closest;
569   TrafficVectorIterator i = activeTraffic.begin();
570   bool otherReasonToSlowDown = false;
571   bool previousInstruction;
572   if (activeTraffic.size()) 
573     {
574       //while ((i->getId() != id) && (i != activeTraffic.end()))
575       while (i != activeTraffic.end()) {
576         if (i->getId() == id) {
577           break;
578         }
579         i++;
580       }
581     }
582   else
583     {
584       return;
585     }
586   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
587     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
588   }
589   current = i;
590   //closest = current;
591   
592   previousInstruction = current->getSpeedAdjustment();
593   double mindist = HUGE_VAL;
594   if (activeTraffic.size()) 
595     {
596       double course, dist, bearing, minbearing, az2;
597       SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
598       //TrafficVector iterator closest;
599       closest = current;
600       for (TrafficVectorIterator i = activeTraffic.begin(); 
601            i != activeTraffic.end(); i++)
602         {
603           if (i == current) {
604         continue;
605       }
606       
607         SGGeod other(SGGeod::fromDegM(i->getLongitude(),
608           i->getLatitude(), i->getAltitude()));
609         SGGeodesy::inverse(curr, other, course, az2, dist);
610         bearing = fabs(heading-course);
611             if (bearing > 180)
612               bearing = 360-bearing;
613             if ((dist < mindist) && (bearing < 60.0))
614               {
615                 mindist = dist;
616                 closest = i;
617                 minbearing = bearing;
618               }
619         }
620       //Check traffic at the tower controller
621       if (towerController->hasActiveTraffic())
622         {
623           for (TrafficVectorIterator i = towerController->getActiveTraffic().begin(); 
624                i != towerController->getActiveTraffic().end(); i++)
625             {
626               //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
627               SGGeod other(SGGeod::fromDegM(i->getLongitude(),
628           i->getLatitude(), i->getAltitude()));
629         SGGeodesy::inverse(curr, other, course, az2, dist);
630         bearing = fabs(heading-course);
631               if (bearing > 180)
632           bearing = 360-bearing;
633               if ((dist < mindist) && (bearing < 60.0))
634                 {
635                   mindist = dist;
636                   closest = i;
637                   minbearing = bearing;
638                   otherReasonToSlowDown = true;
639                 }
640             }
641         }
642       // Finally, check UserPosition
643       double userLatitude  = fgGetDouble("/position/latitude-deg");
644       double userLongitude = fgGetDouble("/position/longitude-deg");
645       SGGeod user(SGGeod::fromDeg(userLatitude,userLongitude));
646       SGGeodesy::inverse(user, curr, course, az2, dist);
647       
648       bearing = fabs(heading-course);
649       if (bearing > 180)
650         bearing = 360-bearing;
651       if ((dist < mindist) && (bearing < 60.0))
652         {
653           mindist = dist;
654           //closest = i;
655           minbearing = bearing;
656           otherReasonToSlowDown = true;
657         }
658       
659       //        if (closest == current) {
660       //          //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: closest and current match");
661       //          //return;
662       //        }      
663       //cerr << "Distance : " << dist << " bearing : " << bearing << " heading : " << heading 
664       //   << " course : " << course << endl;
665       current->clearSpeedAdjustment();
666     
667       if (current->checkPositionAndIntentions(*closest) || otherReasonToSlowDown) 
668         {
669            double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
670            if (mindist < 2*maxAllowableDistance)
671              {
672                if (current->getId() == closest->getWaitsForId())
673                  return;
674                else 
675                  current->setWaitsForId(closest->getId());
676                if (closest->getId() != current->getId())
677                  current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
678                else
679                  current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
680                if (mindist < maxAllowableDistance)
681                  {
682                    //double newSpeed = (maxAllowableDistance-mindist);
683                    //current->setSpeedAdjustment(newSpeed);
684                    //if (mindist < 0.5* maxAllowableDistance)
685                    //  {
686                        current->setSpeedAdjustment(0);
687                        //  }
688                  }
689              }
690         }
691     }
692 }
693
694 /**
695    Check for "Hold position instruction".
696    The hold position should be issued under the following conditions:
697    1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
698    2) For taxiing aircraft that use one taxiway in opposite directions
699    3) For crossing or merging taxiroutes.
700 */
701
702 void FGGroundNetwork::checkHoldPosition(int id, double lat, 
703                                         double lon, double heading, 
704                                         double speed, double alt)
705 {
706   
707   TrafficVectorIterator current;
708   TrafficVectorIterator i = activeTraffic.begin();
709   if (activeTraffic.size()) 
710     {
711       //while ((i->getId() != id) && i != activeTraffic.end()) 
712       while (i != activeTraffic.end()) {
713         if (i->getId() == id) {
714           break;
715         }
716         i++;
717       }
718     }
719   else
720     {
721       return ;
722     } 
723   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
724     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
725   }
726   current = i;
727   current->setHoldPosition(false);
728   SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
729   
730   for (i = activeTraffic.begin(); 
731        i != activeTraffic.end(); i++)
732     {
733       if (i->getId() != current->getId()) 
734         {
735           int node = current->crosses(this, *i);
736           if (node != -1)
737             {
738           FGTaxiNode* taxiNode = findNode(node);
739           
740               // Determine whether it's save to continue or not. 
741               // If we have a crossing route, there are two possibilities:
742               // 1) This is an interestion
743               // 2) This is oncoming two-way traffic, using the same taxiway.
744               //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
745              
746         SGGeod other(SGGeod::fromDegM(i->getLongitude(), i->getLatitude(), i->getAltitude()));
747         bool needsToWait;
748               bool opposing;
749               if (current->isOpposing(this, *i, node))
750                 {
751                   needsToWait = true;
752                   opposing    = true;
753                   //cerr << "Hold check 2 : " << node << "  has opposing segment " << endl;
754                   // issue a "Hold Position" as soon as we're close to the offending node
755                   // For now, I'm doing this as long as the other aircraft doesn't
756                   // have a hold instruction as soon as we're within a reasonable 
757                   // distance from the offending node.
758                   // This may be a bit of a conservative estimate though, as it may
759                   // be well possible that both aircraft can both continue to taxi 
760                   // without crashing into each other.
761                 } 
762               else 
763                 {
764                   opposing = false;
765                   if (SGGeodesy::distanceM(other, taxiNode->getGeod()) > 200) // 2.0*i->getRadius())
766                     {
767                       needsToWait = false; 
768                       //cerr << "Hold check 3 : " << id <<"  Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue " 
769                       //           << endl;
770                     }
771                   else 
772                     {
773                       needsToWait = true;
774                       //cerr << "Hold check 4: " << id << "  Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
775                     }
776                 }
777       
778       double dist = SGGeodesy::distanceM(curr, taxiNode->getGeod());
779       if (!(i->hasHoldPosition()))
780                 {
781                   
782                   if ((dist < 200) && //2.5*current->getRadius()) && 
783                       (needsToWait) &&
784                       (i->onRoute(this, *current)) &&
785                       //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
786                       (!(current->getId() == i->getWaitsForId())))
787                       //(!(i->getSpeedAdjustment()))) // &&
788                       //(!(current->getSpeedAdjustment())))
789                     
790                     {
791                       current->setHoldPosition(true);
792                       current->setWaitsForId(i->getId());
793                       //cerr << "Hold check 5: " << current->getCallSign() <<"  Setting Hold Position: distance to node ("  << node << ") "
794                       //           << dist << " meters. Waiting for " << i->getCallSign();
795                       //if (opposing)
796                       //cerr <<" [opposing] " << endl;
797                       //else
798                       //        cerr << "[non-opposing] " << endl;
799                       //if (i->hasSpeefAdjustment())
800                       //        {
801                       //          cerr << " (which in turn waits for ) " << i->
802                     }
803                   else
804                     {
805                       //cerr << "Hold check 6: " << id << "  No need to hold yet: Distance to node : " << dist << " nm"<< endl;
806                     }
807                 }
808             }
809         }
810     }
811 }
812
813 /**
814  * Check whether situations occur where the current aircraft is waiting for itself
815  * due to higher order interactions. 
816  * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
817  * for a. Ideally each aircraft only waits for one other aircraft, so by tracing 
818  * through this list of waiting aircraft, we can check if we'd eventually end back 
819  * at the current aircraft.
820  *
821  * Note that we should consider the situation where we are actually checking aircraft
822  * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
823  * the looping aircraft. If we don't check for that, this function will get stuck into
824  * endless loop.
825  */
826
827 bool FGGroundNetwork::checkForCircularWaits(int id)
828 {  
829    //cerr << "Performing Wait check " << id << endl;
830    int target = 0;
831    TrafficVectorIterator current, other;
832    TrafficVectorIterator i = activeTraffic.begin();
833    int trafficSize = activeTraffic.size();
834    if (trafficSize)  {
835         while (i != activeTraffic.end()) {
836         if (i->getId() == id) {
837             break;
838         }
839         i++;
840     }
841   }
842   else {
843       return false;
844   }
845   if (i == activeTraffic.end() || (trafficSize == 0)) {
846     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
847   }
848    
849    current = i;
850    target = current->getWaitsForId();
851    //bool printed = false; // Note that this variable is for debugging purposes only.
852    int counter = 0;
853
854    if (id == target) {
855        //cerr << "aircraft waits for user" << endl;
856        return false;
857    }
858
859
860    while ((target > 0) && (target != id) && counter++ < trafficSize) {
861     //printed = true;
862      TrafficVectorIterator i = activeTraffic.begin();
863      if (trafficSize)  {
864           //while ((i->getId() != id) && i != activeTraffic.end()) 
865           while (i != activeTraffic.end()) {
866           if (i->getId() == target) {
867               break;
868           }
869           i++;
870         }
871       }
872       else {
873         return false;
874     } 
875     if (i == activeTraffic.end() || (trafficSize == 0)) {
876         //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
877       // The target id is not found on the current network, which means it's at the tower
878       //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
879       return false;
880     }
881     other = i;
882     target = other->getWaitsForId();
883
884     // actually this trap isn't as impossible as it first seemed:
885     // the setWaitsForID(id) is set to current when the aircraft
886     // is waiting for the user controlled aircraft. 
887     //if (current->getId() == other->getId()) {
888     //    cerr << "Caught the impossible trap" << endl;
889     //    cerr << "Current = " << current->getId() << endl;
890     //    cerr << "Other   = " << other  ->getId() << endl;
891     //    for (TrafficVectorIterator at = activeTraffic.begin();
892     //          at != activeTraffic.end();
893     //          at++) {
894     //        cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
895     //    }
896     //    exit(1);
897     if (current->getId() == other->getId())
898         return false;
899     //}
900     //cerr << current->getCallSign() << " (" << current->getId()  << ") " << " -> " << other->getCallSign() 
901     //     << " (" << other->getId()  << "); " << endl;;
902     //current = other;
903    }
904
905
906
907
908
909
910    //if (printed)
911    //   cerr << "[done] " << endl << endl;;
912    if (id == target) {
913        SG_LOG(SG_GENERAL, SG_WARN, "Detected circular wait condition: Id = " << id << "target = " << target);
914        return true;
915    } else {
916    return false;
917    }
918 }
919
920 // Note that this function is probably obsolete...
921 bool FGGroundNetwork::hasInstruction(int id)
922 {
923     TrafficVectorIterator i = activeTraffic.begin();
924    // Search search if the current id has an entry
925    // This might be faster using a map instead of a vector, but let's start by taking a safe route
926    if (activeTraffic.size()) 
927      {
928        //while ((i->getId() != id) && i != activeTraffic.end()) {
929        while (i != activeTraffic.end()) {
930          if (i->getId() == id) {
931            break;
932          }
933          i++;
934        }
935      }
936    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
937      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
938    } else {
939      return i->hasInstruction();
940    }
941   return false;
942 }
943
944 FGATCInstruction FGGroundNetwork::getInstruction(int id)
945 {
946   TrafficVectorIterator i = activeTraffic.begin();
947   // Search search if the current id has an entry
948    // This might be faster using a map instead of a vector, but let's start by taking a safe route
949    if (activeTraffic.size()) {
950      //while ((i->getId() != id) && i != activeTraffic.end()) {
951      while (i != activeTraffic.end()) {
952        if (i->getId() == id) {
953          break;
954        }
955        i++;
956      }
957    }
958    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
959      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
960    } else {
961      return i->getInstruction();
962    }
963   return FGATCInstruction();
964 }
965
966