]> git.mxchange.org Git - flightgear.git/blob - src/Airports/groundnetwork.cxx
Merge branch 'csaba/nan'
[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    There 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(), 
629                                             i->getAltitude()));
630         SGGeodesy::inverse(curr, other, course, az2, dist);
631         bearing = fabs(heading-course);
632               if (bearing > 180)
633           bearing = 360-bearing;
634               if ((dist < mindist) && (bearing < 60.0))
635                 {
636                   mindist = dist;
637                   closest = i;
638                   minbearing = bearing;
639                   otherReasonToSlowDown = true;
640                 }
641             }
642         }
643       // Finally, check UserPosition
644       double userLatitude  = fgGetDouble("/position/latitude-deg");
645       double userLongitude = fgGetDouble("/position/longitude-deg");
646       SGGeod user(SGGeod::fromDeg(userLongitude,userLatitude));
647       SGGeodesy::inverse(curr, user, course, az2, dist);
648
649       bearing = fabs(heading-course);
650       if (bearing > 180)
651       bearing = 360-bearing;
652       if ((dist < mindist) && (bearing < 60.0))
653         {
654           mindist = dist;
655           //closest = i;
656           minbearing = bearing;
657           otherReasonToSlowDown = true;
658         }
659
660       current->clearSpeedAdjustment();
661     
662       if (current->checkPositionAndIntentions(*closest) || otherReasonToSlowDown) 
663         {
664            double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
665            if (mindist < 2*maxAllowableDistance)
666              {
667                if (current->getId() == closest->getWaitsForId())
668                  return;
669                else 
670                  current->setWaitsForId(closest->getId());
671                if (closest->getId() != current->getId())
672                  current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
673                else
674                  current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
675                if (mindist < maxAllowableDistance)
676                  {
677                    //double newSpeed = (maxAllowableDistance-mindist);
678                    //current->setSpeedAdjustment(newSpeed);
679                    //if (mindist < 0.5* maxAllowableDistance)
680                    //  {
681                        current->setSpeedAdjustment(0);
682                        //  }
683                  }
684              }
685         }
686     }
687 }
688
689 /**
690    Check for "Hold position instruction".
691    The hold position should be issued under the following conditions:
692    1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
693    2) For taxiing aircraft that use one taxiway in opposite directions
694    3) For crossing or merging taxiroutes.
695 */
696
697 void FGGroundNetwork::checkHoldPosition(int id, double lat, 
698                                         double lon, double heading, 
699                                         double speed, double alt)
700 {
701   
702   TrafficVectorIterator current;
703   TrafficVectorIterator i = activeTraffic.begin();
704   if (activeTraffic.size()) 
705     {
706       //while ((i->getId() != id) && i != activeTraffic.end()) 
707       while (i != activeTraffic.end()) {
708         if (i->getId() == id) {
709           break;
710         }
711         i++;
712       }
713     }
714   else
715     {
716       return ;
717     } 
718   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
719     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
720   }
721   current = i;
722   current->setHoldPosition(false);
723   SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
724   
725   for (i = activeTraffic.begin(); 
726        i != activeTraffic.end(); i++)
727     {
728       if (i->getId() != current->getId()) 
729         {
730           int node = current->crosses(this, *i);
731           if (node != -1)
732             {
733           FGTaxiNode* taxiNode = findNode(node);
734           
735               // Determine whether it's save to continue or not. 
736               // If we have a crossing route, there are two possibilities:
737               // 1) This is an interestion
738               // 2) This is oncoming two-way traffic, using the same taxiway.
739               //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
740              
741         SGGeod other(SGGeod::fromDegM(i->getLongitude(), i->getLatitude(), i->getAltitude()));
742         bool needsToWait;
743               bool opposing;
744               if (current->isOpposing(this, *i, node))
745                 {
746                   needsToWait = true;
747                   opposing    = true;
748                   //cerr << "Hold check 2 : " << node << "  has opposing segment " << endl;
749                   // issue a "Hold Position" as soon as we're close to the offending node
750                   // For now, I'm doing this as long as the other aircraft doesn't
751                   // have a hold instruction as soon as we're within a reasonable 
752                   // distance from the offending node.
753                   // This may be a bit of a conservative estimate though, as it may
754                   // be well possible that both aircraft can both continue to taxi 
755                   // without crashing into each other.
756                 } 
757               else 
758                 {
759                   opposing = false;
760                   if (SGGeodesy::distanceM(other, taxiNode->getGeod()) > 200) // 2.0*i->getRadius())
761                     {
762                       needsToWait = false; 
763                       //cerr << "Hold check 3 : " << id <<"  Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue " 
764                       //           << endl;
765                     }
766                   else 
767                     {
768                       needsToWait = true;
769                       //cerr << "Hold check 4: " << id << "  Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
770                     }
771                 }
772       
773       double dist = SGGeodesy::distanceM(curr, taxiNode->getGeod());
774       if (!(i->hasHoldPosition()))
775                 {
776                   
777                   if ((dist < 200) && //2.5*current->getRadius()) && 
778                       (needsToWait) &&
779                       (i->onRoute(this, *current)) &&
780                       //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
781                       (!(current->getId() == i->getWaitsForId())))
782                       //(!(i->getSpeedAdjustment()))) // &&
783                       //(!(current->getSpeedAdjustment())))
784                     
785                     {
786                       current->setHoldPosition(true);
787                       current->setWaitsForId(i->getId());
788                       //cerr << "Hold check 5: " << current->getCallSign() <<"  Setting Hold Position: distance to node ("  << node << ") "
789                       //           << dist << " meters. Waiting for " << i->getCallSign();
790                       //if (opposing)
791                       //cerr <<" [opposing] " << endl;
792                       //else
793                       //        cerr << "[non-opposing] " << endl;
794                       //if (i->hasSpeefAdjustment())
795                       //        {
796                       //          cerr << " (which in turn waits for ) " << i->
797                     }
798                   else
799                     {
800                       //cerr << "Hold check 6: " << id << "  No need to hold yet: Distance to node : " << dist << " nm"<< endl;
801                     }
802                 }
803             }
804         }
805     }
806 }
807
808 /**
809  * Check whether situations occur where the current aircraft is waiting for itself
810  * due to higher order interactions. 
811  * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
812  * for a. Ideally each aircraft only waits for one other aircraft, so by tracing 
813  * through this list of waiting aircraft, we can check if we'd eventually end back 
814  * at the current aircraft.
815  *
816  * Note that we should consider the situation where we are actually checking aircraft
817  * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
818  * the looping aircraft. If we don't check for that, this function will get stuck into
819  * endless loop.
820  */
821
822 bool FGGroundNetwork::checkForCircularWaits(int id)
823 {  
824    //cerr << "Performing Wait check " << id << endl;
825    int target = 0;
826    TrafficVectorIterator current, other;
827    TrafficVectorIterator i = activeTraffic.begin();
828    int trafficSize = activeTraffic.size();
829    if (trafficSize)  {
830         while (i != activeTraffic.end()) {
831         if (i->getId() == id) {
832             break;
833         }
834         i++;
835     }
836   }
837   else {
838       return false;
839   }
840   if (i == activeTraffic.end() || (trafficSize == 0)) {
841     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
842   }
843    
844    current = i;
845    target = current->getWaitsForId();
846    //bool printed = false; // Note that this variable is for debugging purposes only.
847    int counter = 0;
848
849    if (id == target) {
850        //cerr << "aircraft waits for user" << endl;
851        return false;
852    }
853
854
855    while ((target > 0) && (target != id) && counter++ < trafficSize) {
856     //printed = true;
857      TrafficVectorIterator i = activeTraffic.begin();
858      if (trafficSize)  {
859           //while ((i->getId() != id) && i != activeTraffic.end()) 
860           while (i != activeTraffic.end()) {
861           if (i->getId() == target) {
862               break;
863           }
864           i++;
865         }
866       }
867       else {
868         return false;
869     } 
870     if (i == activeTraffic.end() || (trafficSize == 0)) {
871         //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
872       // The target id is not found on the current network, which means it's at the tower
873       //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
874       return false;
875     }
876     other = i;
877     target = other->getWaitsForId();
878
879     // actually this trap isn't as impossible as it first seemed:
880     // the setWaitsForID(id) is set to current when the aircraft
881     // is waiting for the user controlled aircraft. 
882     //if (current->getId() == other->getId()) {
883     //    cerr << "Caught the impossible trap" << endl;
884     //    cerr << "Current = " << current->getId() << endl;
885     //    cerr << "Other   = " << other  ->getId() << endl;
886     //    for (TrafficVectorIterator at = activeTraffic.begin();
887     //          at != activeTraffic.end();
888     //          at++) {
889     //        cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
890     //    }
891     //    exit(1);
892     if (current->getId() == other->getId())
893         return false;
894     //}
895     //cerr << current->getCallSign() << " (" << current->getId()  << ") " << " -> " << other->getCallSign() 
896     //     << " (" << other->getId()  << "); " << endl;;
897     //current = other;
898    }
899
900
901
902
903
904
905    //if (printed)
906    //   cerr << "[done] " << endl << endl;;
907    if (id == target) {
908        SG_LOG(SG_GENERAL, SG_WARN, "Detected circular wait condition: Id = " << id << "target = " << target);
909        return true;
910    } else {
911    return false;
912    }
913 }
914
915 // Note that this function is probably obsolete...
916 bool FGGroundNetwork::hasInstruction(int id)
917 {
918     TrafficVectorIterator i = activeTraffic.begin();
919    // Search search if the current id has an entry
920    // This might be faster using a map instead of a vector, but let's start by taking a safe route
921    if (activeTraffic.size()) 
922      {
923        //while ((i->getId() != id) && i != activeTraffic.end()) {
924        while (i != activeTraffic.end()) {
925          if (i->getId() == id) {
926            break;
927          }
928          i++;
929        }
930      }
931    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
932      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
933    } else {
934      return i->hasInstruction();
935    }
936   return false;
937 }
938
939 FGATCInstruction FGGroundNetwork::getInstruction(int id)
940 {
941   TrafficVectorIterator i = activeTraffic.begin();
942   // Search search if the current id has an entry
943    // This might be faster using a map instead of a vector, but let's start by taking a safe route
944    if (activeTraffic.size()) {
945      //while ((i->getId() != id) && i != activeTraffic.end()) {
946      while (i != activeTraffic.end()) {
947        if (i->getId() == id) {
948          break;
949        }
950        i++;
951      }
952    }
953    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
954      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
955    } else {
956      return i->getInstruction();
957    }
958   return FGATCInstruction();
959 }
960
961