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)->getElevationM (parent->getElevation()*SG_FEET_TO_METER) << " "
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)->getElevationM (parent->getElevation()*SG_FEET_TO_METER) << " "
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()*SG_FEET_TO_METER);
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 at " << SG_ORIGIN);
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 at " << SG_ORIGIN);
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 at " << SG_ORIGIN);
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() *
915 // closest->getAircraft()->getTakeOffStatus() &&
916 // (current->getAircraft()->getTrafficRef()->getDepartureAirport() == closest->getAircraft()->getTrafficRef()->getDepartureAirport()) &&
917 // (current->getAircraft()->GetFlightPlan()->getRunway() == closest->getAircraft()->GetFlightPlan()->getRunway())
919 // current->getAircraft()->scheduleForATCTowerDepartureControl(1);
921 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
923 if (mindist < maxAllowableDistance) {
924 //double newSpeed = (maxAllowableDistance-mindist);
925 //current->setSpeedAdjustment(newSpeed);
926 //if (mindist < 0.5* maxAllowableDistance)
928 current->setSpeedAdjustment(0);
933 if ((closest == closestOnNetwork) && (current->getPriority() < closest->getPriority()) && needBraking) {
934 swap(current, closest);
940 Check for "Hold position instruction".
941 The hold position should be issued under the following conditions:
942 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
943 2) For taxiing aircraft that use one taxiway in opposite directions
944 3) For crossing or merging taxiroutes.
947 void FGGroundNetwork::checkHoldPosition(int id, double lat,
948 double lon, double heading,
949 double speed, double alt)
951 TrafficVectorIterator current;
952 TrafficVectorIterator i = activeTraffic.begin();
953 if (activeTraffic.size()) {
954 //while ((i->getId() != id) && i != activeTraffic.end())
955 while (i != activeTraffic.end()) {
956 if (i->getId() == id) {
964 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
965 SG_LOG(SG_GENERAL, SG_ALERT,
966 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition at " << SG_ORIGIN);
970 if (current->getAircraft()->getTakeOffStatus() == 1) {
971 current->setHoldPosition(true);
974 if (current->getAircraft()->getTakeOffStatus() == 2) {
975 current->setHoldPosition(false);
978 bool origStatus = current->hasHoldPosition();
979 current->setHoldPosition(false);
980 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
981 int currentRoute = i->getCurrentPosition();
983 if (i->getIntentions().size()) {
984 nextRoute = (*(i->getIntentions().begin()));
988 if (currentRoute > 0) {
989 FGTaxiSegment *tx = findSegment(currentRoute);
992 nx = findSegment(nextRoute);
996 if (tx->hasBlock() || nx->hasBlock() ) {
997 current->setHoldPosition(true);
1002 /* for (i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1003 if (i->getId() != current->getId()) {
1004 int node = current->crosses(this, *i);
1006 FGTaxiNode *taxiNode = findNode(node);
1008 // Determine whether it's save to continue or not.
1009 // If we have a crossing route, there are two possibilities:
1010 // 1) This is an interestion
1011 // 2) This is oncoming two-way traffic, using the same taxiway.
1012 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
1014 SGGeod other(SGGeod::
1015 fromDegM(i->getLongitude(), i->getLatitude(),
1019 if (current->isOpposing(this, *i, node)) {
1022 //cerr << "Hold check 2 : " << node << " has opposing segment " << endl;
1023 // issue a "Hold Position" as soon as we're close to the offending node
1024 // For now, I'm doing this as long as the other aircraft doesn't
1025 // have a hold instruction as soon as we're within a reasonable
1026 // distance from the offending node.
1027 // This may be a bit of a conservative estimate though, as it may
1028 // be well possible that both aircraft can both continue to taxi
1029 // without crashing into each other.
1032 if (SGGeodesy::distanceM(other, taxiNode->getGeod()) > 200) // 2.0*i->getRadius())
1034 needsToWait = false;
1035 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
1039 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
1044 SGGeodesy::distanceM(curr, taxiNode->getGeod());
1045 if (!(i->hasHoldPosition())) {
1047 if ((dist < 200) && //2.5*current->getRadius()) &&
1048 (needsToWait) && (i->onRoute(this, *current)) &&
1049 //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
1050 (!(current->getId() == i->getWaitsForId())))
1051 //(!(i->getSpeedAdjustment()))) // &&
1052 //(!(current->getSpeedAdjustment())))
1055 if (!(isUserAircraft(i->getAircraft()))) { // test code. Don't wait for the user, let the user wait for you.
1056 current->setHoldPosition(true);
1057 current->setWaitsForId(i->getId());
1059 //cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
1060 // << dist << " meters. Waiting for " << i->getCallSign();
1062 //cerr <<" [opposing] " << endl;
1064 // cerr << "[non-opposing] " << endl;
1065 //if (i->hasSpeefAdjustment())
1067 // cerr << " (which in turn waits for ) " << i->
1069 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
1075 bool currStatus = current->hasHoldPosition();
1076 current->setHoldPosition(origStatus);
1077 // Either a Hold Position or a resume taxi transmission has been issued
1078 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1079 if ((now - lastTransmission) > 2) {
1082 if (current->getState() == 0) {
1083 if ((origStatus != currStatus) && available) {
1084 //cerr << "Issueing hold short instrudtion " << currStatus << " " << available << endl;
1085 if (currStatus == true) { // No has a hold short instruction
1086 transmit(&(*current), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR, true);
1087 //cerr << "Transmittin hold short instrudtion " << currStatus << " " << available << endl;
1088 current->setState(1);
1090 transmit(&(*current), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR, true);
1091 //cerr << "Transmittig resume instrudtion " << currStatus << " " << available << endl;
1092 current->setState(2);
1094 lastTransmission = now;
1096 // Don't act on the changed instruction until the transmission is confirmed
1097 // So set back to original status
1098 //cerr << "Current state " << current->getState() << endl;
1102 // 6 = Report runway
1103 // 7 = Acknowledge report runway
1104 // 8 = Switch tower frequency
1105 //9 = Acknowledge switch tower frequency
1107 //int state = current->getState();
1108 if (checkTransmissionState(1,1, current, now, MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND)) {
1109 current->setState(0);
1110 current->setHoldPosition(true);
1112 if (checkTransmissionState(2,2, current, now, MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND)) {
1113 current->setState(0);
1114 current->setHoldPosition(false);
1116 if (current->getAircraft()->getTakeOffStatus() && (current->getState() == 0)) {
1117 //cerr << "Scheduling " << current->getAircraft()->getCallSign() << " for hold short" << endl;
1118 current->setState(6);
1120 if (checkTransmissionState(6,6, current, now, MSG_REPORT_RUNWAY_HOLD_SHORT, ATC_AIR_TO_GROUND)) {
1122 if (checkTransmissionState(7,7, current, now, MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT, ATC_GROUND_TO_AIR)) {
1124 if (checkTransmissionState(8,8, current, now, MSG_SWITCH_TOWER_FREQUENCY, ATC_GROUND_TO_AIR)) {
1126 if (checkTransmissionState(9,9, current, now, MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY, ATC_AIR_TO_GROUND)) {
1131 //current->setState(0);
1135 * Check whether situations occur where the current aircraft is waiting for itself
1136 * due to higher order interactions.
1137 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
1138 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
1139 * through this list of waiting aircraft, we can check if we'd eventually end back
1140 * at the current aircraft.
1142 * Note that we should consider the situation where we are actually checking aircraft
1143 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
1144 * the looping aircraft. If we don't check for that, this function will get stuck into
1148 bool FGGroundNetwork::checkForCircularWaits(int id)
1150 //cerr << "Performing Wait check " << id << endl;
1152 TrafficVectorIterator current, other;
1153 TrafficVectorIterator i = activeTraffic.begin();
1154 int trafficSize = activeTraffic.size();
1156 while (i != activeTraffic.end()) {
1157 if (i->getId() == id) {
1165 if (i == activeTraffic.end() || (trafficSize == 0)) {
1166 SG_LOG(SG_GENERAL, SG_ALERT,
1167 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits at " << SG_ORIGIN);
1171 target = current->getWaitsForId();
1172 //bool printed = false; // Note that this variable is for debugging purposes only.
1176 //cerr << "aircraft waits for user" << endl;
1181 while ((target > 0) && (target != id) && counter++ < trafficSize) {
1183 TrafficVectorIterator i = activeTraffic.begin();
1185 //while ((i->getId() != id) && i != activeTraffic.end())
1186 while (i != activeTraffic.end()) {
1187 if (i->getId() == target) {
1195 if (i == activeTraffic.end() || (trafficSize == 0)) {
1196 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
1197 // The target id is not found on the current network, which means it's at the tower
1198 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
1202 target = other->getWaitsForId();
1204 // actually this trap isn't as impossible as it first seemed:
1205 // the setWaitsForID(id) is set to current when the aircraft
1206 // is waiting for the user controlled aircraft.
1207 //if (current->getId() == other->getId()) {
1208 // cerr << "Caught the impossible trap" << endl;
1209 // cerr << "Current = " << current->getId() << endl;
1210 // cerr << "Other = " << other ->getId() << endl;
1211 // for (TrafficVectorIterator at = activeTraffic.begin();
1212 // at != activeTraffic.end();
1214 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
1217 if (current->getId() == other->getId())
1220 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
1221 // << " (" << other->getId() << "); " << endl;;
1231 // cerr << "[done] " << endl << endl;;
1233 SG_LOG(SG_GENERAL, SG_WARN,
1234 "Detected circular wait condition: Id = " << id <<
1235 "target = " << target);
1242 // Note that this function is probably obsolete...
1243 bool FGGroundNetwork::hasInstruction(int id)
1245 TrafficVectorIterator i = activeTraffic.begin();
1246 // Search search if the current id has an entry
1247 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1248 if (activeTraffic.size()) {
1249 //while ((i->getId() != id) && i != activeTraffic.end()) {
1250 while (i != activeTraffic.end()) {
1251 if (i->getId() == id) {
1257 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1258 SG_LOG(SG_GENERAL, SG_ALERT,
1259 "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1261 return i->hasInstruction();
1266 FGATCInstruction FGGroundNetwork::getInstruction(int id)
1268 TrafficVectorIterator i = activeTraffic.begin();
1269 // Search search if the current id has an entry
1270 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1271 if (activeTraffic.size()) {
1272 //while ((i->getId() != id) && i != activeTraffic.end()) {
1273 while (i != activeTraffic.end()) {
1274 if (i->getId() == id) {
1280 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1281 SG_LOG(SG_GENERAL, SG_ALERT,
1282 "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1284 return i->getInstruction();
1286 return FGATCInstruction();
1289 // Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
1290 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
1291 double lon, double elev, double hdg, double slope)
1293 SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
1294 obj_pos = geod.makeZUpFrame();
1295 // hdg is not a compass heading, but a counter-clockwise rotation
1296 // around the Z axis
1297 obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
1299 obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
1306 void FGGroundNetwork::render(bool visible)
1309 SGMaterialLib *matlib = globals->get_matlib();
1312 globals->get_scenery()->get_scene_graph()->removeChild(group);
1313 //while (group->getNumChildren()) {
1314 // cerr << "Number of children: " << group->getNumChildren() << endl;
1315 //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
1316 //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
1317 //geode->releaseGLObjects();
1318 //group->removeChild(geode);
1323 group = new osg::Group;
1324 FGScenery * local_scenery = globals->get_scenery();
1325 double elevation_meters = 0.0;
1326 double elevation_feet = 0.0;
1327 //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
1329 for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1330 // Handle start point
1331 int pos = i->getCurrentPosition() - 1;
1334 SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1335 SGGeod end (SGGeod::fromDeg(segments[pos]->getEnd()->getLongitude(), segments[pos]->getEnd()->getLatitude()));
1337 double length = SGGeodesy::distanceM(start, end);
1338 //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
1340 double az2, heading; //, distanceM;
1341 SGGeodesy::inverse(start, end, heading, az2, length);
1342 double coveredDistance = length * 0.5;
1344 SGGeodesy::direct(start, heading, coveredDistance, center, az2);
1345 //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
1346 ///////////////////////////////////////////////////////////////////////////////
1347 // Make a helper function out of this
1348 osg::Matrix obj_pos;
1349 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1350 obj_trans->setDataVariance(osg::Object::STATIC);
1351 // Experimental: Calculate slope here, based on length, and the individual elevations
1352 double elevationStart;
1353 if (isUserAircraft((i)->getAircraft())) {
1354 elevationStart = fgGetDouble("/position/ground-elev-m");
1356 elevationStart = ((i)->getAircraft()->_getAltitude());
1358 double elevationEnd = segments[pos]->getEnd()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
1359 //cerr << "Using elevation " << elevationEnd << endl;
1361 if ((elevationEnd == 0) || (elevationEnd = parent->getElevation())) {
1362 SGGeod center2 = end;
1363 center2.setElevationM(SG_MAX_ELEVATION_M);
1364 if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1365 elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1366 //elevation_meters += 0.5;
1369 elevationEnd = parent->getElevation();
1371 segments[pos]->getEnd()->setElevation(elevationEnd);
1373 double elevationMean = (elevationStart + elevationEnd) / 2.0;
1374 double elevDiff = elevationEnd - elevationStart;
1376 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1378 //cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
1380 WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean+ 0.5, -(heading), slope );
1382 obj_trans->setMatrix( obj_pos );
1383 //osg::Vec3 center(0, 0, 0)
1385 float width = length /2.0;
1386 osg::Vec3 corner(-width, 0, 0.25f);
1387 osg::Vec3 widthVec(2*width + 1, 0, 0);
1388 osg::Vec3 heightVec(0, 1, 0);
1389 osg::Geometry* geometry;
1390 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1391 simgear::EffectGeode* geode = new simgear::EffectGeode;
1392 geode->setName("test");
1393 geode->addDrawable(geometry);
1394 //osg::Node *custom_obj;
1396 if (segments[pos]->hasBlock()) {
1397 mat = matlib->find("UnidirectionalTaperRed");
1399 mat = matlib->find("UnidirectionalTaperGreen");
1402 geode->setEffect(mat->get_effect());
1403 obj_trans->addChild(geode);
1404 // wire as much of the scene graph together as we can
1405 //->addChild( obj_trans );
1406 group->addChild( obj_trans );
1407 /////////////////////////////////////////////////////////////////////
1409 //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
1411 for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
1412 osg::Matrix obj_pos;
1415 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1416 obj_trans->setDataVariance(osg::Object::STATIC);
1418 // Experimental: Calculate slope here, based on length, and the individual elevations
1419 double elevationStart = segments[k]->getStart()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
1420 double elevationEnd = segments[k]->getEnd ()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
1421 if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
1422 SGGeod center2 = segments[k]->getStart()->getGeod();
1423 center2.setElevationM(SG_MAX_ELEVATION_M);
1424 if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
1425 elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
1426 //elevation_meters += 0.5;
1429 elevationStart = parent->getElevation();
1431 segments[k]->getStart()->setElevation(elevationStart);
1433 if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1434 SGGeod center2 = segments[k]->getEnd()->getGeod();
1435 center2.setElevationM(SG_MAX_ELEVATION_M);
1436 if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1437 elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1438 //elevation_meters += 0.5;
1441 elevationEnd = parent->getElevation();
1443 segments[k]->getEnd()->setElevation(elevationEnd);
1446 double elevationMean = (elevationStart + elevationEnd) / 2.0;
1447 double elevDiff = elevationEnd - elevationStart;
1448 double length = segments[k]->getLength();
1449 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1451 // cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
1454 WorldCoordinate( obj_pos, segments[k]->getLatitude(), segments[k]->getLongitude(), elevationMean+ 0.5, -(segments[k]->getHeading()), slope );
1456 obj_trans->setMatrix( obj_pos );
1457 //osg::Vec3 center(0, 0, 0)
1459 float width = segments[k]->getLength() /2.0;
1460 osg::Vec3 corner(-width, 0, 0.25f);
1461 osg::Vec3 widthVec(2*width + 1, 0, 0);
1462 osg::Vec3 heightVec(0, 1, 0);
1463 osg::Geometry* geometry;
1464 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1465 simgear::EffectGeode* geode = new simgear::EffectGeode;
1466 geode->setName("test");
1467 geode->addDrawable(geometry);
1468 //osg::Node *custom_obj;
1470 if (segments[k]->hasBlock()) {
1471 mat = matlib->find("UnidirectionalTaperRed");
1473 mat = matlib->find("UnidirectionalTaperGreen");
1476 geode->setEffect(mat->get_effect());
1477 obj_trans->addChild(geode);
1478 // wire as much of the scene graph together as we can
1479 //->addChild( obj_trans );
1480 group->addChild( obj_trans );
1485 globals->get_scenery()->get_scene_graph()->addChild(group);
1489 string FGGroundNetwork::getName() {
1490 return string(parent->getId() + "-ground");
1493 void FGGroundNetwork::update(double dt)
1495 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1499 //sort(activeTraffic.begin(), activeTraffic.end(), compare_trafficrecords);
1500 // Handle traffic that is under ground control first; this way we'll prevent clutter at the gate areas.
1501 // Don't allow an aircraft to pushback when a taxiing aircraft is currently using part of the intended route.
1502 for (TrafficVectorIterator i = parent->getDynamics()->getStartupController()->getActiveTraffic().begin();
1503 i != parent->getDynamics()->getStartupController()->getActiveTraffic().end(); i++) {
1505 i->setPriority(priority++);
1506 if (i->isActive(60)) {
1508 // Check for all active aircraft whether it's current pos segment is
1509 // an opposite of one of the departing aircraft's intentions
1510 for (TrafficVectorIterator j = activeTraffic.begin(); j != activeTraffic.end(); j++) {
1511 int pos = j->getCurrentPosition();
1513 FGTaxiSegment *seg = segments[pos-1]->opposite();
1515 int posReverse = seg->getIndex();
1516 for (intVecIterator k = i->getIntentions().begin(); k != i->getIntentions().end(); k++) {
1517 if ((*k) == posReverse) {
1519 segments[posReverse-1]->block();
1525 // if the current aircraft is still allowed to pushback, we can start reserving a route for if by blocking all the entry taxiways.
1526 if (i->pushBackAllowed()) {
1527 int pos = i->getCurrentPosition();
1529 FGTaxiSegment *seg = segments[pos-1];
1530 FGTaxiNode *node = seg->getEnd();
1531 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1532 if (((*tsi)->getEnd() == node) && ((*tsi) != seg)) {
1537 for (intVecIterator j = i->getIntentions().begin(); j != i->getIntentions().end(); j++) {
1540 FGTaxiSegment *seg = segments[pos-1];
1541 FGTaxiNode *node = seg->getEnd();
1542 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1543 if (((*tsi)->getEnd() == node) && ((*tsi) != seg)) {
1552 for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1553 i->setPriority(priority++);
1554 int pos = i->getCurrentPosition();
1556 if (segments[pos-1]->hasBlock()) {
1557 //SG_LOG(SG_GENERAL, SG_ALERT, "Taxiway incursion for AI aircraft" << i->getAircraft()->getCallSign());
1562 for (ivi = i->getIntentions().begin(); ivi != i->getIntentions().end(); ivi++) {
1563 int segIndex = (*ivi);
1565 if (segments[segIndex-1]->hasBlock())
1569 //after this, ivi points just behind the last valid unblocked taxi segment.
1570 for (intVecIterator j = i->getIntentions().begin(); j != ivi; j++) {
1573 FGTaxiSegment *seg = segments[pos-1];
1574 FGTaxiNode *node = seg->getEnd();
1575 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1576 if (((*tsi)->getEnd() == node) && ((*tsi) != seg)) {