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>
44 #include <AIModel/AIFlightPlan.hxx>
48 #include "groundnetwork.hxx"
54 /**************************************************************************
56 *************************************************************************/
57 FGTaxiNode::FGTaxiNode()
62 bool compare_nodes(FGTaxiNode *a, FGTaxiNode *b) {
66 /***************************************************************************
68 **************************************************************************/
69 FGTaxiSegment::FGTaxiSegment()
71 oppositeDirection = 0;
74 void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
76 FGTaxiNodeVectorIterator i = nodes->begin();
77 while (i != nodes->end())
79 if ((*i)->getIndex() == startNode)
81 start = (*i)->getAddress();
82 (*i)->addSegment(this);
89 void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
91 FGTaxiNodeVectorIterator i = nodes->begin();
92 while (i != nodes->end())
94 if ((*i)->getIndex() == endNode)
96 end = (*i)->getAddress();
103 // There is probably a computationally cheaper way of
105 void FGTaxiSegment::setTrackDistance()
108 SGWayPoint first (start->getLongitude(),
109 start->getLatitude(),
111 SGWayPoint second (end->getLongitude(),
114 first.CourseAndDistance(second, &course, &length);
117 bool compare_segments(FGTaxiSegment *a, FGTaxiSegment *b) {
121 /***************************************************************************
123 **************************************************************************/
124 bool FGTaxiRoute::next(int *nde)
126 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
127 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
128 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
129 //if (currNode != nodes.end())
130 // cerr << "true" << endl;
132 // cerr << "false" << endl;
133 //if (nodes.size() != (routes.size()) +1)
134 // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
136 if (currNode == nodes.end())
139 if (currNode != nodes.begin()) // make sure route corresponds to the end node
145 bool FGTaxiRoute::next(int *nde, int *rte)
147 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
148 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
149 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
150 //if (currNode != nodes.end())
151 // cerr << "true" << endl;
153 // cerr << "false" << endl;
154 if (nodes.size() != (routes.size()) +1) {
155 SG_LOG(SG_GENERAL, SG_ALERT, "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size());
158 if (currNode == nodes.end())
161 //*rte = *(currRoute);
162 if (currNode != nodes.begin()) // Make sure route corresponds to the end node
169 // If currNode points to the first node, this means the aircraft is not on the taxi node
170 // yet. Make sure to return a unique identifyer in this situation though, because otherwise
171 // the speed adjust AI code may be unable to resolve whether two aircraft are on the same
172 // taxi route or not. the negative of the preceding route seems a logical choice, as it is
173 // unique for any starting location.
174 // Note that this is probably just a temporary fix until I get Parking / tower control working.
175 *rte = -1 * *(currRoute);
181 void FGTaxiRoute::rewind(int route)
187 if (!(next(&currPoint, &currRoute))) {
188 SG_LOG(SG_GENERAL,SG_ALERT, "Error in rewinding TaxiRoute: current" << currRoute
189 << " goal " << route);
191 } while (currRoute != route);
197 /***************************************************************************
199 **************************************************************************/
201 FGGroundNetwork::FGGroundNetwork()
207 currTraffic = activeTraffic.begin();
211 FGGroundNetwork::~FGGroundNetwork()
213 for (FGTaxiNodeVectorIterator node = nodes.begin();
220 for (FGTaxiSegmentVectorIterator seg = segments.begin();
221 seg != segments.end();
229 void FGGroundNetwork::addSegment(const FGTaxiSegment &seg)
231 segments.push_back(new FGTaxiSegment(seg));
234 void FGGroundNetwork::addNode(const FGTaxiNode &node)
236 nodes.push_back(new FGTaxiNode(node));
239 void FGGroundNetwork::addNodes(FGParkingVec *parkings)
242 FGParkingVecIterator i = parkings->begin();
243 while (i != parkings->end())
245 n.setIndex(i->getIndex());
246 n.setLatitude(i->getLatitude());
247 n.setLongitude(i->getLongitude());
248 nodes.push_back(new FGTaxiNode(n));
256 void FGGroundNetwork::init()
260 sort(nodes.begin(), nodes.end(), compare_nodes);
261 //sort(segments.begin(), segments.end(), compare_segments());
262 FGTaxiSegmentVectorIterator i = segments.begin();
263 while(i != segments.end()) {
264 //cerr << "initializing node " << i->getIndex() << endl;
265 (*i)->setStart(&nodes);
266 (*i)->setEnd (&nodes);
267 (*i)->setTrackDistance();
268 (*i)->setIndex(index);
269 //cerr << "Track distance = " << i->getLength() << endl;
270 //cerr << "Track ends at" << i->getEnd()->getIndex() << endl;
274 i = segments.begin();
275 while(i != segments.end()) {
276 FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute();
277 while (j != (*i)->getEnd()->getEndRoute())
279 if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex())
281 int start1 = (*i)->getStart()->getIndex();
282 int end1 = (*i)->getEnd() ->getIndex();
283 int start2 = (*j)->getStart()->getIndex();
284 int end2 = (*j)->getEnd()->getIndex();
285 int oppIndex = (*j)->getIndex();
286 cerr << "Opposite of " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
287 << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl;
297 int FGGroundNetwork::findNearestNode(double lat, double lon)
299 double minDist = HUGE_VAL;
302 SGWayPoint first (lon,
306 for (FGTaxiNodeVectorIterator
308 itr != nodes.end(); itr++)
311 SGWayPoint second ((*itr)->getLongitude(),
312 (*itr)->getLatitude(),
314 first.CourseAndDistance(second, &course, &dist);
318 index = (*itr)->getIndex();
319 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
325 FGTaxiNode *FGGroundNetwork::findNode(int idx)
327 for (FGTaxiNodeVectorIterator
329 itr != nodes.end(); itr++)
331 if (itr->getIndex() == idx)
332 return itr->getAddress();
335 if ((idx >= 0) && (idx < nodes.size()))
336 return nodes[idx]->getAddress();
341 FGTaxiSegment *FGGroundNetwork::findSegment(int idx)
343 for (FGTaxiSegmentVectorIterator
344 itr = segments.begin();
345 itr != segments.end(); itr++)
347 if (itr->getIndex() == idx)
348 return itr->getAddress();
351 if ((idx > 0) && (idx <= segments.size()))
352 return segments[idx-1]->getAddress();
355 cerr << "Alert: trying to find invalid segment " << idx << endl;
360 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end)
364 FGTaxiNode *firstNode = findNode(start);
365 FGTaxiNode *lastNode = findNode(end);
366 //prevNode = prevPrevNode = -1;
371 //cerr << "Begin of Trace " << endl;
372 trace(firstNode, end, 0, 0);
373 //cerr << "End of Trace" << endl;
378 SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find route from waypoint " << start << " to " << end );
381 sort(routes.begin(), routes.end());
382 //for (intVecIterator i = route.begin(); i != route.end(); i++)
384 // rte->push_back(*i);
387 if (routes.begin() != routes.end())
388 return *(routes.begin());
394 void FGGroundNetwork::trace(FGTaxiNode *currNode, int end, int depth, double distance)
396 // Just check some preconditions of the trace algorithm
397 if (nodesStack.size() != routesStack.size())
399 SG_LOG(SG_GENERAL, SG_ALERT, "size of nodesStack and routesStack is not equal. NodesStack :"
400 << nodesStack.size() << ". RoutesStack : " << routesStack.size());
402 nodesStack.push_back(currNode->getIndex());
403 totalDistance += distance;
404 //cerr << "Starting trace " << depth << " total distance: " << totalDistance<< " "
405 // << currNode->getIndex() << endl;
407 // If the current route matches the required end point we found a valid route
408 // So we can add this to the routing table
409 if (currNode->getIndex() == end)
411 //cerr << "Found route : " << totalDistance << "" << " " << *(nodesStack.end()-1) << endl;
412 routes.push_back(FGTaxiRoute(nodesStack,routesStack,totalDistance));
413 if (nodesStack.empty() || routesStack.empty())
415 printRoutingError(string("while finishing route"));
417 nodesStack.pop_back();
418 routesStack.pop_back();
420 maxDistance = totalDistance;
422 if (totalDistance < maxDistance)
423 maxDistance = totalDistance;
425 totalDistance -= distance;
430 // search if the currentNode has been encountered before
431 // if so, we should step back one level, because it is
432 // rather rediculous to proceed further from here.
433 // if the current node has not been encountered before,
434 // i should point to nodesStack.end()-1; and we can continue
435 // if i is not nodesStack.end, the previous node was found,
436 // and we should return.
437 // This only works at trace levels of 1 or higher though
439 intVecIterator i = nodesStack.begin();
440 while ((*i) != currNode->getIndex()) {
441 //cerr << "Route so far : " << (*i) << endl;
444 if (i != nodesStack.end()-1) {
445 if (nodesStack.empty() || routesStack.empty())
447 printRoutingError(string("while returning from an already encountered node"));
449 nodesStack.pop_back();
450 routesStack.pop_back();
451 totalDistance -= distance;
454 // If the total distance from start to the current waypoint
455 // is longer than that of a route we can also stop this trace
456 // and go back one level.
457 if ((totalDistance > maxDistance) && foundRoute)
459 //cerr << "Stopping rediculously long trace: " << totalDistance << endl;
460 if (nodesStack.empty() || routesStack.empty())
462 printRoutingError(string("while returning from finding a rediculously long route"));
464 nodesStack.pop_back();
465 routesStack.pop_back();
466 totalDistance -= distance;
471 //cerr << "2" << endl;
472 if (currNode->getBeginRoute() != currNode->getEndRoute())
474 //cerr << "3" << endl;
475 for (FGTaxiSegmentVectorIterator
476 i = currNode->getBeginRoute();
477 i != currNode->getEndRoute();
480 //cerr << (*i)->getLength() << endl;
481 //cerr << (*i)->getIndex() << endl;
482 int idx = (*i)->getIndex();
483 routesStack.push_back((*i)->getIndex());
484 trace((*i)->getEnd(), end, depth+1, (*i)->getLength());
486 // // cerr << currNode -> getIndex() << " ";
487 // route.push_back(currNode->getIndex());
494 //SG_LOG( SG_GENERAL, SG_DEBUG, "4" );
496 if (nodesStack.empty())
498 printRoutingError(string("while finishing trace"));
500 nodesStack.pop_back();
501 // Make sure not to dump the level-zero routesStack entry, because that was never created.
504 routesStack.pop_back();
505 //cerr << "leaving trace " << routesStack.size() << endl;
507 totalDistance -= distance;
511 void FGGroundNetwork::printRoutingError(string mess)
513 SG_LOG(SG_GENERAL, SG_ALERT, "Error in ground network trace algorithm " << mess);
514 if (nodesStack.empty())
516 SG_LOG(SG_GENERAL, SG_ALERT, " nodesStack is empty. Dumping routesStack");
517 for (intVecIterator i = routesStack.begin() ; i != routesStack.end(); i++)
518 SG_LOG(SG_GENERAL, SG_ALERT, "Route " << (*i));
520 if (routesStack.empty())
522 SG_LOG(SG_GENERAL, SG_ALERT, " routesStack is empty. Dumping nodesStack");
523 for (intVecIterator i = nodesStack.begin() ; i != nodesStack.end(); i++)
524 SG_LOG(SG_GENERAL, SG_ALERT, "Node " << (*i));
530 void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
531 double lat, double lon, double heading,
532 double speed, double alt, double radius, int leg)
534 TrafficVectorIterator i = activeTraffic.begin();
535 // Search search if the current id alread has an entry
536 // This might be faster using a map instead of a vector, but let's start by taking a safe route
537 if (activeTraffic.size()) {
538 //while ((i->getId() != id) && i != activeTraffic.end()) {
539 while (i != activeTraffic.end()) {
540 if (i->getId() == id) {
546 // Add a new TrafficRecord if no one exsists for this aircraft.
547 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
550 rec.setPositionAndIntentions(currentPosition, intendedRoute);
551 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
552 rec.setRadius(radius); // only need to do this when creating the record.
553 activeTraffic.push_back(rec);
555 i->setPositionAndIntentions(currentPosition, intendedRoute);
556 i->setPositionAndHeading(lat, lon, heading, speed, alt);
560 void FGGroundNetwork::signOff(int id) {
561 TrafficVectorIterator i = activeTraffic.begin();
562 // Search search if the current id alread has an entry
563 // This might be faster using a map instead of a vector, but let's start by taking a safe route
564 if (activeTraffic.size()) {
565 //while ((i->getId() != id) && i != activeTraffic.end()) {
566 while (i != activeTraffic.end()) {
567 if (i->getId() == id) {
573 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
574 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off");
576 i = activeTraffic.erase(i);
580 void FGGroundNetwork::update(int id, double lat, double lon, double heading, double speed, double alt,
582 TrafficVectorIterator i = activeTraffic.begin();
583 // Search search if the current id has an entry
584 // This might be faster using a map instead of a vector, but let's start by taking a safe route
585 TrafficVectorIterator current, closest;
586 if (activeTraffic.size()) {
587 //while ((i->getId() != id) && i != activeTraffic.end()) {
588 while (i != activeTraffic.end()) {
589 if (i->getId() == id) {
595 // update position of the current aircraft
596 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
597 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
599 i->setPositionAndHeading(lat, lon, heading, speed, alt);
605 // Update every three secs, but add some randomness
606 // to prevent all IA objects doing this in synchrony
607 //if (getDt() < (3.0) + (rand() % 10))
611 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
612 checkHoldPosition (id, lat, lon, heading, speed, alt);
615 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
616 double lon, double heading,
617 double speed, double alt)
620 // Scan for a speed adjustment change. Find the nearest aircraft that is in front
621 // and adjust speed when we get too close. Only do this when current position and/or
622 // intentions of the current aircraft match current taxiroute position of the proximate
623 // aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
624 // instruction. See below for the hold position instruction.
625 TrafficVectorIterator current, closest;
626 TrafficVectorIterator i = activeTraffic.begin();
627 bool otherReasonToSlowDown = false;
628 if (activeTraffic.size())
630 //while ((i->getId() != id) && (i != activeTraffic.end()))
631 while (i != activeTraffic.end()) {
632 if (i->getId() == id) {
642 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
643 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
646 double mindist = HUGE;
647 if (activeTraffic.size())
649 double course, dist, bearing, minbearing;
650 SGWayPoint curr (lon,
653 //TrafficVector iterator closest;
655 for (TrafficVectorIterator i = activeTraffic.begin();
656 i != activeTraffic.end(); i++)
659 //SGWayPoint curr (lon,
662 SGWayPoint other (i->getLongitude (),
665 other.CourseAndDistance(curr, &course, &dist);
666 bearing = fabs(heading-course);
668 bearing = 360-bearing;
669 if ((dist < mindist) && (bearing < 60.0))
673 minbearing = bearing;
677 //Check traffic at the tower controller
678 if (towerController->hasActiveTraffic())
680 for (TrafficVectorIterator i = towerController->getActiveTraffic().begin();
681 i != towerController->getActiveTraffic().end(); i++)
684 //SGWayPoint curr (lon,
687 SGWayPoint other (i->getLongitude (),
690 other.CourseAndDistance(curr, &course, &dist);
691 bearing = fabs(heading-course);
693 bearing = 360-bearing;
694 if ((dist < mindist) && (bearing < 60.0))
698 minbearing = bearing;
699 otherReasonToSlowDown = true;
704 // Finally, check UserPosition
705 double userLatitude = fgGetDouble("/position/latitude-deg");
706 double userLongitude = fgGetDouble("/position/longitude-deg");
707 SGWayPoint user (userLongitude,
709 alt); // Alt is not really important here.
710 user.CourseAndDistance(curr, &course, &dist);
711 bearing = fabs(heading-course);
713 bearing = 360-bearing;
714 if ((dist < mindist) && (bearing < 60.0))
718 minbearing = bearing;
719 otherReasonToSlowDown = true;
722 // if (closest == current) {
723 // //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: closest and current match");
726 //cerr << "Distance : " << dist << " bearing : " << bearing << " heading : " << heading
727 // << " course : " << course << endl;
728 current->clearSpeedAdjustment();
729 // Only clear the heading adjustment at positive speeds, otherwise the waypoint following
732 current->clearHeadingAdjustment();
736 //current->clearSpeedAdjustment();
737 //current->clearHeadingAdjustment();
742 if (current->getId() == closest->getWaitsForId())
745 current->setWaitsForId(closest->getId());
748 // Getting close: Slow down to a bit less than the other aircraft
749 double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
750 if (mindist > maxAllowableDistance)
752 if (current->checkPositionAndIntentions(*closest) || otherReasonToSlowDown)
754 // Adjust speed, but don't let it drop to below 1 knots
755 //if (fabs(speed) > 1)
756 if (!(current->hasHeadingAdjustment()))
758 if (closest != current)
759 current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
761 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
762 //cerr << "Adjusting speed to " << closest->getSpeed() * (mindist / 100) << " "
763 // << "Bearing = " << minbearing << " Distance = " << mindist
764 // << " Latitude = " <<lat << " longitude = " << lon << endl;
765 //<< " Latitude = " <<closest->getLatitude()
766 //<< " longitude = " << closest->getLongitude()
771 double newSpeed = (maxAllowableDistance-mindist);
772 current->setSpeedAdjustment(newSpeed);
778 if (!(current->hasHeadingAdjustment()))
783 // current->setSpeedAdjustment(newSpeed);
785 newSpeed = -1 * (maxAllowableDistance-mindist);
786 current->setSpeedAdjustment(newSpeed);
787 current->setHeadingAdjustment(heading);
795 void FGGroundNetwork::checkHoldPosition(int id, double lat,
796 double lon, double heading,
797 double speed, double alt)
799 // Check for "Hold position instruction".
800 // The hold position should be issued under the following conditions:
801 // 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
802 // 2) For taxiing aircraft that use one taxiway in opposite directions
803 // 3) For crossing or merging taxiroutes.
805 TrafficVectorIterator current;
806 TrafficVectorIterator i = activeTraffic.begin();
807 if (activeTraffic.size())
809 //while ((i->getId() != id) && i != activeTraffic.end())
810 while (i != activeTraffic.end()) {
811 if (i->getId() == id) {
821 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
822 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
825 current->setHoldPosition(false);
826 SGWayPoint curr (lon,
829 double course, dist, bearing, minbearing;
830 for (i = activeTraffic.begin();
831 i != activeTraffic.end(); i++)
835 int node = current->crosses(this, *i);
838 // Determine whether it's save to continue or not.
839 // If we have a crossing route, there are two possibilities:
840 // 1) This is an interestion
841 // 2) This is oncoming two-way traffic, using the same taxiway.
842 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
843 SGWayPoint nodePos(findNode(node)->getLongitude (),
844 findNode(node)->getLatitude (),
848 SGWayPoint other (i->getLongitude (),
851 //other.CourseAndDistance(curr, &course, &dist);
853 if (current->isOpposing(this, *i, node))
856 //cerr << "Hold check 2 : " << id << " has opposing segment " << endl;
857 // issue a "Hold Position" as soon as we're close to the offending node
858 // For now, I'm doing this as long as the other aircraft doesn't
859 // have a hold instruction as soon as we're within a reasonable
860 // distance from the offending node.
861 // This may be a bit of a conservative estimate though, as it may
862 // be well possible that both aircraft can both continue to taxi
863 // without crashing into each other.
867 other.CourseAndDistance(nodePos, &course, &dist);
868 if (dist > 2.0*i->getRadius())
871 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
877 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " nm" << endl;
880 curr.CourseAndDistance(nodePos, &course, &dist);
881 if (!(i->hasHoldPosition()))
884 if ((dist < 2.5*current->getRadius()) &&
886 (!(current->getId() == i->getWaitsForId())) &&
887 (!(current->getSpeedAdjustment())))
890 current->setHoldPosition(true);
891 //cerr << "Hold check 5: " << id <<" Setting Hold Position: distance to node : " << dist << " nm"<< endl;
895 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
903 // Note that this function is probably obsolete...
904 bool FGGroundNetwork::hasInstruction(int id)
906 TrafficVectorIterator i = activeTraffic.begin();
907 // Search search if the current id has an entry
908 // This might be faster using a map instead of a vector, but let's start by taking a safe route
909 if (activeTraffic.size())
911 //while ((i->getId() != id) && i != activeTraffic.end()) {
912 while (i != activeTraffic.end()) {
913 if (i->getId() == id) {
919 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
920 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
922 return i->hasInstruction();
927 FGATCInstruction FGGroundNetwork::getInstruction(int id)
929 TrafficVectorIterator i = activeTraffic.begin();
930 // Search search if the current id has an entry
931 // This might be faster using a map instead of a vector, but let's start by taking a safe route
932 if (activeTraffic.size()) {
933 //while ((i->getId() != id) && i != activeTraffic.end()) {
934 while (i != activeTraffic.end()) {
935 if (i->getId() == id) {
941 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
942 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
944 return i->getInstruction();
946 return FGATCInstruction();