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.
32 #include <osg/Geometry>
33 #include <osg/MatrixTransform>
36 #include <simgear/debug/logstream.hxx>
37 #include <simgear/route/waypoint.hxx>
38 #include <simgear/scene/material/EffectGeode.hxx>
39 #include <simgear/scene/material/matlib.hxx>
40 #include <simgear/scene/material/mat.hxx>
42 #include <Airports/simple.hxx>
43 #include <Airports/dynamics.hxx>
45 #include <AIModel/AIAircraft.hxx>
46 #include <AIModel/AIFlightPlan.hxx>
48 #include <ATC/atc_mgr.hxx>
50 #include <Scenery/scenery.hxx>
52 #include "groundnetwork.hxx"
54 /***************************************************************************
56 **************************************************************************/
58 void FGTaxiSegment::setStart(FGTaxiNodeVector * nodes)
60 FGTaxiNodeVectorIterator i = nodes->begin();
61 while (i != nodes->end()) {
62 //cerr << "Scanning start node index" << (*i)->getIndex() << endl;
63 if ((*i)->getIndex() == startNode) {
64 start = (*i)->getAddress();
65 (*i)->addSegment(this);
70 SG_LOG(SG_GENERAL, SG_ALERT,
71 "Could not find start node " << startNode << endl);
74 void FGTaxiSegment::setEnd(FGTaxiNodeVector * nodes)
76 FGTaxiNodeVectorIterator i = nodes->begin();
77 while (i != nodes->end()) {
78 //cerr << "Scanning end node index" << (*i)->getIndex() << endl;
79 if ((*i)->getIndex() == endNode) {
80 end = (*i)->getAddress();
85 SG_LOG(SG_GENERAL, SG_ALERT,
86 "Could not find end node " << endNode << endl);
91 // There is probably a computationally cheaper way of
93 void FGTaxiSegment::setDimensions(double elevation)
95 length = SGGeodesy::distanceM(start->getGeod(), end->getGeod());
96 //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
98 double az2; //, distanceM;
99 SGGeodesy::inverse(start->getGeod(), end->getGeod(), heading, az2, length);
100 double coveredDistance = length * 0.5;
101 SGGeodesy::direct(start->getGeod(), heading, coveredDistance, center, az2);
102 //start->setElevation(elevation);
103 //end->setElevation(elevation);
104 //cerr << "Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
108 //void FGTaxiSegment::setCourseDiff(double crse)
110 // headingDiff = fabs(course - crse);
112 // if (headingDiff > 180)
113 // headingDiff = fabs(headingDiff - 360);
117 /***************************************************************************
119 **************************************************************************/
120 bool FGTaxiRoute::next(int *nde)
122 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
123 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
124 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
125 //if (currNode != nodes.end())
126 // cerr << "true" << endl;
128 // cerr << "false" << endl;
129 //if (nodes.size() != (routes.size()) +1)
130 // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
132 if (currNode == nodes.end())
135 if (currNode != nodes.begin()) // make sure route corresponds to the end node
141 bool FGTaxiRoute::next(int *nde, int *rte)
143 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
144 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
145 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
146 //if (currNode != nodes.end())
147 // cerr << "true" << endl;
149 // cerr << "false" << endl;
150 if (nodes.size() != (routes.size()) + 1) {
151 SG_LOG(SG_GENERAL, SG_ALERT,
152 "ALERT: Misconfigured TaxiRoute : " << nodes.
153 size() << " " << routes.size());
156 if (currNode == nodes.end())
159 //*rte = *(currRoute);
160 if (currNode != nodes.begin()) // Make sure route corresponds to the end node
165 // If currNode points to the first node, this means the aircraft is not on the taxi node
166 // yet. Make sure to return a unique identifyer in this situation though, because otherwise
167 // the speed adjust AI code may be unable to resolve whether two aircraft are on the same
168 // taxi route or not. the negative of the preceding route seems a logical choice, as it is
169 // unique for any starting location.
170 // Note that this is probably just a temporary fix until I get Parking / tower control working.
171 *rte = -1 * *(currRoute);
178 void FGTaxiRoute::rewind(int route)
184 if (!(next(&currPoint, &currRoute))) {
185 SG_LOG(SG_GENERAL, SG_ALERT,
186 "Error in rewinding TaxiRoute: current" << currRoute <<
189 } while (currRoute != route);
195 /***************************************************************************
197 **************************************************************************/
198 bool compare_nodes(FGTaxiNode * a, FGTaxiNode * b)
203 bool compare_segments(FGTaxiSegment * a, FGTaxiSegment * b)
208 bool compare_trafficrecords(FGTrafficRecord a, FGTrafficRecord b)
210 return (a.getIntentions().size() < b.getIntentions().size());
213 FGGroundNetwork::FGGroundNetwork()
221 currTraffic = activeTraffic.begin();
223 networkInitialized = false;
227 FGGroundNetwork::~FGGroundNetwork()
229 //cerr << "Running Groundnetwork Destructor " << endl;
230 bool saveData = false;
232 if (fgGetBool("/sim/ai/groundnet-cache")) {
233 SGPath cacheData(fgGetString("/sim/fg-home"));
234 cacheData.append("ai");
235 string airport = parent->getId();
237 if ((airport) != "") {
239 ::snprintf(buffer, 128, "%c/%c/%c/",
240 airport[0], airport[1], airport[2]);
241 cacheData.append(buffer);
242 if (!cacheData.exists()) {
243 cacheData.create_dir(0777);
245 cacheData.append(airport + "-groundnet-cache.txt");
246 cachefile.open(cacheData.str().c_str());
250 cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl;
251 for (FGTaxiNodeVectorIterator node = nodes.begin();
252 node != nodes.end(); node++) {
254 cachefile << (*node)->getIndex () << " "
255 << (*node)->getElevation () << " "
261 pushBackNodes.clear();
262 for (FGTaxiSegmentVectorIterator seg = segments.begin();
263 seg != segments.end(); seg++) {
272 void FGGroundNetwork::saveElevationCache() {
273 //cerr << "Running Groundnetwork Destructor " << endl;
274 bool saveData = false;
276 if (fgGetBool("/sim/ai/groundnet-cache")) {
277 SGPath cacheData(fgGetString("/sim/fg-home"));
278 cacheData.append("ai");
279 string airport = parent->getId();
281 if ((airport) != "") {
283 ::snprintf(buffer, 128, "%c/%c/%c/",
284 airport[0], airport[1], airport[2]);
285 cacheData.append(buffer);
286 if (!cacheData.exists()) {
287 cacheData.create_dir(0777);
289 cacheData.append(airport + "-groundnet-cache.txt");
290 cachefile.open(cacheData.str().c_str());
294 cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl;
295 for (FGTaxiNodeVectorIterator node = nodes.begin();
296 node != nodes.end(); node++) {
298 cachefile << (*node)->getIndex () << " "
299 << (*node)->getElevation () << " "
308 void FGGroundNetwork::addSegment(const FGTaxiSegment & seg)
310 segments.push_back(new FGTaxiSegment(seg));
313 void FGGroundNetwork::addNode(const FGTaxiNode & node)
315 nodes.push_back(new FGTaxiNode(node));
318 void FGGroundNetwork::addNodes(FGParkingVec * parkings)
321 FGParkingVecIterator i = parkings->begin();
322 while (i != parkings->end()) {
323 n.setIndex(i->getIndex());
324 n.setLatitude(i->getLatitude());
325 n.setLongitude(i->getLongitude());
326 n.setElevation(parent->getElevation());
327 nodes.push_back(new FGTaxiNode(n));
335 void FGGroundNetwork::init()
337 if (networkInitialized) {
338 FGATCController::init();
339 //cerr << "FGground network already initialized" << endl;
345 sort(nodes.begin(), nodes.end(), compare_nodes);
346 //sort(segments.begin(), segments.end(), compare_segments());
347 FGTaxiSegmentVectorIterator i = segments.begin();
348 while (i != segments.end()) {
349 (*i)->setStart(&nodes);
350 (*i)->setEnd(&nodes);
351 (*i)->setDimensions(parent->getElevation() * SG_FEET_TO_METER);
352 (*i)->setIndex(index);
353 if ((*i)->isPushBack()) {
354 pushBackNodes.push_back((*i)->getEnd());
356 //SG_LOG(SG_GENERAL, SG_BULK, "initializing segment " << (*i)->getIndex() << endl);
357 //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = " << (*i)->getLength() << endl);
358 //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from " << (*i)->getStart()->getIndex() << " to "
359 // << (*i)->getEnd()->getIndex() << endl);
364 i = segments.begin();
365 while (i != segments.end()) {
366 FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute();
367 while (j != (*i)->getEnd()->getEndRoute()) {
368 if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex()) {
369 // int start1 = (*i)->getStart()->getIndex();
370 // int end1 = (*i)->getEnd() ->getIndex();
371 // int start2 = (*j)->getStart()->getIndex();
372 // int end2 = (*j)->getEnd()->getIndex();
373 // int oppIndex = (*j)->getIndex();
374 //cerr << "Opposite of " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
375 // << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl;
376 (*i)->setOpposite(*j);
383 //FGTaxiNodeVectorIterator j = nodes.begin();
384 //while (j != nodes.end()) {
385 // if ((*j)->getHoldPointType() == 3) {
386 // pushBackNodes.push_back((*j));
390 //cerr << "Done initializing ground network" << endl;
392 if (fgGetBool("/sim/ai/groundnet-cache")) {
393 SGPath cacheData(fgGetString("/sim/fg-home"));
394 cacheData.append("ai");
395 string airport = parent->getId();
397 if ((airport) != "") {
399 ::snprintf(buffer, 128, "%c/%c/%c/",
400 airport[0], airport[1], airport[2]);
401 cacheData.append(buffer);
402 if (!cacheData.exists()) {
403 cacheData.create_dir(0777);
407 cacheData.append(airport + "-groundnet-cache.txt");
408 if (cacheData.exists()) {
409 ifstream data(cacheData.c_str());
412 if (revisionStr != "[GroundNetcachedata:ref:2011:09:04]") {
413 SG_LOG(SG_GENERAL, SG_ALERT,"GroundNetwork Warning: discarding outdated cachefile " <<
414 cacheData.c_str() << " for Airport " << airport);
416 for (FGTaxiNodeVectorIterator i = nodes.begin();
419 (*i)->setElevation(parent->getElevation() * SG_FEET_TO_METER);
420 data >> index >> elev;
423 if (index != (*i)->getIndex()) {
424 SG_LOG(SG_GENERAL, SG_ALERT, "Index read from ground network cache at airport " << airport << " does not match index in the network itself");
426 (*i)->setElevation(elev);
433 //cerr << "Finished initializing " << parent->getId() << " groundnetwork " << endl;
434 networkInitialized = true;
437 int FGGroundNetwork::findNearestNode(const SGGeod & aGeod)
439 double minDist = HUGE_VAL;
442 for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end();
444 double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod());
447 index = (*itr)->getIndex();
448 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
455 int FGGroundNetwork::findNearestNode(double lat, double lon)
457 return findNearestNode(SGGeod::fromDeg(lon, lat));
460 FGTaxiNode *FGGroundNetwork::findNode(unsigned idx)
462 for (FGTaxiNodeVectorIterator
464 itr != nodes.end(); itr++)
466 if (itr->getIndex() == idx)
467 return itr->getAddress();
470 if ((idx >= 0) && (idx < nodes.size()))
471 return nodes[idx]->getAddress();
476 FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx)
478 for (FGTaxiSegmentVectorIterator
479 itr = segments.begin();
480 itr != segments.end(); itr++)
482 if (itr->getIndex() == idx)
483 return itr->getAddress();
486 if ((idx > 0) && (idx <= segments.size()))
487 return segments[idx - 1]->getAddress();
489 //cerr << "Alert: trying to find invalid segment " << idx << endl;
495 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end,
498 //implements Dijkstra's algorithm to find shortest distance route from start to end
499 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
501 //double INFINITE = 100000000000.0;
502 // initialize scoring values
503 int nParkings = parent->getDynamics()->getNrOfParkings();
504 FGTaxiNodeVector *currNodesSet;
506 currNodesSet = &nodes;
508 currNodesSet = &pushBackNodes;
511 for (FGTaxiNodeVectorIterator
512 itr = currNodesSet->begin(); itr != currNodesSet->end(); itr++) {
513 (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
514 (*itr)->setPreviousNode(0); //
515 (*itr)->setPreviousSeg(0); //
518 FGTaxiNode *firstNode = findNode(start);
519 firstNode->setPathScore(0);
521 FGTaxiNode *lastNode = findNode(end);
523 FGTaxiNodeVector unvisited(*currNodesSet); // working copy
525 while (!unvisited.empty()) {
526 FGTaxiNode *best = *(unvisited.begin());
527 for (FGTaxiNodeVectorIterator
528 itr = unvisited.begin(); itr != unvisited.end(); itr++) {
529 if ((*itr)->getPathScore() < best->getPathScore())
533 FGTaxiNodeVectorIterator newend =
534 remove(unvisited.begin(), unvisited.end(), best);
535 unvisited.erase(newend, unvisited.end());
537 if (best == lastNode) { // found route or best not connected
540 for (FGTaxiSegmentVectorIterator
541 seg = best->getBeginRoute();
542 seg != best->getEndRoute(); seg++) {
543 if (fullSearch || (*seg)->isPushBack()) {
544 FGTaxiNode *tgt = (*seg)->getEnd();
546 best->getPathScore() + (*seg)->getLength() +
547 (*seg)->getPenalty(nParkings);
548 if (alt < tgt->getPathScore()) { // Relax (u,v)
549 tgt->setPathScore(alt);
550 tgt->setPreviousNode(best);
551 tgt->setPreviousSeg(*seg); //
554 // // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
560 if (lastNode->getPathScore() == HUGE_VAL) {
561 // no valid route found
563 SG_LOG(SG_GENERAL, SG_ALERT,
564 "Failed to find route from waypoint " << start << " to "
565 << end << " at " << parent->getId());
569 //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
571 // assemble route from backtrace information
572 intVec nodes, routes;
573 FGTaxiNode *bt = lastNode;
574 while (bt->getPreviousNode() != 0) {
575 nodes.push_back(bt->getIndex());
576 routes.push_back(bt->getPreviousSegment()->getIndex());
577 bt = bt->getPreviousNode();
579 nodes.push_back(start);
580 reverse(nodes.begin(), nodes.end());
581 reverse(routes.begin(), routes.end());
583 return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
587 int FGTaxiSegment::getPenalty(int nGates)
590 if (end->getIndex() < nGates) {
593 if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
599 /* ATC Related Functions */
601 void FGGroundNetwork::announcePosition(int id,
602 FGAIFlightPlan * intendedRoute,
603 int currentPosition, double lat,
604 double lon, double heading,
605 double speed, double alt,
606 double radius, int leg,
607 FGAIAircraft * aircraft)
610 TrafficVectorIterator i = activeTraffic.begin();
611 // Search search if the current id alread has an entry
612 // This might be faster using a map instead of a vector, but let's start by taking a safe route
613 if (activeTraffic.size()) {
614 //while ((i->getId() != id) && i != activeTraffic.end()) {
615 while (i != activeTraffic.end()) {
616 if (i->getId() == id) {
622 // Add a new TrafficRecord if no one exsists for this aircraft.
623 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
627 rec.setPositionAndIntentions(currentPosition, intendedRoute);
628 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
629 rec.setRadius(radius); // only need to do this when creating the record.
630 rec.setAircraft(aircraft);
631 activeTraffic.push_front(rec);
634 i->setPositionAndIntentions(currentPosition, intendedRoute);
635 i->setPositionAndHeading(lat, lon, heading, speed, alt);
640 void FGGroundNetwork::signOff(int id)
642 TrafficVectorIterator i = activeTraffic.begin();
643 // Search search if the current id alread has an entry
644 // This might be faster using a map instead of a vector, but let's start by taking a safe route
645 if (activeTraffic.size()) {
646 //while ((i->getId() != id) && i != activeTraffic.end()) {
647 while (i != activeTraffic.end()) {
648 if (i->getId() == id) {
654 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
655 SG_LOG(SG_GENERAL, SG_ALERT,
656 "AI error: Aircraft without traffic record is signing off");
658 i = activeTraffic.erase(i);
662 * The ground network can deal with the following states:
663 * 0 = Normal; no action required
664 * 1 = "Acknowledge "Hold position
665 * 2 = "Acknowledge "Resume taxi".
666 * 3 = "Issue TaxiClearance"
667 * 4 = Acknowledge Taxi Clearance"
668 * 5 = Post acknowlegde taxiclearance: Start taxiing
670 * 7 = Acknowledge report runway
671 * 8 = Switch tower frequency
672 * 9 = Acknowledge switch tower frequency
673 *************************************************************************************************************************/
674 bool FGGroundNetwork::checkTransmissionState(int minState, int maxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId,
677 int state = i->getState();
678 if ((state >= minState) && (state <= maxState) && available) {
679 if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
680 //cerr << "Checking state " << state << " for " << i->getAircraft()->getCallSign() << endl;
681 static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
682 int n = trans_num->getIntValue();
684 trans_num->setIntValue(-1);
686 //cerr << "Selected transmission message " << n << endl;
687 FGATCManager *atc = (FGATCManager*) globals->get_subsystem("atc");
688 FGATCDialogNew::instance()->removeEntry(1);
690 //cerr << "creating message for " << i->getAircraft()->getCallSign() << endl;
691 transmit(&(*i), msgId, msgDir, false);
695 transmit(&(*i), msgId, msgDir, true);
697 lastTransmission = now;
704 void FGGroundNetwork::updateAircraftInformation(int id, double lat, double lon,
705 double heading, double speed, double alt,
708 time_t currentTime = time(NULL);
709 if (nextSave < currentTime) {
710 saveElevationCache();
711 nextSave = currentTime + 100 + rand() % 200;
713 // Check whether aircraft are on hold due to a preceding pushback. If so, make sure to
714 // Transmit air-to-ground "Ready to taxi request:
715 // Transmit ground to air approval / hold
716 // Transmit confirmation ...
717 // Probably use a status mechanism similar to the Engine start procedure in the startup controller.
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()) {
726 while (i != activeTraffic.end()) {
727 if (i->getId() == id) {
733 // update position of the current aircraft
734 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
735 SG_LOG(SG_GENERAL, SG_ALERT,
736 "AI error: updating aircraft without traffic record");
738 i->setPositionAndHeading(lat, lon, heading, speed, alt);
744 // Update every three secs, but add some randomness
745 // to prevent all IA objects doing this in synchrony
746 //if (getDt() < (3.0) + (rand() % 10))
750 current->clearResolveCircularWait();
751 current->setWaitsForId(0);
752 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
753 bool needsTaxiClearance = current->getAircraft()->getTaxiClearanceRequest();
754 if (!needsTaxiClearance) {
755 checkHoldPosition(id, lat, lon, heading, speed, alt);
756 //if (checkForCircularWaits(id)) {
757 // i->setResolveCircularWait();
760 current->setHoldPosition(true);
761 int state = current->getState();
762 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
763 if ((now - lastTransmission) > 15) {
766 if (checkTransmissionState(0,2, current, now, MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) {
767 current->setState(3);
769 if (checkTransmissionState(3,3, current, now, MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR)) {
770 current->setState(4);
772 if (checkTransmissionState(4,4, current, now, MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) {
773 current->setState(5);
775 if ((state == 5) && available) {
776 current->setState(0);
777 current->getAircraft()->setTaxiClearanceRequest(false);
778 current->setHoldPosition(false);
786 Scan for a speed adjustment change. Find the nearest aircraft that is in front
787 and adjust speed when we get too close. Only do this when current position and/or
788 intentions of the current aircraft match current taxiroute position of the proximate
789 aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
790 instruction. See below for the hold position instruction.
792 Note that there currently still is one flaw in the logic that needs to be addressed.
793 There can be situations where one aircraft is in front of the current aircraft, on a separate
794 route, but really close after an intersection coming off the current route. This
795 aircraft is still close enough to block the current aircraft. This situation is currently
796 not addressed yet, but should be.
799 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
800 double lon, double heading,
801 double speed, double alt)
804 TrafficVectorIterator current, closest, closestOnNetwork;
805 TrafficVectorIterator i = activeTraffic.begin();
806 bool otherReasonToSlowDown = false;
807 bool previousInstruction;
808 if (activeTraffic.size()) {
809 //while ((i->getId() != id) && (i != activeTraffic.end()))
810 while (i != activeTraffic.end()) {
811 if (i->getId() == id) {
819 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
820 SG_LOG(SG_GENERAL, SG_ALERT,
821 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
826 previousInstruction = current->getSpeedAdjustment();
827 double mindist = HUGE_VAL;
828 if (activeTraffic.size()) {
829 double course, dist, bearing, minbearing, az2;
830 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
831 //TrafficVector iterator closest;
833 for (TrafficVectorIterator i = activeTraffic.begin();
834 i != activeTraffic.end(); i++) {
839 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
842 SGGeodesy::inverse(curr, other, course, az2, dist);
843 bearing = fabs(heading - course);
845 bearing = 360 - bearing;
846 if ((dist < mindist) && (bearing < 60.0)) {
849 closestOnNetwork = i;
850 minbearing = bearing;
854 //Check traffic at the tower controller
855 if (towerController->hasActiveTraffic()) {
856 for (TrafficVectorIterator i =
857 towerController->getActiveTraffic().begin();
858 i != towerController->getActiveTraffic().end(); i++) {
859 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
860 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
863 SGGeodesy::inverse(curr, other, course, az2, dist);
864 bearing = fabs(heading - course);
866 bearing = 360 - bearing;
867 if ((dist < mindist) && (bearing < 60.0)) {
868 //cerr << "Current aircraft " << current->getAircraft()->getTrafficRef()->getCallSign()
869 // << " is closest to " << i->getAircraft()->getTrafficRef()->getCallSign()
870 // << ", which has status " << i->getAircraft()->isScheduledForTakeoff()
874 minbearing = bearing;
875 otherReasonToSlowDown = true;
879 // Finally, check UserPosition
880 // Note, as of 2011-08-01, this should no longer be necessecary.
882 double userLatitude = fgGetDouble("/position/latitude-deg");
883 double userLongitude = fgGetDouble("/position/longitude-deg");
884 SGGeod user(SGGeod::fromDeg(userLongitude, userLatitude));
885 SGGeodesy::inverse(curr, user, course, az2, dist);
887 bearing = fabs(heading - course);
889 bearing = 360 - bearing;
890 if ((dist < mindist) && (bearing < 60.0)) {
893 minbearing = bearing;
894 otherReasonToSlowDown = true;
897 current->clearSpeedAdjustment();
898 bool needBraking = false;
899 if (current->checkPositionAndIntentions(*closest)
900 || otherReasonToSlowDown) {
901 double maxAllowableDistance =
902 (1.1 * current->getRadius()) +
903 (1.1 * closest->getRadius());
904 if (mindist < 2 * maxAllowableDistance) {
905 if (current->getId() == closest->getWaitsForId())
908 current->setWaitsForId(closest->getId());
909 if (closest->getId() != current->getId()) {
910 current->setSpeedAdjustment(closest->getSpeed() *
914 closest->getAircraft()->getTakeOffStatus() &&
915 (current->getAircraft()->getTrafficRef()->getDepartureAirport() == closest->getAircraft()->getTrafficRef()->getDepartureAirport()) &&
916 (current->getAircraft()->GetFlightPlan()->getRunway() == closest->getAircraft()->GetFlightPlan()->getRunway())
918 current->getAircraft()->scheduleForATCTowerDepartureControl(1);
920 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
922 if (mindist < maxAllowableDistance) {
923 //double newSpeed = (maxAllowableDistance-mindist);
924 //current->setSpeedAdjustment(newSpeed);
925 //if (mindist < 0.5* maxAllowableDistance)
927 current->setSpeedAdjustment(0);
932 if ((closest == closestOnNetwork) && (current->getPriority() < closest->getPriority()) && needBraking) {
933 swap(current, closest);
939 Check for "Hold position instruction".
940 The hold position should be issued under the following conditions:
941 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
942 2) For taxiing aircraft that use one taxiway in opposite directions
943 3) For crossing or merging taxiroutes.
946 void FGGroundNetwork::checkHoldPosition(int id, double lat,
947 double lon, double heading,
948 double speed, double alt)
950 TrafficVectorIterator current;
951 TrafficVectorIterator i = activeTraffic.begin();
952 if (activeTraffic.size()) {
953 //while ((i->getId() != id) && i != activeTraffic.end())
954 while (i != activeTraffic.end()) {
955 if (i->getId() == id) {
963 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
964 SG_LOG(SG_GENERAL, SG_ALERT,
965 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
968 bool origStatus = current->hasHoldPosition();
969 current->setHoldPosition(false);
970 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
971 int currentRoute = i->getCurrentPosition();
973 if (i->getIntentions().size()) {
974 nextRoute = (*(i->getIntentions().begin()));
978 if (currentRoute > 0) {
979 FGTaxiSegment *tx = findSegment(currentRoute);
982 nx = findSegment(nextRoute);
986 if (tx->hasBlock() || nx->hasBlock() ) {
987 current->setHoldPosition(true);
992 /* for (i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
993 if (i->getId() != current->getId()) {
994 int node = current->crosses(this, *i);
996 FGTaxiNode *taxiNode = findNode(node);
998 // Determine whether it's save to continue or not.
999 // If we have a crossing route, there are two possibilities:
1000 // 1) This is an interestion
1001 // 2) This is oncoming two-way traffic, using the same taxiway.
1002 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
1004 SGGeod other(SGGeod::
1005 fromDegM(i->getLongitude(), i->getLatitude(),
1009 if (current->isOpposing(this, *i, node)) {
1012 //cerr << "Hold check 2 : " << node << " has opposing segment " << endl;
1013 // issue a "Hold Position" as soon as we're close to the offending node
1014 // For now, I'm doing this as long as the other aircraft doesn't
1015 // have a hold instruction as soon as we're within a reasonable
1016 // distance from the offending node.
1017 // This may be a bit of a conservative estimate though, as it may
1018 // be well possible that both aircraft can both continue to taxi
1019 // without crashing into each other.
1022 if (SGGeodesy::distanceM(other, taxiNode->getGeod()) > 200) // 2.0*i->getRadius())
1024 needsToWait = false;
1025 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
1029 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
1034 SGGeodesy::distanceM(curr, taxiNode->getGeod());
1035 if (!(i->hasHoldPosition())) {
1037 if ((dist < 200) && //2.5*current->getRadius()) &&
1038 (needsToWait) && (i->onRoute(this, *current)) &&
1039 //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
1040 (!(current->getId() == i->getWaitsForId())))
1041 //(!(i->getSpeedAdjustment()))) // &&
1042 //(!(current->getSpeedAdjustment())))
1045 if (!(isUserAircraft(i->getAircraft()))) { // test code. Don't wait for the user, let the user wait for you.
1046 current->setHoldPosition(true);
1047 current->setWaitsForId(i->getId());
1049 //cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
1050 // << dist << " meters. Waiting for " << i->getCallSign();
1052 //cerr <<" [opposing] " << endl;
1054 // cerr << "[non-opposing] " << endl;
1055 //if (i->hasSpeefAdjustment())
1057 // cerr << " (which in turn waits for ) " << i->
1059 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
1065 bool currStatus = current->hasHoldPosition();
1066 current->setHoldPosition(origStatus);
1067 // Either a Hold Position or a resume taxi transmission has been issued
1068 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1069 if ((now - lastTransmission) > 2) {
1072 if (current->getState() == 0) {
1073 if ((origStatus != currStatus) && available) {
1074 //cerr << "Issueing hold short instrudtion " << currStatus << " " << available << endl;
1075 if (currStatus == true) { // No has a hold short instruction
1076 transmit(&(*current), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR, true);
1077 //cerr << "Transmittin hold short instrudtion " << currStatus << " " << available << endl;
1078 current->setState(1);
1080 transmit(&(*current), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR, true);
1081 //cerr << "Transmittig resume instrudtion " << currStatus << " " << available << endl;
1082 current->setState(2);
1084 lastTransmission = now;
1086 // Don't act on the changed instruction until the transmission is confirmed
1087 // So set back to original status
1088 //cerr << "Current state " << current->getState() << endl;
1092 // 6 = Report runway
1093 // 7 = Acknowledge report runway
1094 // 8 = Switch tower frequency
1095 //9 = Acknowledge switch tower frequency
1097 //int state = current->getState();
1098 if (checkTransmissionState(1,1, current, now, MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND)) {
1099 current->setState(0);
1100 current->setHoldPosition(true);
1102 if (checkTransmissionState(2,2, current, now, MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND)) {
1103 current->setState(0);
1104 current->setHoldPosition(false);
1106 if (current->getAircraft()->getTakeOffStatus() && (current->getState() == 0)) {
1107 //cerr << "Scheduling " << current->getAircraft()->getCallSign() << " for hold short" << endl;
1108 current->setState(6);
1110 if (checkTransmissionState(6,6, current, now, MSG_REPORT_RUNWAY_HOLD_SHORT, ATC_AIR_TO_GROUND)) {
1112 if (checkTransmissionState(7,7, current, now, MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT, ATC_GROUND_TO_AIR)) {
1114 if (checkTransmissionState(8,8, current, now, MSG_SWITCH_TOWER_FREQUENCY, ATC_GROUND_TO_AIR)) {
1116 if (checkTransmissionState(9,9, current, now, MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY, ATC_AIR_TO_GROUND)) {
1121 //current->setState(0);
1125 * Check whether situations occur where the current aircraft is waiting for itself
1126 * due to higher order interactions.
1127 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
1128 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
1129 * through this list of waiting aircraft, we can check if we'd eventually end back
1130 * at the current aircraft.
1132 * Note that we should consider the situation where we are actually checking aircraft
1133 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
1134 * the looping aircraft. If we don't check for that, this function will get stuck into
1138 bool FGGroundNetwork::checkForCircularWaits(int id)
1140 //cerr << "Performing Wait check " << id << endl;
1142 TrafficVectorIterator current, other;
1143 TrafficVectorIterator i = activeTraffic.begin();
1144 int trafficSize = activeTraffic.size();
1146 while (i != activeTraffic.end()) {
1147 if (i->getId() == id) {
1155 if (i == activeTraffic.end() || (trafficSize == 0)) {
1156 SG_LOG(SG_GENERAL, SG_ALERT,
1157 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
1161 target = current->getWaitsForId();
1162 //bool printed = false; // Note that this variable is for debugging purposes only.
1166 //cerr << "aircraft waits for user" << endl;
1171 while ((target > 0) && (target != id) && counter++ < trafficSize) {
1173 TrafficVectorIterator i = activeTraffic.begin();
1175 //while ((i->getId() != id) && i != activeTraffic.end())
1176 while (i != activeTraffic.end()) {
1177 if (i->getId() == target) {
1185 if (i == activeTraffic.end() || (trafficSize == 0)) {
1186 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
1187 // The target id is not found on the current network, which means it's at the tower
1188 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
1192 target = other->getWaitsForId();
1194 // actually this trap isn't as impossible as it first seemed:
1195 // the setWaitsForID(id) is set to current when the aircraft
1196 // is waiting for the user controlled aircraft.
1197 //if (current->getId() == other->getId()) {
1198 // cerr << "Caught the impossible trap" << endl;
1199 // cerr << "Current = " << current->getId() << endl;
1200 // cerr << "Other = " << other ->getId() << endl;
1201 // for (TrafficVectorIterator at = activeTraffic.begin();
1202 // at != activeTraffic.end();
1204 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
1207 if (current->getId() == other->getId())
1210 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
1211 // << " (" << other->getId() << "); " << endl;;
1221 // cerr << "[done] " << endl << endl;;
1223 SG_LOG(SG_GENERAL, SG_WARN,
1224 "Detected circular wait condition: Id = " << id <<
1225 "target = " << target);
1232 // Note that this function is probably obsolete...
1233 bool FGGroundNetwork::hasInstruction(int id)
1235 TrafficVectorIterator i = activeTraffic.begin();
1236 // Search search if the current id has an entry
1237 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1238 if (activeTraffic.size()) {
1239 //while ((i->getId() != id) && i != activeTraffic.end()) {
1240 while (i != activeTraffic.end()) {
1241 if (i->getId() == id) {
1247 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1248 SG_LOG(SG_GENERAL, SG_ALERT,
1249 "AI error: checking ATC instruction for aircraft without traffic record");
1251 return i->hasInstruction();
1256 FGATCInstruction FGGroundNetwork::getInstruction(int id)
1258 TrafficVectorIterator i = activeTraffic.begin();
1259 // Search search if the current id has an entry
1260 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1261 if (activeTraffic.size()) {
1262 //while ((i->getId() != id) && i != activeTraffic.end()) {
1263 while (i != activeTraffic.end()) {
1264 if (i->getId() == id) {
1270 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1271 SG_LOG(SG_GENERAL, SG_ALERT,
1272 "AI error: requesting ATC instruction for aircraft without traffic record");
1274 return i->getInstruction();
1276 return FGATCInstruction();
1279 // Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
1280 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
1281 double lon, double elev, double hdg, double slope)
1283 SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
1284 obj_pos = geod.makeZUpFrame();
1285 // hdg is not a compass heading, but a counter-clockwise rotation
1286 // around the Z axis
1287 obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
1289 obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
1296 void FGGroundNetwork::render(bool visible)
1299 SGMaterialLib *matlib = globals->get_matlib();
1302 globals->get_scenery()->get_scene_graph()->removeChild(group);
1303 //while (group->getNumChildren()) {
1304 // cerr << "Number of children: " << group->getNumChildren() << endl;
1305 //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
1306 //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
1307 //geode->releaseGLObjects();
1308 //group->removeChild(geode);
1313 group = new osg::Group;
1314 FGScenery * local_scenery = globals->get_scenery();
1315 double elevation_meters = 0.0;
1316 double elevation_feet = 0.0;
1317 //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
1319 for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1320 // Handle start point
1321 int pos = i->getCurrentPosition() - 1;
1324 SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1325 SGGeod end (SGGeod::fromDeg(segments[pos]->getEnd()->getLongitude(), segments[pos]->getEnd()->getLatitude()));
1327 double length = SGGeodesy::distanceM(start, end);
1328 //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
1330 double az2, heading; //, distanceM;
1331 SGGeodesy::inverse(start, end, heading, az2, length);
1332 double coveredDistance = length * 0.5;
1334 SGGeodesy::direct(start, heading, coveredDistance, center, az2);
1335 //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
1336 ///////////////////////////////////////////////////////////////////////////////
1337 // Make a helper function out of this
1338 osg::Matrix obj_pos;
1339 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1340 obj_trans->setDataVariance(osg::Object::STATIC);
1341 // Experimental: Calculate slope here, based on length, and the individual elevations
1342 double elevationStart;
1343 if (isUserAircraft((i)->getAircraft())) {
1344 elevationStart = fgGetDouble("/position/ground-elev-m");
1346 elevationStart = ((i)->getAircraft()->_getAltitude());
1348 double elevationEnd = segments[pos]->getEnd()->getElevation();
1349 //cerr << "Using elevation " << elevationEnd << endl;
1351 if ((elevationEnd == 0) || (elevationEnd = parent->getElevation())) {
1352 SGGeod center2 = end;
1353 center2.setElevationM(SG_MAX_ELEVATION_M);
1354 if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1355 elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1356 //elevation_meters += 0.5;
1359 elevationEnd = parent->getElevation();
1361 segments[pos]->getEnd()->setElevation(elevationEnd);
1363 double elevationMean = (elevationStart + elevationEnd) / 2.0;
1364 double elevDiff = elevationEnd - elevationStart;
1366 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1368 //cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
1370 WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean+ 0.5, -(heading), slope );
1372 obj_trans->setMatrix( obj_pos );
1373 //osg::Vec3 center(0, 0, 0)
1375 float width = length /2.0;
1376 osg::Vec3 corner(-width, 0, 0.25f);
1377 osg::Vec3 widthVec(2*width + 1, 0, 0);
1378 osg::Vec3 heightVec(0, 1, 0);
1379 osg::Geometry* geometry;
1380 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1381 simgear::EffectGeode* geode = new simgear::EffectGeode;
1382 geode->setName("test");
1383 geode->addDrawable(geometry);
1384 //osg::Node *custom_obj;
1386 if (segments[pos]->hasBlock()) {
1387 mat = matlib->find("UnidirectionalTaperRed");
1389 mat = matlib->find("UnidirectionalTaperGreen");
1392 geode->setEffect(mat->get_effect());
1393 obj_trans->addChild(geode);
1394 // wire as much of the scene graph together as we can
1395 //->addChild( obj_trans );
1396 group->addChild( obj_trans );
1397 /////////////////////////////////////////////////////////////////////
1399 //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
1401 for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
1402 osg::Matrix obj_pos;
1405 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1406 obj_trans->setDataVariance(osg::Object::STATIC);
1408 // Experimental: Calculate slope here, based on length, and the individual elevations
1409 double elevationStart = segments[k]->getStart()->getElevation();
1410 double elevationEnd = segments[k]->getEnd ()->getElevation();
1411 if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
1412 SGGeod center2 = segments[k]->getStart()->getGeod();
1413 center2.setElevationM(SG_MAX_ELEVATION_M);
1414 if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
1415 elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
1416 //elevation_meters += 0.5;
1419 elevationStart = parent->getElevation();
1421 segments[k]->getStart()->setElevation(elevationStart);
1423 if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1424 SGGeod center2 = segments[k]->getEnd()->getGeod();
1425 center2.setElevationM(SG_MAX_ELEVATION_M);
1426 if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1427 elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1428 //elevation_meters += 0.5;
1431 elevationEnd = parent->getElevation();
1433 segments[k]->getEnd()->setElevation(elevationEnd);
1436 double elevationMean = (elevationStart + elevationEnd) / 2.0;
1437 double elevDiff = elevationEnd - elevationStart;
1438 double length = segments[k]->getLength();
1439 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1441 // cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
1444 WorldCoordinate( obj_pos, segments[k]->getLatitude(), segments[k]->getLongitude(), elevationMean+ 0.5, -(segments[k]->getHeading()), slope );
1446 obj_trans->setMatrix( obj_pos );
1447 //osg::Vec3 center(0, 0, 0)
1449 float width = segments[k]->getLength() /2.0;
1450 osg::Vec3 corner(-width, 0, 0.25f);
1451 osg::Vec3 widthVec(2*width + 1, 0, 0);
1452 osg::Vec3 heightVec(0, 1, 0);
1453 osg::Geometry* geometry;
1454 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1455 simgear::EffectGeode* geode = new simgear::EffectGeode;
1456 geode->setName("test");
1457 geode->addDrawable(geometry);
1458 //osg::Node *custom_obj;
1460 if (segments[k]->hasBlock()) {
1461 mat = matlib->find("UnidirectionalTaperRed");
1463 mat = matlib->find("UnidirectionalTaperGreen");
1466 geode->setEffect(mat->get_effect());
1467 obj_trans->addChild(geode);
1468 // wire as much of the scene graph together as we can
1469 //->addChild( obj_trans );
1470 group->addChild( obj_trans );
1475 globals->get_scenery()->get_scene_graph()->addChild(group);
1479 string FGGroundNetwork::getName() {
1480 return string(parent->getId() + "-ground");
1483 void FGGroundNetwork::update(double dt)
1485 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1489 //sort(activeTraffic.begin(), activeTraffic.end(), compare_trafficrecords);
1490 // Handle traffic that is under ground control first; this way we'll prevent clutter at the gate areas.
1491 // Don't allow an aircraft to pushback when a taxiing aircraft is currently using part of the intended route.
1492 for (TrafficVectorIterator i = parent->getDynamics()->getStartupController()->getActiveTraffic().begin();
1493 i != parent->getDynamics()->getStartupController()->getActiveTraffic().end(); i++) {
1495 i->setPriority(priority++);
1496 if (i->isActive(60)) {
1498 // Check for all active aircraft whether it's current pos segment is
1499 // an opposite of one of the departing aircraft's intentions
1500 for (TrafficVectorIterator j = activeTraffic.begin(); j != activeTraffic.end(); j++) {
1501 int pos = j->getCurrentPosition();
1503 FGTaxiSegment *seg = segments[pos-1]->opposite();
1505 int posReverse = seg->getIndex();
1506 for (intVecIterator k = i->getIntentions().begin(); k != i->getIntentions().end(); k++) {
1507 if ((*k) == posReverse) {
1509 segments[posReverse-1]->block();
1515 // if the current aircraft is still allowed to pushback, we can start reserving a route for if by blocking all the entry taxiways.
1516 if (i->pushBackAllowed()) {
1517 int pos = i->getCurrentPosition();
1519 FGTaxiSegment *seg = segments[pos-1];
1520 FGTaxiNode *node = seg->getEnd();
1521 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1522 if (((*tsi)->getEnd() == node) && ((*tsi) != seg)) {
1527 for (intVecIterator j = i->getIntentions().begin(); j != i->getIntentions().end(); j++) {
1530 FGTaxiSegment *seg = segments[pos-1];
1531 FGTaxiNode *node = seg->getEnd();
1532 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1533 if (((*tsi)->getEnd() == node) && ((*tsi) != seg)) {
1542 for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1543 i->setPriority(priority++);
1544 int pos = i->getCurrentPosition();
1546 if (segments[pos-1]->hasBlock()) {
1547 SG_LOG(SG_GENERAL, SG_ALERT, "Taxiway incursion for AI aircraft" << i->getAircraft()->getCallSign());
1552 for (ivi = i->getIntentions().begin(); ivi != i->getIntentions().end(); ivi++) {
1553 int segIndex = (*ivi);
1555 if (segments[segIndex-1]->hasBlock())
1559 //after this, ivi points just behind the last valid unblocked taxi segment.
1560 for (intVecIterator j = i->getIntentions().begin(); j != ivi; j++) {
1563 FGTaxiSegment *seg = segments[pos-1];
1564 FGTaxiNode *node = seg->getEnd();
1565 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1566 if (((*tsi)->getEnd() == node) && ((*tsi) != seg)) {