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 <simgear/debug/logstream.hxx>
31 #include <simgear/route/waypoint.hxx>
33 #include <Airports/simple.hxx>
34 #include <Airports/dynamics.hxx>
36 #include <AIModel/AIAircraft.hxx>
37 #include <AIModel/AIFlightPlan.hxx>
39 #include "groundnetwork.hxx"
41 /***************************************************************************
43 **************************************************************************/
45 void FGTaxiSegment::setStart(FGTaxiNodeVector * nodes)
47 FGTaxiNodeVectorIterator i = nodes->begin();
48 while (i != nodes->end()) {
49 //cerr << "Scanning start node index" << (*i)->getIndex() << endl;
50 if ((*i)->getIndex() == startNode) {
51 start = (*i)->getAddress();
52 (*i)->addSegment(this);
57 SG_LOG(SG_GENERAL, SG_ALERT,
58 "Could not find start node " << startNode << endl);
61 void FGTaxiSegment::setEnd(FGTaxiNodeVector * nodes)
63 FGTaxiNodeVectorIterator i = nodes->begin();
64 while (i != nodes->end()) {
65 //cerr << "Scanning end node index" << (*i)->getIndex() << endl;
66 if ((*i)->getIndex() == endNode) {
67 end = (*i)->getAddress();
72 SG_LOG(SG_GENERAL, SG_ALERT,
73 "Could not find end node " << endNode << endl);
78 // There is probably a computationally cheaper way of
80 void FGTaxiSegment::setTrackDistance()
82 length = SGGeodesy::distanceM(start->getGeod(), end->getGeod());
86 void FGTaxiSegment::setCourseDiff(double crse)
88 headingDiff = fabs(course - crse);
90 if (headingDiff > 180)
91 headingDiff = fabs(headingDiff - 360);
95 /***************************************************************************
97 **************************************************************************/
98 bool FGTaxiRoute::next(int *nde)
100 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
101 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
102 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
103 //if (currNode != nodes.end())
104 // cerr << "true" << endl;
106 // cerr << "false" << endl;
107 //if (nodes.size() != (routes.size()) +1)
108 // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
110 if (currNode == nodes.end())
113 if (currNode != nodes.begin()) // make sure route corresponds to the end node
119 bool FGTaxiRoute::next(int *nde, int *rte)
121 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
122 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
123 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
124 //if (currNode != nodes.end())
125 // cerr << "true" << endl;
127 // cerr << "false" << endl;
128 if (nodes.size() != (routes.size()) + 1) {
129 SG_LOG(SG_GENERAL, SG_ALERT,
130 "ALERT: Misconfigured TaxiRoute : " << nodes.
131 size() << " " << routes.size());
134 if (currNode == nodes.end())
137 //*rte = *(currRoute);
138 if (currNode != nodes.begin()) // Make sure route corresponds to the end node
143 // If currNode points to the first node, this means the aircraft is not on the taxi node
144 // yet. Make sure to return a unique identifyer in this situation though, because otherwise
145 // the speed adjust AI code may be unable to resolve whether two aircraft are on the same
146 // taxi route or not. the negative of the preceding route seems a logical choice, as it is
147 // unique for any starting location.
148 // Note that this is probably just a temporary fix until I get Parking / tower control working.
149 *rte = -1 * *(currRoute);
155 void FGTaxiRoute::rewind(int route)
161 if (!(next(&currPoint, &currRoute))) {
162 SG_LOG(SG_GENERAL, SG_ALERT,
163 "Error in rewinding TaxiRoute: current" << currRoute <<
166 } while (currRoute != route);
172 /***************************************************************************
174 **************************************************************************/
175 bool compare_nodes(FGTaxiNode * a, FGTaxiNode * b)
180 bool compare_segments(FGTaxiSegment * a, FGTaxiSegment * b)
185 FGGroundNetwork::FGGroundNetwork()
193 currTraffic = activeTraffic.begin();
197 FGGroundNetwork::~FGGroundNetwork()
199 for (FGTaxiNodeVectorIterator node = nodes.begin();
200 node != nodes.end(); node++) {
204 pushBackNodes.clear();
205 for (FGTaxiSegmentVectorIterator seg = segments.begin();
206 seg != segments.end(); seg++) {
212 void FGGroundNetwork::addSegment(const FGTaxiSegment & seg)
214 segments.push_back(new FGTaxiSegment(seg));
217 void FGGroundNetwork::addNode(const FGTaxiNode & node)
219 nodes.push_back(new FGTaxiNode(node));
222 void FGGroundNetwork::addNodes(FGParkingVec * parkings)
225 FGParkingVecIterator i = parkings->begin();
226 while (i != parkings->end()) {
227 n.setIndex(i->getIndex());
228 n.setLatitude(i->getLatitude());
229 n.setLongitude(i->getLongitude());
230 nodes.push_back(new FGTaxiNode(n));
238 void FGGroundNetwork::init()
242 sort(nodes.begin(), nodes.end(), compare_nodes);
243 //sort(segments.begin(), segments.end(), compare_segments());
244 FGTaxiSegmentVectorIterator i = segments.begin();
245 while (i != segments.end()) {
246 (*i)->setStart(&nodes);
247 (*i)->setEnd(&nodes);
248 (*i)->setTrackDistance();
249 (*i)->setIndex(index);
250 if ((*i)->isPushBack()) {
251 pushBackNodes.push_back((*i)->getEnd());
253 //SG_LOG(SG_GENERAL, SG_BULK, "initializing segment " << (*i)->getIndex() << endl);
254 //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = " << (*i)->getLength() << endl);
255 //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from " << (*i)->getStart()->getIndex() << " to "
256 // << (*i)->getEnd()->getIndex() << endl);
261 i = segments.begin();
262 while (i != segments.end()) {
263 FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute();
264 while (j != (*i)->getEnd()->getEndRoute()) {
265 if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex()) {
266 // int start1 = (*i)->getStart()->getIndex();
267 // int end1 = (*i)->getEnd() ->getIndex();
268 // int start2 = (*j)->getStart()->getIndex();
269 // int end2 = (*j)->getEnd()->getIndex();
270 // int oppIndex = (*j)->getIndex();
271 //cerr << "Opposite of " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
272 // << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl;
273 (*i)->setOpposite(*j);
280 //FGTaxiNodeVectorIterator j = nodes.begin();
281 //while (j != nodes.end()) {
282 // if ((*j)->getHoldPointType() == 3) {
283 // pushBackNodes.push_back((*j));
287 //cerr << "Done initializing ground network" << endl;
291 int FGGroundNetwork::findNearestNode(const SGGeod & aGeod)
293 double minDist = HUGE_VAL;
296 for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end();
298 double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod());
301 index = (*itr)->getIndex();
302 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
309 int FGGroundNetwork::findNearestNode(double lat, double lon)
311 return findNearestNode(SGGeod::fromDeg(lon, lat));
314 FGTaxiNode *FGGroundNetwork::findNode(unsigned idx)
316 for (FGTaxiNodeVectorIterator
318 itr != nodes.end(); itr++)
320 if (itr->getIndex() == idx)
321 return itr->getAddress();
324 if ((idx >= 0) && (idx < nodes.size()))
325 return nodes[idx]->getAddress();
330 FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx)
332 for (FGTaxiSegmentVectorIterator
333 itr = segments.begin();
334 itr != segments.end(); itr++)
336 if (itr->getIndex() == idx)
337 return itr->getAddress();
340 if ((idx > 0) && (idx <= segments.size()))
341 return segments[idx - 1]->getAddress();
343 //cerr << "Alert: trying to find invalid segment " << idx << endl;
349 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end,
352 //implements Dijkstra's algorithm to find shortest distance route from start to end
353 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
355 //double INFINITE = 100000000000.0;
356 // initialize scoring values
357 int nParkings = parent->getDynamics()->getNrOfParkings();
358 FGTaxiNodeVector *currNodesSet;
360 currNodesSet = &nodes;
362 currNodesSet = &pushBackNodes;
365 for (FGTaxiNodeVectorIterator
366 itr = currNodesSet->begin(); itr != currNodesSet->end(); itr++) {
367 (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
368 (*itr)->setPreviousNode(0); //
369 (*itr)->setPreviousSeg(0); //
372 FGTaxiNode *firstNode = findNode(start);
373 firstNode->setPathScore(0);
375 FGTaxiNode *lastNode = findNode(end);
377 FGTaxiNodeVector unvisited(*currNodesSet); // working copy
379 while (!unvisited.empty()) {
380 FGTaxiNode *best = *(unvisited.begin());
381 for (FGTaxiNodeVectorIterator
382 itr = unvisited.begin(); itr != unvisited.end(); itr++) {
383 if ((*itr)->getPathScore() < best->getPathScore())
387 FGTaxiNodeVectorIterator newend =
388 remove(unvisited.begin(), unvisited.end(), best);
389 unvisited.erase(newend, unvisited.end());
391 if (best == lastNode) { // found route or best not connected
394 for (FGTaxiSegmentVectorIterator
395 seg = best->getBeginRoute();
396 seg != best->getEndRoute(); seg++) {
397 if (fullSearch || (*seg)->isPushBack()) {
398 FGTaxiNode *tgt = (*seg)->getEnd();
400 best->getPathScore() + (*seg)->getLength() +
401 (*seg)->getPenalty(nParkings);
402 if (alt < tgt->getPathScore()) { // Relax (u,v)
403 tgt->setPathScore(alt);
404 tgt->setPreviousNode(best);
405 tgt->setPreviousSeg(*seg); //
408 // // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
414 if (lastNode->getPathScore() == HUGE_VAL) {
415 // no valid route found
417 SG_LOG(SG_GENERAL, SG_ALERT,
418 "Failed to find route from waypoint " << start << " to "
419 << end << " at " << parent->getId());
423 //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
425 // assemble route from backtrace information
426 intVec nodes, routes;
427 FGTaxiNode *bt = lastNode;
428 while (bt->getPreviousNode() != 0) {
429 nodes.push_back(bt->getIndex());
430 routes.push_back(bt->getPreviousSegment()->getIndex());
431 bt = bt->getPreviousNode();
433 nodes.push_back(start);
434 reverse(nodes.begin(), nodes.end());
435 reverse(routes.begin(), routes.end());
437 return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
441 int FGTaxiSegment::getPenalty(int nGates)
444 if (end->getIndex() < nGates) {
447 if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
453 /* ATC Related Functions */
455 void FGGroundNetwork::announcePosition(int id,
456 FGAIFlightPlan * intendedRoute,
457 int currentPosition, double lat,
458 double lon, double heading,
459 double speed, double alt,
460 double radius, int leg,
461 FGAIAircraft * aircraft)
463 TrafficVectorIterator i = activeTraffic.begin();
464 // Search search if the current id alread has an entry
465 // This might be faster using a map instead of a vector, but let's start by taking a safe route
466 if (activeTraffic.size()) {
467 //while ((i->getId() != id) && i != activeTraffic.end()) {
468 while (i != activeTraffic.end()) {
469 if (i->getId() == id) {
475 // Add a new TrafficRecord if no one exsists for this aircraft.
476 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
480 rec.setPositionAndIntentions(currentPosition, intendedRoute);
481 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
482 rec.setRadius(radius); // only need to do this when creating the record.
483 rec.setAircraft(aircraft);
484 activeTraffic.push_back(rec);
486 i->setPositionAndIntentions(currentPosition, intendedRoute);
487 i->setPositionAndHeading(lat, lon, heading, speed, alt);
491 void FGGroundNetwork::signOff(int id)
493 TrafficVectorIterator i = activeTraffic.begin();
494 // Search search if the current id alread has an entry
495 // This might be faster using a map instead of a vector, but let's start by taking a safe route
496 if (activeTraffic.size()) {
497 //while ((i->getId() != id) && i != activeTraffic.end()) {
498 while (i != activeTraffic.end()) {
499 if (i->getId() == id) {
505 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
506 SG_LOG(SG_GENERAL, SG_ALERT,
507 "AI error: Aircraft without traffic record is signing off");
509 i = activeTraffic.erase(i);
513 void FGGroundNetwork::update(int id, double lat, double lon,
514 double heading, double speed, double alt,
517 // Check whether aircraft are on hold due to a preceding pushback. If so, make sure to
518 // Transmit air-to-ground "Ready to taxi request:
519 // Transmit ground to air approval / hold
520 // Transmit confirmation ...
521 // Probably use a status mechanism similar to the Engine start procedure in the startup controller.
524 TrafficVectorIterator i = activeTraffic.begin();
525 // Search search if the current id has an entry
526 // This might be faster using a map instead of a vector, but let's start by taking a safe route
527 TrafficVectorIterator current, closest;
528 if (activeTraffic.size()) {
529 //while ((i->getId() != id) && i != activeTraffic.end()) {
530 while (i != activeTraffic.end()) {
531 if (i->getId() == id) {
537 // update position of the current aircraft
538 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
539 SG_LOG(SG_GENERAL, SG_ALERT,
540 "AI error: updating aircraft without traffic record");
542 i->setPositionAndHeading(lat, lon, heading, speed, alt);
548 // Update every three secs, but add some randomness
549 // to prevent all IA objects doing this in synchrony
550 //if (getDt() < (3.0) + (rand() % 10))
554 current->clearResolveCircularWait();
555 current->setWaitsForId(0);
556 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
557 bool needsTaxiClearance = current->getAircraft()->getTaxiClearanceRequest();
558 if (!needsTaxiClearance) {
559 checkHoldPosition(id, lat, lon, heading, speed, alt);
560 if (checkForCircularWaits(id)) {
561 i->setResolveCircularWait();
564 current->setHoldPosition(true);
565 int state = current->getState();
566 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
567 if ((now - lastTransmission) > 15) {
570 if ((state < 3) && available) {
571 transmit(&(*current), MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND);
572 current->setState(3);
573 lastTransmission = now;
576 if ((state == 3) && available) {
577 transmit(&(*current), MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR);
578 current->setState(4);
579 lastTransmission = now;
582 if ((state == 4) && available) {
583 transmit(&(*current), MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND);
584 current->setState(5);
585 lastTransmission = now;
588 if ((state == 5) && available) {
589 current->setState(0);
590 current->getAircraft()->setTaxiClearanceRequest(false);
591 current->setHoldPosition(true);
600 Scan for a speed adjustment change. Find the nearest aircraft that is in front
601 and adjust speed when we get too close. Only do this when current position and/or
602 intentions of the current aircraft match current taxiroute position of the proximate
603 aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
604 instruction. See below for the hold position instruction.
606 Note that there currently still is one flaw in the logic that needs to be addressed.
607 There can be situations where one aircraft is in front of the current aircraft, on a separate
608 route, but really close after an intersection coming off the current route. This
609 aircraft is still close enough to block the current aircraft. This situation is currently
610 not addressed yet, but should be.
613 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
614 double lon, double heading,
615 double speed, double alt)
618 TrafficVectorIterator current, closest;
619 TrafficVectorIterator i = activeTraffic.begin();
620 bool otherReasonToSlowDown = false;
621 bool previousInstruction;
622 if (activeTraffic.size()) {
623 //while ((i->getId() != id) && (i != activeTraffic.end()))
624 while (i != activeTraffic.end()) {
625 if (i->getId() == id) {
633 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
634 SG_LOG(SG_GENERAL, SG_ALERT,
635 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
640 previousInstruction = current->getSpeedAdjustment();
641 double mindist = HUGE_VAL;
642 if (activeTraffic.size()) {
643 double course, dist, bearing, minbearing, az2;
644 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
645 //TrafficVector iterator closest;
647 for (TrafficVectorIterator i = activeTraffic.begin();
648 i != activeTraffic.end(); i++) {
653 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
656 SGGeodesy::inverse(curr, other, course, az2, dist);
657 bearing = fabs(heading - course);
659 bearing = 360 - bearing;
660 if ((dist < mindist) && (bearing < 60.0)) {
663 minbearing = bearing;
666 //Check traffic at the tower controller
667 if (towerController->hasActiveTraffic()) {
668 for (TrafficVectorIterator i =
669 towerController->getActiveTraffic().begin();
670 i != towerController->getActiveTraffic().end(); i++) {
671 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
672 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
675 SGGeodesy::inverse(curr, other, course, az2, dist);
676 bearing = fabs(heading - course);
678 bearing = 360 - bearing;
679 if ((dist < mindist) && (bearing < 60.0)) {
682 minbearing = bearing;
683 otherReasonToSlowDown = true;
687 // Finally, check UserPosition
688 double userLatitude = fgGetDouble("/position/latitude-deg");
689 double userLongitude = fgGetDouble("/position/longitude-deg");
690 SGGeod user(SGGeod::fromDeg(userLongitude, userLatitude));
691 SGGeodesy::inverse(curr, user, course, az2, dist);
693 bearing = fabs(heading - course);
695 bearing = 360 - bearing;
696 if ((dist < mindist) && (bearing < 60.0)) {
699 minbearing = bearing;
700 otherReasonToSlowDown = true;
703 current->clearSpeedAdjustment();
705 if (current->checkPositionAndIntentions(*closest)
706 || otherReasonToSlowDown) {
707 double maxAllowableDistance =
708 (1.1 * current->getRadius()) +
709 (1.1 * closest->getRadius());
710 if (mindist < 2 * maxAllowableDistance) {
711 if (current->getId() == closest->getWaitsForId())
714 current->setWaitsForId(closest->getId());
715 if (closest->getId() != current->getId())
716 current->setSpeedAdjustment(closest->getSpeed() *
719 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
720 if (mindist < maxAllowableDistance) {
721 //double newSpeed = (maxAllowableDistance-mindist);
722 //current->setSpeedAdjustment(newSpeed);
723 //if (mindist < 0.5* maxAllowableDistance)
725 current->setSpeedAdjustment(0);
734 Check for "Hold position instruction".
735 The hold position should be issued under the following conditions:
736 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
737 2) For taxiing aircraft that use one taxiway in opposite directions
738 3) For crossing or merging taxiroutes.
741 void FGGroundNetwork::checkHoldPosition(int id, double lat,
742 double lon, double heading,
743 double speed, double alt)
745 TrafficVectorIterator current;
746 TrafficVectorIterator i = activeTraffic.begin();
747 if (activeTraffic.size()) {
748 //while ((i->getId() != id) && i != activeTraffic.end())
749 while (i != activeTraffic.end()) {
750 if (i->getId() == id) {
758 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
759 SG_LOG(SG_GENERAL, SG_ALERT,
760 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
763 bool origStatus = current->hasHoldPosition();
764 current->setHoldPosition(false);
765 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
767 for (i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
768 if (i->getId() != current->getId()) {
769 int node = current->crosses(this, *i);
771 FGTaxiNode *taxiNode = findNode(node);
773 // Determine whether it's save to continue or not.
774 // If we have a crossing route, there are two possibilities:
775 // 1) This is an interestion
776 // 2) This is oncoming two-way traffic, using the same taxiway.
777 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
779 SGGeod other(SGGeod::
780 fromDegM(i->getLongitude(), i->getLatitude(),
784 if (current->isOpposing(this, *i, node)) {
787 //cerr << "Hold check 2 : " << node << " has opposing segment " << endl;
788 // issue a "Hold Position" as soon as we're close to the offending node
789 // For now, I'm doing this as long as the other aircraft doesn't
790 // have a hold instruction as soon as we're within a reasonable
791 // distance from the offending node.
792 // This may be a bit of a conservative estimate though, as it may
793 // be well possible that both aircraft can both continue to taxi
794 // without crashing into each other.
797 if (SGGeodesy::distanceM(other, taxiNode->getGeod()) > 200) // 2.0*i->getRadius())
800 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
804 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
809 SGGeodesy::distanceM(curr, taxiNode->getGeod());
810 if (!(i->hasHoldPosition())) {
812 if ((dist < 200) && //2.5*current->getRadius()) &&
813 (needsToWait) && (i->onRoute(this, *current)) &&
814 //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
815 (!(current->getId() == i->getWaitsForId())))
816 //(!(i->getSpeedAdjustment()))) // &&
817 //(!(current->getSpeedAdjustment())))
820 current->setHoldPosition(true);
821 current->setWaitsForId(i->getId());
822 //cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
823 // << dist << " meters. Waiting for " << i->getCallSign();
825 //cerr <<" [opposing] " << endl;
827 // cerr << "[non-opposing] " << endl;
828 //if (i->hasSpeefAdjustment())
830 // cerr << " (which in turn waits for ) " << i->
832 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
838 bool currStatus = current->hasHoldPosition();
840 // Either a Hold Position or a resume taxi transmission has been issued
841 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
842 if ((now - lastTransmission) > 2) {
845 if ((origStatus != currStatus) && available) {
846 //cerr << "Issueing hold short instrudtion " << currStatus << " " << available << endl;
847 if (currStatus == true) { // No has a hold short instruction
848 transmit(&(*current), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR);
849 //cerr << "Transmittin hold short instrudtion " << currStatus << " " << available << endl;
850 current->setState(1);
852 transmit(&(*current), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR);
853 //cerr << "Transmittig resume instrudtion " << currStatus << " " << available << endl;
854 current->setState(2);
856 lastTransmission = now;
858 // Don't act on the changed instruction until the transmission is confirmed
859 // So set back to original status
860 current->setHoldPosition(origStatus);
861 //cerr << "Current state " << current->getState() << endl;
864 int state = current->getState();
865 if ((state == 1) && (available)) {
866 //cerr << "ACKNOWLEDGE HOLD" << endl;
867 transmit(&(*current), MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND);
868 current->setState(0);
869 current->setHoldPosition(true);
870 lastTransmission = now;
874 if ((state == 2) && (available)) {
875 //cerr << "ACKNOWLEDGE RESUME" << endl;
876 transmit(&(*current), MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND);
877 current->setState(0);
878 current->setHoldPosition(false);
879 lastTransmission = now;
885 * Check whether situations occur where the current aircraft is waiting for itself
886 * due to higher order interactions.
887 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
888 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
889 * through this list of waiting aircraft, we can check if we'd eventually end back
890 * at the current aircraft.
892 * Note that we should consider the situation where we are actually checking aircraft
893 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
894 * the looping aircraft. If we don't check for that, this function will get stuck into
898 bool FGGroundNetwork::checkForCircularWaits(int id)
900 //cerr << "Performing Wait check " << id << endl;
902 TrafficVectorIterator current, other;
903 TrafficVectorIterator i = activeTraffic.begin();
904 int trafficSize = activeTraffic.size();
906 while (i != activeTraffic.end()) {
907 if (i->getId() == id) {
915 if (i == activeTraffic.end() || (trafficSize == 0)) {
916 SG_LOG(SG_GENERAL, SG_ALERT,
917 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
921 target = current->getWaitsForId();
922 //bool printed = false; // Note that this variable is for debugging purposes only.
926 //cerr << "aircraft waits for user" << endl;
931 while ((target > 0) && (target != id) && counter++ < trafficSize) {
933 TrafficVectorIterator i = activeTraffic.begin();
935 //while ((i->getId() != id) && i != activeTraffic.end())
936 while (i != activeTraffic.end()) {
937 if (i->getId() == target) {
945 if (i == activeTraffic.end() || (trafficSize == 0)) {
946 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
947 // The target id is not found on the current network, which means it's at the tower
948 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
952 target = other->getWaitsForId();
954 // actually this trap isn't as impossible as it first seemed:
955 // the setWaitsForID(id) is set to current when the aircraft
956 // is waiting for the user controlled aircraft.
957 //if (current->getId() == other->getId()) {
958 // cerr << "Caught the impossible trap" << endl;
959 // cerr << "Current = " << current->getId() << endl;
960 // cerr << "Other = " << other ->getId() << endl;
961 // for (TrafficVectorIterator at = activeTraffic.begin();
962 // at != activeTraffic.end();
964 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
967 if (current->getId() == other->getId())
970 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
971 // << " (" << other->getId() << "); " << endl;;
981 // cerr << "[done] " << endl << endl;;
983 SG_LOG(SG_GENERAL, SG_WARN,
984 "Detected circular wait condition: Id = " << id <<
985 "target = " << target);
992 // Note that this function is probably obsolete...
993 bool FGGroundNetwork::hasInstruction(int id)
995 TrafficVectorIterator i = activeTraffic.begin();
996 // Search search if the current id has an entry
997 // This might be faster using a map instead of a vector, but let's start by taking a safe route
998 if (activeTraffic.size()) {
999 //while ((i->getId() != id) && i != activeTraffic.end()) {
1000 while (i != activeTraffic.end()) {
1001 if (i->getId() == id) {
1007 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1008 SG_LOG(SG_GENERAL, SG_ALERT,
1009 "AI error: checking ATC instruction for aircraft without traffic record");
1011 return i->hasInstruction();
1016 FGATCInstruction FGGroundNetwork::getInstruction(int id)
1018 TrafficVectorIterator i = activeTraffic.begin();
1019 // Search search if the current id has an entry
1020 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1021 if (activeTraffic.size()) {
1022 //while ((i->getId() != id) && i != activeTraffic.end()) {
1023 while (i != activeTraffic.end()) {
1024 if (i->getId() == id) {
1030 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1031 SG_LOG(SG_GENERAL, SG_ALERT,
1032 "AI error: requesting ATC instruction for aircraft without traffic record");
1034 return i->getInstruction();
1036 return FGATCInstruction();