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"
52 /**************************************************************************
54 *************************************************************************/
55 FGTaxiNode::FGTaxiNode()
59 /***************************************************************************
61 **************************************************************************/
62 FGTaxiSegment::FGTaxiSegment()
64 oppositeDirection = 0;
67 void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
69 FGTaxiNodeVectorIterator i = nodes->begin();
70 while (i != nodes->end())
72 if (i->getIndex() == startNode)
74 start = i->getAddress();
82 void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
84 FGTaxiNodeVectorIterator i = nodes->begin();
85 while (i != nodes->end())
87 if (i->getIndex() == endNode)
89 end = i->getAddress();
96 // There is probably a computationally cheaper way of
98 void FGTaxiSegment::setTrackDistance()
101 SGWayPoint first (start->getLongitude(),
102 start->getLatitude(),
104 SGWayPoint second (end->getLongitude(),
107 first.CourseAndDistance(second, &course, &length);
109 /***************************************************************************
111 **************************************************************************/
112 bool FGTaxiRoute::next(int *nde)
114 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
115 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
116 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
117 //if (currNode != nodes.end())
118 // cerr << "true" << endl;
120 // cerr << "false" << endl;
121 //if (nodes.size() != (routes.size()) +1)
122 // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
124 if (currNode == nodes.end())
127 if (currNode != nodes.begin()) // make sure route corresponds to the end node
133 bool FGTaxiRoute::next(int *nde, int *rte)
135 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
136 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
137 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
138 //if (currNode != nodes.end())
139 // cerr << "true" << endl;
141 // cerr << "false" << endl;
142 if (nodes.size() != (routes.size()) +1) {
143 SG_LOG(SG_GENERAL, SG_ALERT, "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size());
146 if (currNode == nodes.end())
149 //*rte = *(currRoute);
150 if (currNode != nodes.begin()) // Make sure route corresponds to the end node
157 // If currNode points to the first node, this means the aircraft is not on the taxi node
158 // yet. Make sure to return a unique identifyer in this situation though, because otherwise
159 // the speed adjust AI code may be unable to resolve whether two aircraft are on the same
160 // taxi route or not. the negative of the preceding route seems a logical choice, as it is
161 // unique for any starting location.
162 // Note that this is probably just a temporary fix until I get Parking / tower control working.
163 *rte = -1 * *(currRoute);
169 void FGTaxiRoute::rewind(int route)
175 if (!(next(&currPoint, &currRoute))) {
176 SG_LOG(SG_GENERAL,SG_ALERT, "Error in rewinding TaxiRoute: current" << currRoute
177 << " goal " << route);
179 } while (currRoute != route);
182 /***************************************************************************
184 **************************************************************************/
185 void FGTrafficRecord::setPositionAndIntentions(int pos, FGAIFlightPlan *route)
189 if (intentions.size()) {
190 if (*intentions.begin() != pos) {
191 SG_LOG(SG_GENERAL, SG_ALERT, "Error in FGTrafficRecord::setPositionAndIntentions");
192 cerr << "Pos : " << pos << " Curr " << *(intentions.begin()) << endl;
193 for (intVecIterator i = intentions.begin(); i != intentions.end() ; i++) {
198 intentions.erase(intentions.begin());
200 //int legNr, routeNr;
201 //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
202 int size = route->getNrOfWayPoints();
203 cerr << "Setting pos" << pos << " ";
204 cerr << "setting intentions ";
205 for (int i = 0; i < size; i++) {
206 int val = route->getRouteIndex(i);
208 if ((val) && (val != pos))
210 intentions.push_back(val);
215 //while (route->next(&legNr, &routeNr)) {
216 //intentions.push_back(routeNr);
218 //route->rewind(currentPos);
223 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord &other)
226 //cerr << "Start check 1" << endl;
227 if (currentPos == other.currentPos)
229 //cerr << "Check Position and intentions: current matches" << endl;
232 // else if (other.intentions.size())
234 // cerr << "Start check 2" << endl;
235 // intVecIterator i = other.intentions.begin();
236 // while (!((i == other.intentions.end()) || ((*i) == currentPos)))
238 // if (i != other.intentions.end()) {
239 // cerr << "Check Position and intentions: current matches other.intentions" << endl;
242 else if (intentions.size()) {
243 //cerr << "Start check 3" << endl;
244 intVecIterator i = intentions.begin();
245 while (!((i == intentions.end()) || ((*i) == other.currentPos)))
247 if (i != intentions.end()) {
248 //cerr << "Check Position and intentions: .other.current matches" << endl;
252 //cerr << "Done !!" << endl;
256 void FGTrafficRecord::setPositionAndHeading(double lat, double lon, double hdg,
257 double spd, double alt)
266 int FGTrafficRecord::crosses(FGGroundNetwork *net, FGTrafficRecord &other)
268 if (checkPositionAndIntentions(other) || (other.checkPositionAndIntentions(*this)))
271 int currentTargetNode = 0, otherTargetNode = 0;
273 currentTargetNode = net->findSegment(currentPos )->getEnd()->getIndex(); // OKAY,...
274 if (other.currentPos > 0)
275 otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,...
276 if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
277 return currentTargetNode;
278 if (intentions.size())
280 for (i = intentions.begin(); i != intentions.end(); i++)
282 if (currentTargetNode == net->findSegment(*i)->getEnd()->getIndex())
284 cerr << "Current crosses at " << currentTargetNode <<endl;
285 return currentTargetNode;
290 if (other.intentions.size())
292 for (i = other.intentions.begin(); i != other.intentions.end(); i++)
294 if (otherTargetNode == net->findSegment(*i)->getEnd()->getIndex())
296 cerr << "Other crosses at " << currentTargetNode <<endl;
297 return otherTargetNode;
301 if (intentions.size() && other.intentions.size())
303 for (i = intentions.begin(); i != intentions.end(); i++)
305 for (j = other.intentions.begin(); j != other.intentions.end(); j++)
307 //cerr << "finding segment " << *i << " and " << *j << endl;
308 currentTargetNode = net->findSegment(*i)->getEnd()->getIndex();
309 otherTargetNode = net->findSegment(*j)->getEnd()->getIndex();
310 if (currentTargetNode == otherTargetNode)
312 //cerr << "Routes will cross at " << currentTargetNode << endl;
313 return currentTargetNode;
321 bool FGTrafficRecord::isOpposing (FGGroundNetwork *net, FGTrafficRecord &other, int node)
323 // Check if current segment is the reverse segment for the other aircraft
325 //cerr << "Current segment " << currentPos << endl;
326 if ((currentPos > 0) && (other.currentPos > 0))
328 opp = net->findSegment(currentPos)->opposite();
330 if (opp->getIndex() == other.currentPos)
334 for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
336 for (intVecIterator j = intentions.begin(); j != intentions.end(); j++)
338 // cerr << "Current segment 1 " << (*i) << endl;
339 if (opp = net->findSegment(*i)->opposite())
341 if (opp->getIndex() ==
342 net->findSegment(*j)->getIndex())
344 cerr << "Nodes " << net->findSegment(*i)->getIndex()
345 << " and " << net->findSegment(*j)->getIndex()
346 << " are opposites " << endl;
347 if (net->findSegment(*i)->getStart()->getIndex() == node) {
349 cerr << "Found the node" << endl;
362 /***************************************************************************
364 **************************************************************************/
366 FGGroundNetwork::FGGroundNetwork()
372 currTraffic = activeTraffic.begin();
376 void FGGroundNetwork::addSegment(const FGTaxiSegment &seg)
378 segments.push_back(seg);
381 void FGGroundNetwork::addNode(const FGTaxiNode &node)
383 nodes.push_back(node);
386 void FGGroundNetwork::addNodes(FGParkingVec *parkings)
389 FGParkingVecIterator i = parkings->begin();
390 while (i != parkings->end())
392 n.setIndex(i->getIndex());
393 n.setLatitude(i->getLatitude());
394 n.setLongitude(i->getLongitude());
403 void FGGroundNetwork::init()
407 sort(nodes.begin(), nodes.end());
408 sort(segments.begin(), segments.end());
409 FGTaxiSegmentVectorIterator i = segments.begin();
410 while(i != segments.end()) {
411 //cerr << "initializing node " << i->getIndex() << endl;
414 i->setTrackDistance();
416 //cerr << "Track distance = " << i->getLength() << endl;
417 //cerr << "Track ends at" << i->getEnd()->getIndex() << endl;
421 i = segments.begin();
422 while(i != segments.end()) {
423 FGTaxiSegmentPointerVectorIterator j = i->getEnd()->getBeginRoute();
424 while (j != i->getEnd()->getEndRoute())
426 if ((*j)->getEnd()->getIndex() == i->getStart()->getIndex())
428 int start1 = i->getStart()->getIndex();
429 int end1 = i->getEnd() ->getIndex();
430 int start2 = (*j)->getStart()->getIndex();
431 int end2 = (*j)->getEnd()->getIndex();
432 int oppIndex = (*j)->getIndex();
433 cerr << "Oppossite of " << i->getIndex() << " (" << start1 << "," << end1 << ") "
434 << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl;
444 int FGGroundNetwork::findNearestNode(double lat, double lon)
446 double minDist = HUGE_VAL;
449 SGWayPoint first (lon,
453 for (FGTaxiNodeVectorIterator
455 itr != nodes.end(); itr++)
458 SGWayPoint second (itr->getLongitude(),
461 first.CourseAndDistance(second, &course, &dist);
465 index = itr->getIndex();
466 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
472 FGTaxiNode *FGGroundNetwork::findNode(int idx)
474 for (FGTaxiNodeVectorIterator
476 itr != nodes.end(); itr++)
478 if (itr->getIndex() == idx)
479 return itr->getAddress();
482 if ((idx >= 0) && (idx < nodes.size()))
483 return nodes[idx].getAddress();
488 FGTaxiSegment *FGGroundNetwork::findSegment(int idx)
490 for (FGTaxiSegmentVectorIterator
491 itr = segments.begin();
492 itr != segments.end(); itr++)
494 if (itr->getIndex() == idx)
495 return itr->getAddress();
498 if ((idx > 0) && (idx <= segments.size()))
499 return segments[idx-1].getAddress();
502 cerr << "Alert: trying to find invalid segment " << idx << endl;
507 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end)
511 FGTaxiNode *firstNode = findNode(start);
512 FGTaxiNode *lastNode = findNode(end);
513 //prevNode = prevPrevNode = -1;
519 trace(firstNode, end, 0, 0);
524 SG_LOG( SG_GENERAL, SG_INFO, "Failed to find route from waypoint " << start << " to " << end );
527 sort(routes.begin(), routes.end());
528 //for (intVecIterator i = route.begin(); i != route.end(); i++)
530 // rte->push_back(*i);
533 if (routes.begin() != routes.end())
534 return *(routes.begin());
540 void FGGroundNetwork::trace(FGTaxiNode *currNode, int end, int depth, double distance)
542 // Just check some preconditions of the trace algorithm
543 if (nodesStack.size() != routesStack.size())
545 SG_LOG(SG_GENERAL, SG_ALERT, "size of nodesStack and routesStack is not equal. NodesStack :"
546 << nodesStack.size() << ". RoutesStack : " << routesStack.size());
548 nodesStack.push_back(currNode->getIndex());
549 totalDistance += distance;
550 //cerr << "Starting trace " << depth << " total distance: " << totalDistance<< endl;
551 //<< currNode->getIndex() << endl;
553 // If the current route matches the required end point we found a valid route
554 // So we can add this to the routing table
555 if (currNode->getIndex() == end)
557 //cerr << "Found route : " << totalDistance << "" << " " << *(nodesStack.end()-1) << endl;
558 routes.push_back(FGTaxiRoute(nodesStack,routesStack,totalDistance));
559 if (nodesStack.empty() || routesStack.empty())
561 printRoutingError(string("while finishing route"));
563 nodesStack.pop_back();
564 routesStack.pop_back();
566 maxDistance = totalDistance;
568 if (totalDistance < maxDistance)
569 maxDistance = totalDistance;
571 totalDistance -= distance;
576 // search if the currentNode has been encountered before
577 // if so, we should step back one level, because it is
578 // rather rediculous to proceed further from here.
579 // if the current node has not been encountered before,
580 // i should point to nodesStack.end()-1; and we can continue
581 // if i is not nodesStack.end, the previous node was found,
582 // and we should return.
583 // This only works at trace levels of 1 or higher though
585 intVecIterator i = nodesStack.begin();
586 while ((*i) != currNode->getIndex()) {
587 //cerr << "Route so far : " << (*i) << endl;
590 if (i != nodesStack.end()-1) {
591 if (nodesStack.empty() || routesStack.empty())
593 printRoutingError(string("while returning from an already encountered node"));
595 nodesStack.pop_back();
596 routesStack.pop_back();
597 totalDistance -= distance;
600 // If the total distance from start to the current waypoint
601 // is longer than that of a route we can also stop this trace
602 // and go back one level.
603 if ((totalDistance > maxDistance) && foundRoute)
605 //cerr << "Stopping rediculously long trace: " << totalDistance << endl;
606 if (nodesStack.empty() || routesStack.empty())
608 printRoutingError(string("while returning from finding a rediculously long route"));
610 nodesStack.pop_back();
611 routesStack.pop_back();
612 totalDistance -= distance;
617 //cerr << "2" << endl;
618 if (currNode->getBeginRoute() != currNode->getEndRoute())
620 //cerr << "3" << endl;
621 for (FGTaxiSegmentPointerVectorIterator
622 i = currNode->getBeginRoute();
623 i != currNode->getEndRoute();
626 //cerr << (*i)->getLength() << endl;
627 //cerr << (*i)->getIndex() << endl;
628 int idx = (*i)->getIndex();
629 routesStack.push_back((*i)->getIndex());
630 trace((*i)->getEnd(), end, depth+1, (*i)->getLength());
632 // // cerr << currNode -> getIndex() << " ";
633 // route.push_back(currNode->getIndex());
640 //SG_LOG( SG_GENERAL, SG_DEBUG, "4" );
642 if (nodesStack.empty())
644 printRoutingError(string("while finishing trace"));
646 nodesStack.pop_back();
647 // Make sure not to dump the level-zero routesStack entry, because that was never created.
650 routesStack.pop_back();
651 //cerr << "leaving trace " << routesStack.size() << endl;
653 totalDistance -= distance;
657 void FGGroundNetwork::printRoutingError(string mess)
659 SG_LOG(SG_GENERAL, SG_ALERT, "Error in ground network trace algorithm " << mess);
660 if (nodesStack.empty())
662 SG_LOG(SG_GENERAL, SG_ALERT, " nodesStack is empty. Dumping routesStack");
663 for (intVecIterator i = routesStack.begin() ; i != routesStack.end(); i++)
664 SG_LOG(SG_GENERAL, SG_ALERT, "Route " << (*i));
666 if (routesStack.empty())
668 SG_LOG(SG_GENERAL, SG_ALERT, " routesStack is empty. Dumping nodesStack");
669 for (intVecIterator i = nodesStack.begin() ; i != nodesStack.end(); i++)
670 SG_LOG(SG_GENERAL, SG_ALERT, "Node " << (*i));
676 void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
677 double lat, double lon, double heading,
678 double speed, double alt, double radius)
680 TrafficVectorIterator i = activeTraffic.begin();
681 // Search search if the current id alread has an entry
682 // This might be faster using a map instead of a vector, but let's start by taking a safe route
683 if (activeTraffic.size()) {
684 while ((i->getId() != id) && i != activeTraffic.end()) {
688 // Add a new TrafficRecord if no one exsists for this aircraft.
689 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
692 rec.setPositionAndIntentions(currentPosition, intendedRoute);
693 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
694 rec.setRadius(radius); // only need to do this when creating the record.
695 activeTraffic.push_back(rec);
697 i->setPositionAndIntentions(currentPosition, intendedRoute);
698 i->setPositionAndHeading(lat, lon, heading, speed, alt);
702 void FGGroundNetwork::signOff(int id) {
703 TrafficVectorIterator i = activeTraffic.begin();
704 // Search search if the current id alread 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 if (activeTraffic.size()) {
707 while ((i->getId() != id) && i != activeTraffic.end()) {
711 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
712 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off");
714 i = activeTraffic.erase(i);
718 void FGGroundNetwork::update(int id, double lat, double lon, double heading, double speed, double alt,
720 TrafficVectorIterator i = activeTraffic.begin();
721 // Search search if the current id has an entry
722 // This might be faster using a map instead of a vector, but let's start by taking a safe route
723 TrafficVectorIterator current, closest;
724 if (activeTraffic.size()) {
725 while ((i->getId() != id) && i != activeTraffic.end()) {
729 // update position of the current aircraft
730 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
731 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
733 i->setPositionAndHeading(lat, lon, heading, speed, alt);
739 // Update every three secs, but add some randomness
740 // to prevent all IA objects doing this in synchrony
741 //if (getDt() < (3.0) + (rand() % 10))
745 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
746 checkHoldPosition (id, lat, lon, heading, speed, alt);
749 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
750 double lon, double heading,
751 double speed, double alt)
754 // Scan for a speed adjustment change. Find the nearest aircraft that is in front
755 // and adjust speed when we get too close. Only do this when current position and/or
756 // intentions of the current aircraft match current taxiroute position of the proximate
757 // aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
758 // instruction. See below for the hold position instruction.
759 TrafficVectorIterator current, closest;
760 TrafficVectorIterator i = activeTraffic.begin();
761 if (activeTraffic.size())
763 while ((i->getId() != id) && (i != activeTraffic.end()))
773 double mindist = HUGE;
774 if (activeTraffic.size())
776 double course, dist, bearing, minbearing;
778 //TrafficVector iterator closest;
779 for (TrafficVectorIterator i = activeTraffic.begin();
780 i != activeTraffic.end(); i++)
783 SGWayPoint curr (lon,
786 SGWayPoint other (i->getLongitude (),
789 other.CourseAndDistance(curr, &course, &dist);
790 bearing = fabs(heading-course);
792 bearing = 360-bearing;
793 if ((dist < mindist) && (bearing < 60.0))
797 minbearing = bearing;
801 //cerr << "Distance : " << dist << " bearing : " << bearing << " heading : " << heading
802 // << " course : " << course << endl;
803 current->clearSpeedAdjustment();
804 // Only clear the heading adjustment at positive speeds, otherwise the waypoint following
807 current->clearHeadingAdjustment();
811 //current->clearSpeedAdjustment();
812 //current->clearHeadingAdjustment();
816 if (current->getId() == closest->getWaitsForId())
819 current->setWaitsForId(closest->getId());
821 // Getting close: Slow down to a bit less than the other aircraft
822 double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
823 if (mindist > maxAllowableDistance)
825 if (current->checkPositionAndIntentions(*closest))
827 // Adjust speed, but don't let it drop to below 1 knots
828 //if (fabs(speed) > 1)
829 if (!(current->hasHeadingAdjustment()))
831 current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
832 //cerr << "Adjusting speed to " << closest->getSpeed() * (mindist / 100) << " "
833 // << "Bearing = " << minbearing << " Distance = " << mindist
834 // << " Latitude = " <<lat << " longitude = " << lon << endl;
835 //<< " Latitude = " <<closest->getLatitude()
836 //<< " longitude = " << closest->getLongitude()
841 double newSpeed = (maxAllowableDistance-mindist);
842 current->setSpeedAdjustment(newSpeed);
848 if (!(current->hasHeadingAdjustment()))
853 current->setSpeedAdjustment(newSpeed);
855 newSpeed = -1 * (maxAllowableDistance-mindist);
856 current->setSpeedAdjustment(newSpeed);
857 current->setHeadingAdjustment(heading);
865 void FGGroundNetwork::checkHoldPosition(int id, double lat,
866 double lon, double heading,
867 double speed, double alt)
869 // Check for "Hold position instruction".
870 // The hold position should be issued under the following conditions:
871 // 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
872 // 2) For taxiing aircraft that use one taxiway in opposite directions
873 // 3) For crossing or merging taxiroutes.
875 TrafficVectorIterator current, closest;
876 TrafficVectorIterator i = activeTraffic.begin();
877 if (activeTraffic.size())
879 while ((i->getId() != id) && i != activeTraffic.end())
889 current->setHoldPosition(false);
890 double course, dist, bearing, minbearing;
891 for (i = activeTraffic.begin();
892 i != activeTraffic.end(); i++)
896 int node = current->crosses(this, *i);
899 // Determine whether it's save to continue or not.
900 // If we have a crossing route, there are two possibilities:
901 // 1) This is an interestion
902 // 2) This is oncoming two-way traffic, using the same taxiway.
903 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
904 SGWayPoint nodePos(findNode(node)->getLongitude (),
905 findNode(node)->getLatitude (),
907 SGWayPoint curr (lon,
911 SGWayPoint other (i->getLongitude (),
914 //other.CourseAndDistance(curr, &course, &dist);
916 if (current->isOpposing(this, *i, node))
919 //cerr << "Hold check 2 : " << id << " has opposing segment " << endl;
920 // issue a "Hold Position" as soon as we're close to the offending node
921 // For now, I'm doing this as long as the other aircraft doesn't
922 // have a hold instruction as soon as we're within a reasonable
923 // distance from the offending node.
924 // This may be a bit of a conservative estimate though, as it may
925 // be well possible that both aircraft can both continue to taxi
926 // without crashing into each other.
930 other.CourseAndDistance(nodePos, &course, &dist);
931 if (dist > 2.0*i->getRadius())
934 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
940 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " nm" << endl;
943 curr.CourseAndDistance(nodePos, &course, &dist);
944 if (!(i->hasHoldPosition()))
947 if ((dist < 2.5*current->getRadius()) &&
949 (!(current->getId() == i->getWaitsForId())) &&
950 (!(current->getSpeedAdjustment())))
953 current->setHoldPosition(true);
954 //cerr << "Hold check 5: " << id <<" Setting Hold Position: distance to node : " << dist << " nm"<< endl;
958 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
967 bool FGGroundNetwork::hasInstruction(int id)
969 TrafficVectorIterator i = activeTraffic.begin();
970 // Search search if the current id has an entry
971 // This might be faster using a map instead of a vector, but let's start by taking a safe route
972 if (activeTraffic.size())
974 while ((i->getId() != id) && i != activeTraffic.end()) {
978 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
979 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
981 return i->hasInstruction();
986 FGATCInstruction FGGroundNetwork::getInstruction(int id)
988 TrafficVectorIterator i = activeTraffic.begin();
989 // Search search if the current id has an entry
990 // This might be faster using a map instead of a vector, but let's start by taking a safe route
991 if (activeTraffic.size()) {
992 while ((i->getId() != id) && i != activeTraffic.end()) {
996 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
997 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
999 return i->getInstruction();
1001 return FGATCInstruction();
1007 /***************************************************************************
1010 * This class is really out of place here, and should be combined with
1011 * FGATC controller and go into it's own file / directory
1012 * I'm leaving it for now though, because I'm testing this stuff quite
1014 **************************************************************************/
1015 FGATCInstruction::FGATCInstruction()
1017 holdPattern = false;
1018 holdPosition = false;
1019 changeSpeed = false;
1020 changeHeading = false;
1021 changeAltitude = false;
1028 bool FGATCInstruction::hasInstruction()
1030 return (holdPattern || holdPosition || changeSpeed || changeHeading || changeAltitude);