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