1 // groundnet.cxx - Implimentation of the FlightGear airport ground handling code
3 // Written by Durk Talsma, started June 2005.
5 // Copyright (C) 2004 Durk Talsma.
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.
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.
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.
30 //#include <plib/sg.h>
31 //#include <plib/ul.h>
33 //#include <Environment/environment_mgr.hxx>
34 //#include <Environment/environment.hxx>
35 //#include <simgear/misc/sg_path.hxx>
36 //#include <simgear/props/props.hxx>
37 //#include <simgear/structure/subsystem_mgr.hxx>
38 #include <simgear/debug/logstream.hxx>
39 #include <simgear/route/waypoint.hxx>
40 //#include <Main/globals.hxx>
41 //#include <Main/fg_props.hxx>
42 //#include <Airports/runways.hxx>
45 #include <AIModel/AIFlightPlan.hxx>
49 #include "groundnetwork.hxx"
55 /**************************************************************************
57 *************************************************************************/
58 FGTaxiNode::FGTaxiNode()
62 void FGTaxiNode::sortEndSegments(bool byLength)
65 sort(next.begin(), next.end(), sortByLength);
67 sort(next.begin(), next.end(), sortByHeadingDiff);
71 bool compare_nodes(FGTaxiNode *a, FGTaxiNode *b) {
75 /***************************************************************************
77 **************************************************************************/
78 FGTaxiSegment::FGTaxiSegment()
80 oppositeDirection = 0;
84 void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
86 FGTaxiNodeVectorIterator i = nodes->begin();
87 while (i != nodes->end())
89 if ((*i)->getIndex() == startNode)
91 start = (*i)->getAddress();
92 (*i)->addSegment(this);
99 void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
101 FGTaxiNodeVectorIterator i = nodes->begin();
102 while (i != nodes->end())
104 if ((*i)->getIndex() == endNode)
106 end = (*i)->getAddress();
115 // There is probably a computationally cheaper way of
117 void FGTaxiSegment::setTrackDistance()
120 SGWayPoint first (start->getLongitude(),
121 start->getLatitude(),
123 SGWayPoint second (end->getLongitude(),
126 first.CourseAndDistance(second, &course, &length);
130 void FGTaxiSegment::setCourseDiff(double crse)
132 headingDiff = fabs(course-crse);
134 if (headingDiff > 180)
135 headingDiff = fabs(headingDiff - 360);
138 bool compare_segments(FGTaxiSegment *a, FGTaxiSegment *b) {
142 bool sortByHeadingDiff(FGTaxiSegment *a, FGTaxiSegment *b) {
143 return a->hasSmallerHeadingDiff(*b);
146 bool sortByLength(FGTaxiSegment *a, FGTaxiSegment *b) {
147 return a->getLength() > b->getLength();
149 /***************************************************************************
151 **************************************************************************/
152 bool FGTaxiRoute::next(int *nde)
154 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
155 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
156 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
157 //if (currNode != nodes.end())
158 // cerr << "true" << endl;
160 // cerr << "false" << endl;
161 //if (nodes.size() != (routes.size()) +1)
162 // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
164 if (currNode == nodes.end())
167 if (currNode != nodes.begin()) // make sure route corresponds to the end node
173 bool FGTaxiRoute::next(int *nde, int *rte)
175 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
176 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
177 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
178 //if (currNode != nodes.end())
179 // cerr << "true" << endl;
181 // cerr << "false" << endl;
182 if (nodes.size() != (routes.size()) +1) {
183 SG_LOG(SG_GENERAL, SG_ALERT, "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size());
186 if (currNode == nodes.end())
189 //*rte = *(currRoute);
190 if (currNode != nodes.begin()) // Make sure route corresponds to the end node
197 // If currNode points to the first node, this means the aircraft is not on the taxi node
198 // yet. Make sure to return a unique identifyer in this situation though, because otherwise
199 // the speed adjust AI code may be unable to resolve whether two aircraft are on the same
200 // taxi route or not. the negative of the preceding route seems a logical choice, as it is
201 // unique for any starting location.
202 // Note that this is probably just a temporary fix until I get Parking / tower control working.
203 *rte = -1 * *(currRoute);
209 void FGTaxiRoute::rewind(int route)
215 if (!(next(&currPoint, &currRoute))) {
216 SG_LOG(SG_GENERAL,SG_ALERT, "Error in rewinding TaxiRoute: current" << currRoute
217 << " goal " << route);
219 } while (currRoute != route);
225 /***************************************************************************
227 **************************************************************************/
229 FGGroundNetwork::FGGroundNetwork()
237 currTraffic = activeTraffic.begin();
241 FGGroundNetwork::~FGGroundNetwork()
243 for (FGTaxiNodeVectorIterator node = nodes.begin();
250 for (FGTaxiSegmentVectorIterator seg = segments.begin();
251 seg != segments.end();
259 void FGGroundNetwork::addSegment(const FGTaxiSegment &seg)
261 segments.push_back(new FGTaxiSegment(seg));
264 void FGGroundNetwork::addNode(const FGTaxiNode &node)
266 nodes.push_back(new FGTaxiNode(node));
269 void FGGroundNetwork::addNodes(FGParkingVec *parkings)
272 FGParkingVecIterator i = parkings->begin();
273 while (i != parkings->end())
275 n.setIndex(i->getIndex());
276 n.setLatitude(i->getLatitude());
277 n.setLongitude(i->getLongitude());
278 nodes.push_back(new FGTaxiNode(n));
286 void FGGroundNetwork::init()
290 sort(nodes.begin(), nodes.end(), compare_nodes);
291 //sort(segments.begin(), segments.end(), compare_segments());
292 FGTaxiSegmentVectorIterator i = segments.begin();
293 while(i != segments.end()) {
294 //cerr << "initializing node " << i->getIndex() << endl;
295 (*i)->setStart(&nodes);
296 (*i)->setEnd (&nodes);
297 (*i)->setTrackDistance();
298 (*i)->setIndex(index);
299 //cerr << "Track distance = " << i->getLength() << endl;
300 //cerr << "Track ends at" << i->getEnd()->getIndex() << endl;
305 i = segments.begin();
306 while(i != segments.end()) {
307 FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute();
308 while (j != (*i)->getEnd()->getEndRoute())
310 if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex())
312 int start1 = (*i)->getStart()->getIndex();
313 int end1 = (*i)->getEnd() ->getIndex();
314 int start2 = (*j)->getStart()->getIndex();
315 int end2 = (*j)->getEnd()->getIndex();
316 int oppIndex = (*j)->getIndex();
317 //cerr << "Opposite of " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
318 // << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl;
319 (*i)->setOpposite(*j);
326 //cerr << "Done initializing ground network" << endl;
332 int FGGroundNetwork::findNearestNode(double lat, double lon)
334 double minDist = HUGE_VAL;
337 SGWayPoint first (lon,
341 for (FGTaxiNodeVectorIterator
343 itr != nodes.end(); itr++)
346 SGWayPoint second ((*itr)->getLongitude(),
347 (*itr)->getLatitude(),
349 first.CourseAndDistance(second, &course, &dist);
353 index = (*itr)->getIndex();
354 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
360 FGTaxiNode *FGGroundNetwork::findNode(int idx)
362 for (FGTaxiNodeVectorIterator
364 itr != nodes.end(); itr++)
366 if (itr->getIndex() == idx)
367 return itr->getAddress();
370 if ((idx >= 0) && (idx < nodes.size()))
371 return nodes[idx]->getAddress();
376 FGTaxiSegment *FGGroundNetwork::findSegment(int idx)
378 for (FGTaxiSegmentVectorIterator
379 itr = segments.begin();
380 itr != segments.end(); itr++)
382 if (itr->getIndex() == idx)
383 return itr->getAddress();
386 if ((idx > 0) && (idx <= segments.size()))
387 return segments[idx-1]->getAddress();
390 //cerr << "Alert: trying to find invalid segment " << idx << endl;
396 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end)
398 //implements Dijkstra's algorithm to find shortest distance route from start to end
399 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
401 //double INFINITE = 100000000000.0;
402 // initialize scoring values
403 for (FGTaxiNodeVectorIterator
405 itr != nodes.end(); itr++) {
406 (*itr)->pathscore = HUGE_VAL; //infinity by all practical means
407 (*itr)->previousnode = 0; //
408 (*itr)->previousseg = 0; //
411 FGTaxiNode *firstNode = findNode(start);
412 firstNode->pathscore = 0;
414 FGTaxiNode *lastNode = findNode(end);
416 FGTaxiNodeVector unvisited(nodes); // working copy
418 while (!unvisited.empty()) {
419 FGTaxiNode* best = *(unvisited.begin());
420 for (FGTaxiNodeVectorIterator
421 itr = unvisited.begin();
422 itr != unvisited.end(); itr++) {
423 if ((*itr)->pathscore < best->pathscore)
427 FGTaxiNodeVectorIterator newend = remove(unvisited.begin(), unvisited.end(), best);
428 unvisited.erase(newend, unvisited.end());
430 if (best == lastNode) { // found route or best not connected
433 for (FGTaxiSegmentVectorIterator
434 seg = best->getBeginRoute();
435 seg != best->getEndRoute(); seg++) {
436 FGTaxiNode* tgt = (*seg)->getEnd();
437 double alt = best->pathscore + (*seg)->getLength();
438 if (alt < tgt->pathscore) { // Relax (u,v)
439 tgt->pathscore = alt;
440 tgt->previousnode = best;
441 tgt->previousseg = *seg; //
447 if (lastNode->pathscore == HUGE_VAL) {
448 // no valid route found
449 SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find route from waypoint " << start << " to " << end << " at " <<
451 exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
453 // assemble route from backtrace information
454 intVec nodes, routes;
455 FGTaxiNode* bt = lastNode;
456 while (bt->previousnode != 0) {
457 nodes.push_back(bt->getIndex());
458 routes.push_back(bt->previousseg->getIndex());
459 bt = bt->previousnode;
461 nodes.push_back(start);
462 reverse(nodes.begin(), nodes.end());
463 reverse(routes.begin(), routes.end());
465 return FGTaxiRoute(nodes, routes, lastNode->pathscore, 0);
470 // void FGGroundNetwork::trace(FGTaxiNode *currNode, int end, int depth, double distance)
472 // // Just check some preconditions of the trace algorithm
473 // if (nodesStack.size() != routesStack.size())
475 // SG_LOG(SG_GENERAL, SG_ALERT, "size of nodesStack and routesStack is not equal. NodesStack :"
476 // << nodesStack.size() << ". RoutesStack : " << routesStack.size());
478 // nodesStack.push_back(currNode->getIndex());
479 // totalDistance += distance;
480 // //cerr << "Starting trace " << currNode->getIndex() << " " << "total distance: " << totalDistance << endl;
481 // // << currNode->getIndex() << endl;
483 // // If the current route matches the required end point we found a valid route
484 // // So we can add this to the routing table
485 // if (currNode->getIndex() == end)
488 // //cerr << "Found route : " << totalDistance << "" << " " << *(nodesStack.end()-1) << " Depth = " << depth << endl;
489 // routes.push_back(FGTaxiRoute(nodesStack,routesStack,totalDistance, depth));
490 // if (nodesStack.empty() || routesStack.empty())
492 // printRoutingError(string("while finishing route"));
494 // nodesStack.pop_back();
495 // routesStack.pop_back();
496 // if (!(foundRoute)) {
497 // maxDistance = totalDistance;
500 // if (totalDistance < maxDistance)
501 // maxDistance = totalDistance;
502 // foundRoute = true;
503 // totalDistance -= distance;
508 // // search if the currentNode has been encountered before
509 // // if so, we should step back one level, because it is
510 // // rather rediculous to proceed further from here.
511 // // if the current node has not been encountered before,
512 // // i should point to nodesStack.end()-1; and we can continue
513 // // if i is not nodesStack.end, the previous node was found,
514 // // and we should return.
515 // // This only works at trace levels of 1 or higher though
517 // intVecIterator i = nodesStack.begin();
518 // while ((*i) != currNode->getIndex()) {
519 // //cerr << "Route so far : " << (*i) << endl;
522 // if (i != nodesStack.end()-1) {
523 // if (nodesStack.empty() || routesStack.empty())
525 // printRoutingError(string("while returning from an already encountered node"));
527 // nodesStack.pop_back();
528 // routesStack.pop_back();
529 // totalDistance -= distance;
532 // if (depth >= maxDepth) {
534 // if (!(count % 100000)) {
535 // maxDepth--; // Gradually decrease maxdepth, to prevent "eternal searches"
536 // //cerr << "Reducing maxdepth to " << maxDepth << endl;
538 // nodesStack.pop_back();
539 // routesStack.pop_back();
540 // totalDistance -= distance;
543 // // If the total distance from start to the current waypoint
544 // // is longer than that of a route we can also stop this trace
545 // // and go back one level.
546 // if ((totalDistance > maxDistance) && foundRoute)
549 // //cerr << "Stopping rediculously long trace: " << totalDistance << endl;
550 // if (nodesStack.empty() || routesStack.empty())
552 // printRoutingError(string("while returning from finding a rediculously long route"));
554 // nodesStack.pop_back();
555 // routesStack.pop_back();
556 // totalDistance -= distance;
561 //cerr << "2" << endl;
562 if (currNode->getBeginRoute() != currNode->getEndRoute())
564 double course, length;
565 //cerr << "3" << endl;
566 // calculate distance and heading "as the crow flies" between starn and end points"
567 SGWayPoint first(currNode->getLongitude(),
568 currNode->getLatitude(),
570 //SGWayPoint second (lastNode->getLongitude(),
571 // lastNode->getLatitude(),
574 first.CourseAndDistance(destination, &course, &length);
575 //for (FGTaxiSegmentVectorIterator
576 // itr = segments.begin();
577 // itr != segments.end(); itr++)
579 // (*itr)->setCourseDiff(course);
581 //FGTaxiNodeVectorIterator nde = nodes.begin();
582 //while (nde != nodes.end()) {
583 //(*nde)->sortEndSegments();
586 for (FGTaxiSegmentVectorIterator
587 i = currNode->getBeginRoute();
588 i != currNode->getEndRoute();
591 (*i)->setCourseDiff(course);
593 currNode->sortEndSegments(foundRoute);
594 for (FGTaxiSegmentVectorIterator
595 i = currNode->getBeginRoute();
596 i != currNode->getEndRoute();
599 //cerr << (*i)->getLength() << endl;
600 //cerr << (*i)->getIndex() << endl;
601 int idx = (*i)->getIndex();
602 routesStack.push_back((*i)->getIndex());
603 trace((*i)->getEnd(), end, depth+1, (*i)->getLength());
605 // // cerr << currNode -> getIndex() << " ";
606 // route.push_back(currNode->getIndex());
613 //SG_LOG( SG_GENERAL, SG_DEBUG, "4" );
615 if (nodesStack.empty())
617 printRoutingError(string("while finishing trace"));
619 nodesStack.pop_back();
620 // Make sure not to dump the level-zero routesStack entry, because that was never created.
623 routesStack.pop_back();
624 //cerr << "leaving trace " << routesStack.size() << endl;
626 totalDistance -= distance;
630 void FGGroundNetwork::printRoutingError(string mess)
632 SG_LOG(SG_GENERAL, SG_ALERT, "Error in ground network trace algorithm " << mess);
633 if (nodesStack.empty())
635 SG_LOG(SG_GENERAL, SG_ALERT, " nodesStack is empty. Dumping routesStack");
636 for (intVecIterator i = routesStack.begin() ; i != routesStack.end(); i++)
637 SG_LOG(SG_GENERAL, SG_ALERT, "Route " << (*i));
639 if (routesStack.empty())
641 SG_LOG(SG_GENERAL, SG_ALERT, " routesStack is empty. Dumping nodesStack");
642 for (intVecIterator i = nodesStack.begin() ; i != nodesStack.end(); i++)
643 SG_LOG(SG_GENERAL, SG_ALERT, "Node " << (*i));
649 void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
650 double lat, double lon, double heading,
651 double speed, double alt, double radius, int leg,
654 TrafficVectorIterator i = activeTraffic.begin();
655 // Search search if the current id alread has an entry
656 // This might be faster using a map instead of a vector, but let's start by taking a safe route
657 if (activeTraffic.size()) {
658 //while ((i->getId() != id) && i != activeTraffic.end()) {
659 while (i != activeTraffic.end()) {
660 if (i->getId() == id) {
666 // Add a new TrafficRecord if no one exsists for this aircraft.
667 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
670 rec.setPositionAndIntentions(currentPosition, intendedRoute);
671 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
672 rec.setRadius(radius); // only need to do this when creating the record.
673 rec.setCallSign(callsign);
674 activeTraffic.push_back(rec);
676 i->setPositionAndIntentions(currentPosition, intendedRoute);
677 i->setPositionAndHeading(lat, lon, heading, speed, alt);
681 void FGGroundNetwork::signOff(int id) {
682 TrafficVectorIterator i = activeTraffic.begin();
683 // Search search if the current id alread has an entry
684 // This might be faster using a map instead of a vector, but let's start by taking a safe route
685 if (activeTraffic.size()) {
686 //while ((i->getId() != id) && i != activeTraffic.end()) {
687 while (i != activeTraffic.end()) {
688 if (i->getId() == id) {
694 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
695 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off");
697 i = activeTraffic.erase(i);
701 void FGGroundNetwork::update(int id, double lat, double lon, double heading, double speed, double alt,
703 TrafficVectorIterator i = activeTraffic.begin();
704 // Search search if the current id has an entry
705 // This might be faster using a map instead of a vector, but let's start by taking a safe route
706 TrafficVectorIterator current, closest;
707 if (activeTraffic.size()) {
708 //while ((i->getId() != id) && i != activeTraffic.end()) {
709 while (i != activeTraffic.end()) {
710 if (i->getId() == id) {
716 // update position of the current aircraft
717 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
718 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
720 i->setPositionAndHeading(lat, lon, heading, speed, alt);
726 // Update every three secs, but add some randomness
727 // to prevent all IA objects doing this in synchrony
728 //if (getDt() < (3.0) + (rand() % 10))
732 current->clearResolveCircularWait();
733 current->setWaitsForId(0);
734 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
735 checkHoldPosition (id, lat, lon, heading, speed, alt);
736 if (checkForCircularWaits(id)) {
737 i->setResolveCircularWait();
742 Scan for a speed adjustment change. Find the nearest aircraft that is in front
743 and adjust speed when we get too close. Only do this when current position and/or
744 intentions of the current aircraft match current taxiroute position of the proximate
745 aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
746 instruction. See below for the hold position instruction.
748 Note that there currently still is one flaw in the logic that needs to be addressed.
749 can be situations where one aircraft is in front of the current aircraft, on a separate
750 route, but really close after an intersection coming off the current route. This
751 aircraft is still close enough to block the current aircraft. This situation is currently
752 not addressed yet, but should be.
755 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
756 double lon, double heading,
757 double speed, double alt)
760 TrafficVectorIterator current, closest;
761 TrafficVectorIterator i = activeTraffic.begin();
762 bool otherReasonToSlowDown = false;
763 bool previousInstruction;
764 if (activeTraffic.size())
766 //while ((i->getId() != id) && (i != activeTraffic.end()))
767 while (i != activeTraffic.end()) {
768 if (i->getId() == id) {
778 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
779 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
784 previousInstruction = current->getSpeedAdjustment();
785 double mindist = HUGE;
786 if (activeTraffic.size())
788 double course, dist, bearing, minbearing;
789 SGWayPoint curr (lon,
792 //TrafficVector iterator closest;
794 for (TrafficVectorIterator i = activeTraffic.begin();
795 i != activeTraffic.end(); i++)
798 //SGWayPoint curr (lon,
801 SGWayPoint other (i->getLongitude (),
804 other.CourseAndDistance(curr, &course, &dist);
805 bearing = fabs(heading-course);
807 bearing = 360-bearing;
808 if ((dist < mindist) && (bearing < 60.0))
812 minbearing = bearing;
816 //Check traffic at the tower controller
817 if (towerController->hasActiveTraffic())
819 for (TrafficVectorIterator i = towerController->getActiveTraffic().begin();
820 i != towerController->getActiveTraffic().end(); i++)
822 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
823 //SGWayPoint curr (lon,
826 SGWayPoint other (i->getLongitude (),
829 other.CourseAndDistance(curr, &course, &dist);
830 bearing = fabs(heading-course);
832 bearing = 360-bearing;
833 if ((dist < mindist) && (bearing < 60.0))
837 minbearing = bearing;
838 otherReasonToSlowDown = true;
842 // Finally, check UserPosition
843 double userLatitude = fgGetDouble("/position/latitude-deg");
844 double userLongitude = fgGetDouble("/position/longitude-deg");
845 SGWayPoint user (userLongitude,
847 alt); // Alt is not really important here.
848 user.CourseAndDistance(curr, &course, &dist);
849 bearing = fabs(heading-course);
851 bearing = 360-bearing;
852 if ((dist < mindist) && (bearing < 60.0))
856 minbearing = bearing;
857 otherReasonToSlowDown = true;
860 // if (closest == current) {
861 // //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: closest and current match");
864 //cerr << "Distance : " << dist << " bearing : " << bearing << " heading : " << heading
865 // << " course : " << course << endl;
866 current->clearSpeedAdjustment();
868 if (current->checkPositionAndIntentions(*closest) || otherReasonToSlowDown)
870 double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
871 if (mindist < 2*maxAllowableDistance)
873 if (current->getId() == closest->getWaitsForId())
876 current->setWaitsForId(closest->getId());
877 if (closest != current)
878 current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
880 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
881 if (mindist < maxAllowableDistance)
883 //double newSpeed = (maxAllowableDistance-mindist);
884 //current->setSpeedAdjustment(newSpeed);
885 //if (mindist < 0.5* maxAllowableDistance)
887 current->setSpeedAdjustment(0);
896 Check for "Hold position instruction".
897 The hold position should be issued under the following conditions:
898 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
899 2) For taxiing aircraft that use one taxiway in opposite directions
900 3) For crossing or merging taxiroutes.
903 void FGGroundNetwork::checkHoldPosition(int id, double lat,
904 double lon, double heading,
905 double speed, double alt)
908 TrafficVectorIterator current;
909 TrafficVectorIterator i = activeTraffic.begin();
910 if (activeTraffic.size())
912 //while ((i->getId() != id) && i != activeTraffic.end())
913 while (i != activeTraffic.end()) {
914 if (i->getId() == id) {
924 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
925 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
928 current->setHoldPosition(false);
929 SGWayPoint curr (lon,
932 double course, dist, bearing, minbearing;
933 for (i = activeTraffic.begin();
934 i != activeTraffic.end(); i++)
938 int node = current->crosses(this, *i);
941 // Determine whether it's save to continue or not.
942 // If we have a crossing route, there are two possibilities:
943 // 1) This is an interestion
944 // 2) This is oncoming two-way traffic, using the same taxiway.
945 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
946 SGWayPoint nodePos(findNode(node)->getLongitude (),
947 findNode(node)->getLatitude (),
950 SGWayPoint other (i->getLongitude (),
953 //other.CourseAndDistance(curr, &course, &dist);
956 if (current->isOpposing(this, *i, node))
960 //cerr << "Hold check 2 : " << node << " has opposing segment " << endl;
961 // issue a "Hold Position" as soon as we're close to the offending node
962 // For now, I'm doing this as long as the other aircraft doesn't
963 // have a hold instruction as soon as we're within a reasonable
964 // distance from the offending node.
965 // This may be a bit of a conservative estimate though, as it may
966 // be well possible that both aircraft can both continue to taxi
967 // without crashing into each other.
972 other.CourseAndDistance(nodePos, &course, &dist);
973 if (dist > 200) // 2.0*i->getRadius())
976 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
982 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
985 curr.CourseAndDistance(nodePos, &course, &dist);
986 if (!(i->hasHoldPosition()))
989 if ((dist < 200) && //2.5*current->getRadius()) &&
991 (i->onRoute(this, *current)) &&
992 //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
993 (!(current->getId() == i->getWaitsForId())))
994 //(!(i->getSpeedAdjustment()))) // &&
995 //(!(current->getSpeedAdjustment())))
998 current->setHoldPosition(true);
999 current->setWaitsForId(i->getId());
1000 //cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
1001 // << dist << " meters. Waiting for " << i->getCallSign();
1003 //cerr <<" [opposing] " << endl;
1005 // cerr << "[non-opposing] " << endl;
1006 //if (i->hasSpeefAdjustment())
1008 // cerr << " (which in turn waits for ) " << i->
1012 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
1021 * Check whether situations occur where the current aircraft is waiting for itself
1022 * due to higher order interactions.
1023 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
1024 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
1025 * through this list of waiting aircraft, we can check if we'd eventually end back
1026 * at the current aircraft.
1028 * Note that we should consider the situation where we are actually checking aircraft
1029 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
1030 * the looping aircraft. If we don't check for that, this function will get stuck into
1034 bool FGGroundNetwork::checkForCircularWaits(int id)
1036 //cerr << "Performing Wait check " << id << endl;
1038 TrafficVectorIterator current, other;
1039 TrafficVectorIterator i = activeTraffic.begin();
1040 int trafficSize = activeTraffic.size();
1042 //while ((i->getId() != id) && i != activeTraffic.end())
1043 while (i != activeTraffic.end()) {
1044 if (i->getId() == id) {
1053 if (i == activeTraffic.end() || (trafficSize == 0)) {
1054 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
1058 target = current->getWaitsForId();
1059 //bool printed = false; // Note that this variable is for debugging purposes only.
1061 while ((target > 0) && (target != id) && counter++ < trafficSize) {
1063 TrafficVectorIterator i = activeTraffic.begin();
1065 //while ((i->getId() != id) && i != activeTraffic.end())
1066 while (i != activeTraffic.end()) {
1067 if (i->getId() == target) {
1076 if (i == activeTraffic.end() || (trafficSize == 0)) {
1077 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
1078 // The target id is not found on the current network, which means it's at the tower
1079 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
1083 target = other->getWaitsForId();
1085 // actually this trap isn't as impossible as it first seemed:
1086 // the setWaitsForID(id) is set to current when the aircraft
1087 // is waiting for the user controlled aircraft.
1088 //if (current->getId() == other->getId()) {
1089 // cerr << "Caught the impossible trap" << endl;
1090 // cerr << "Current = " << current->getId() << endl;
1091 // cerr << "Other = " << other ->getId() << endl;
1092 // for (TrafficVectorIterator at = activeTraffic.begin();
1093 // at != activeTraffic.end();
1095 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
1098 if (current->getId() == other->getId())
1101 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
1102 // << " (" << other->getId() << "); " << endl;;
1106 // cerr << "[done] " << endl << endl;;
1108 SG_LOG(SG_GENERAL, SG_INFO, "Detected circular wait condition");
1115 // Note that this function is probably obsolete...
1116 bool FGGroundNetwork::hasInstruction(int id)
1118 TrafficVectorIterator i = activeTraffic.begin();
1119 // Search search if the current id has an entry
1120 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1121 if (activeTraffic.size())
1123 //while ((i->getId() != id) && i != activeTraffic.end()) {
1124 while (i != activeTraffic.end()) {
1125 if (i->getId() == id) {
1131 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1132 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
1134 return i->hasInstruction();
1139 FGATCInstruction FGGroundNetwork::getInstruction(int id)
1141 TrafficVectorIterator i = activeTraffic.begin();
1142 // Search search if the current id has an entry
1143 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1144 if (activeTraffic.size()) {
1145 //while ((i->getId() != id) && i != activeTraffic.end()) {
1146 while (i != activeTraffic.end()) {
1147 if (i->getId() == id) {
1153 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1154 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
1156 return i->getInstruction();
1158 return FGATCInstruction();