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();
224 networkInitialized = false;
228 FGGroundNetwork::~FGGroundNetwork()
230 //cerr << "Running Groundnetwork Destructor " << endl;
231 bool saveData = false;
233 if (fgGetBool("/sim/ai/groundnet-cache")) {
234 SGPath cacheData(fgGetString("/sim/fg-home"));
235 cacheData.append("ai");
236 string airport = parent->getId();
238 if ((airport) != "") {
240 ::snprintf(buffer, 128, "%c/%c/%c/",
241 airport[0], airport[1], airport[2]);
242 cacheData.append(buffer);
243 if (!cacheData.exists()) {
244 cacheData.create_dir(0777);
246 cacheData.append(airport + "-groundnet-cache.txt");
247 cachefile.open(cacheData.str().c_str());
251 cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl;
252 for (FGTaxiNodeVectorIterator node = nodes.begin();
253 node != nodes.end(); node++) {
255 cachefile << (*node)->getIndex () << " "
256 << (*node)->getElevationM (parent->getElevation()*SG_FEET_TO_METER) << " "
262 pushBackNodes.clear();
263 for (FGTaxiSegmentVectorIterator seg = segments.begin();
264 seg != segments.end(); seg++) {
273 void FGGroundNetwork::saveElevationCache() {
274 //cerr << "Running Groundnetwork Destructor " << endl;
275 bool saveData = false;
277 if (fgGetBool("/sim/ai/groundnet-cache")) {
278 SGPath cacheData(fgGetString("/sim/fg-home"));
279 cacheData.append("ai");
280 string airport = parent->getId();
282 if ((airport) != "") {
284 ::snprintf(buffer, 128, "%c/%c/%c/",
285 airport[0], airport[1], airport[2]);
286 cacheData.append(buffer);
287 if (!cacheData.exists()) {
288 cacheData.create_dir(0777);
290 cacheData.append(airport + "-groundnet-cache.txt");
291 cachefile.open(cacheData.str().c_str());
295 cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl;
296 for (FGTaxiNodeVectorIterator node = nodes.begin();
297 node != nodes.end(); node++) {
299 cachefile << (*node)->getIndex () << " "
300 << (*node)->getElevationM (parent->getElevation()*SG_FEET_TO_METER) << " "
309 void FGGroundNetwork::addSegment(const FGTaxiSegment & seg)
311 segments.push_back(new FGTaxiSegment(seg));
314 void FGGroundNetwork::addNode(const FGTaxiNode & node)
316 nodes.push_back(new FGTaxiNode(node));
319 void FGGroundNetwork::addNodes(FGParkingVec * parkings)
322 FGParkingVecIterator i = parkings->begin();
323 while (i != parkings->end()) {
324 n.setIndex(i->getIndex());
325 n.setLatitude(i->getLatitude());
326 n.setLongitude(i->getLongitude());
327 n.setElevation(parent->getElevation()*SG_FEET_TO_METER);
328 nodes.push_back(new FGTaxiNode(n));
336 void FGGroundNetwork::init()
338 if (networkInitialized) {
339 FGATCController::init();
340 //cerr << "FGground network already initialized" << endl;
346 sort(nodes.begin(), nodes.end(), compare_nodes);
347 //sort(segments.begin(), segments.end(), compare_segments());
348 FGTaxiSegmentVectorIterator i = segments.begin();
349 while (i != segments.end()) {
350 (*i)->setStart(&nodes);
351 (*i)->setEnd(&nodes);
352 (*i)->setDimensions(parent->getElevation() * SG_FEET_TO_METER);
353 (*i)->setIndex(index);
354 if ((*i)->isPushBack()) {
355 pushBackNodes.push_back((*i)->getEnd());
357 //SG_LOG(SG_GENERAL, SG_BULK, "initializing segment " << (*i)->getIndex() << endl);
358 //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = " << (*i)->getLength() << endl);
359 //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from " << (*i)->getStart()->getIndex() << " to "
360 // << (*i)->getEnd()->getIndex() << endl);
365 i = segments.begin();
366 while (i != segments.end()) {
367 FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute();
368 while (j != (*i)->getEnd()->getEndRoute()) {
369 if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex()) {
370 // int start1 = (*i)->getStart()->getIndex();
371 // int end1 = (*i)->getEnd() ->getIndex();
372 // int start2 = (*j)->getStart()->getIndex();
373 // int end2 = (*j)->getEnd()->getIndex();
374 // int oppIndex = (*j)->getIndex();
375 //cerr << "Opposite of " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
376 // << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl;
377 (*i)->setOpposite(*j);
384 //FGTaxiNodeVectorIterator j = nodes.begin();
385 //while (j != nodes.end()) {
386 // if ((*j)->getHoldPointType() == 3) {
387 // pushBackNodes.push_back((*j));
391 //cerr << "Done initializing ground network" << endl;
393 if (fgGetBool("/sim/ai/groundnet-cache")) {
394 SGPath cacheData(fgGetString("/sim/fg-home"));
395 cacheData.append("ai");
396 string airport = parent->getId();
398 if ((airport) != "") {
400 ::snprintf(buffer, 128, "%c/%c/%c/",
401 airport[0], airport[1], airport[2]);
402 cacheData.append(buffer);
403 if (!cacheData.exists()) {
404 cacheData.create_dir(0777);
408 cacheData.append(airport + "-groundnet-cache.txt");
409 if (cacheData.exists()) {
410 ifstream data(cacheData.c_str());
413 if (revisionStr != "[GroundNetcachedata:ref:2011:09:04]") {
414 SG_LOG(SG_GENERAL, SG_ALERT,"GroundNetwork Warning: discarding outdated cachefile " <<
415 cacheData.c_str() << " for Airport " << airport);
417 for (FGTaxiNodeVectorIterator i = nodes.begin();
420 (*i)->setElevation(parent->getElevation() * SG_FEET_TO_METER);
421 data >> index >> elev;
424 if (index != (*i)->getIndex()) {
425 SG_LOG(SG_GENERAL, SG_ALERT, "Index read from ground network cache at airport " << airport << " does not match index in the network itself");
427 (*i)->setElevation(elev);
434 //cerr << "Finished initializing " << parent->getId() << " groundnetwork " << endl;
435 networkInitialized = true;
438 int FGGroundNetwork::findNearestNode(const SGGeod & aGeod)
440 double minDist = HUGE_VAL;
443 for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end();
445 double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod());
448 index = (*itr)->getIndex();
449 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
456 int FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod)
458 double minDist = HUGE_VAL;
461 for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end();
463 if (!((*itr)->getIsOnRunway())) {
466 double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod());
469 index = (*itr)->getIndex();
470 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
478 int FGGroundNetwork::findNearestNode(double lat, double lon)
480 return findNearestNode(SGGeod::fromDeg(lon, lat));
483 FGTaxiNode *FGGroundNetwork::findNode(unsigned idx)
485 for (FGTaxiNodeVectorIterator
487 itr != nodes.end(); itr++)
489 if (itr->getIndex() == idx)
490 return itr->getAddress();
493 if ((idx >= 0) && (idx < nodes.size()))
494 return nodes[idx]->getAddress();
499 FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx)
501 for (FGTaxiSegmentVectorIterator
502 itr = segments.begin();
503 itr != segments.end(); itr++)
505 if (itr->getIndex() == idx)
506 return itr->getAddress();
509 if ((idx > 0) && (idx <= segments.size()))
510 return segments[idx - 1]->getAddress();
512 //cerr << "Alert: trying to find invalid segment " << idx << endl;
518 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end,
521 //implements Dijkstra's algorithm to find shortest distance route from start to end
522 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
524 //double INFINITE = 100000000000.0;
525 // initialize scoring values
526 int nParkings = parent->getDynamics()->getNrOfParkings();
527 FGTaxiNodeVector *currNodesSet;
529 currNodesSet = &nodes;
531 currNodesSet = &pushBackNodes;
534 for (FGTaxiNodeVectorIterator
535 itr = currNodesSet->begin(); itr != currNodesSet->end(); itr++) {
536 (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
537 (*itr)->setPreviousNode(0); //
538 (*itr)->setPreviousSeg(0); //
541 FGTaxiNode *firstNode = findNode(start);
542 firstNode->setPathScore(0);
544 FGTaxiNode *lastNode = findNode(end);
546 FGTaxiNodeVector unvisited(*currNodesSet); // working copy
548 while (!unvisited.empty()) {
549 FGTaxiNode *best = *(unvisited.begin());
550 for (FGTaxiNodeVectorIterator
551 itr = unvisited.begin(); itr != unvisited.end(); itr++) {
552 if ((*itr)->getPathScore() < best->getPathScore())
556 FGTaxiNodeVectorIterator newend =
557 remove(unvisited.begin(), unvisited.end(), best);
558 unvisited.erase(newend, unvisited.end());
560 if (best == lastNode) { // found route or best not connected
563 for (FGTaxiSegmentVectorIterator
564 seg = best->getBeginRoute();
565 seg != best->getEndRoute(); seg++) {
566 if (fullSearch || (*seg)->isPushBack()) {
567 FGTaxiNode *tgt = (*seg)->getEnd();
569 best->getPathScore() + (*seg)->getLength() +
570 (*seg)->getPenalty(nParkings);
571 if (alt < tgt->getPathScore()) { // Relax (u,v)
572 tgt->setPathScore(alt);
573 tgt->setPreviousNode(best);
574 tgt->setPreviousSeg(*seg); //
577 // // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
583 if (lastNode->getPathScore() == HUGE_VAL) {
584 // no valid route found
586 SG_LOG(SG_GENERAL, SG_ALERT,
587 "Failed to find route from waypoint " << start << " to "
588 << end << " at " << parent->getId());
592 //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
594 // assemble route from backtrace information
595 intVec nodes, routes;
596 FGTaxiNode *bt = lastNode;
597 while (bt->getPreviousNode() != 0) {
598 nodes.push_back(bt->getIndex());
599 routes.push_back(bt->getPreviousSegment()->getIndex());
600 bt = bt->getPreviousNode();
602 nodes.push_back(start);
603 reverse(nodes.begin(), nodes.end());
604 reverse(routes.begin(), routes.end());
606 return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
610 int FGTaxiSegment::getPenalty(int nGates)
613 if (end->getIndex() < nGates) {
616 if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
622 /* ATC Related Functions */
624 void FGGroundNetwork::announcePosition(int id,
625 FGAIFlightPlan * intendedRoute,
626 int currentPosition, double lat,
627 double lon, double heading,
628 double speed, double alt,
629 double radius, int leg,
630 FGAIAircraft * aircraft)
633 TrafficVectorIterator i = activeTraffic.begin();
634 // Search search if the current id alread has an entry
635 // This might be faster using a map instead of a vector, but let's start by taking a safe route
636 if (activeTraffic.size()) {
637 //while ((i->getId() != id) && i != activeTraffic.end()) {
638 while (i != activeTraffic.end()) {
639 if (i->getId() == id) {
645 // Add a new TrafficRecord if no one exsists for this aircraft.
646 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
650 rec.setPositionAndIntentions(currentPosition, intendedRoute);
651 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
652 rec.setRadius(radius); // only need to do this when creating the record.
653 rec.setAircraft(aircraft);
654 activeTraffic.push_front(rec);
657 i->setPositionAndIntentions(currentPosition, intendedRoute);
658 i->setPositionAndHeading(lat, lon, heading, speed, alt);
663 void FGGroundNetwork::signOff(int id)
665 TrafficVectorIterator i = activeTraffic.begin();
666 // Search search if the current id alread has an entry
667 // This might be faster using a map instead of a vector, but let's start by taking a safe route
668 if (activeTraffic.size()) {
669 //while ((i->getId() != id) && i != activeTraffic.end()) {
670 while (i != activeTraffic.end()) {
671 if (i->getId() == id) {
677 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
678 SG_LOG(SG_GENERAL, SG_ALERT,
679 "AI error: Aircraft without traffic record is signing off at " << SG_ORIGIN);
681 i = activeTraffic.erase(i);
685 * The ground network can deal with the following states:
686 * 0 = Normal; no action required
687 * 1 = "Acknowledge "Hold position
688 * 2 = "Acknowledge "Resume taxi".
689 * 3 = "Issue TaxiClearance"
690 * 4 = Acknowledge Taxi Clearance"
691 * 5 = Post acknowlegde taxiclearance: Start taxiing
693 * 7 = Acknowledge report runway
694 * 8 = Switch tower frequency
695 * 9 = Acknowledge switch tower frequency
696 *************************************************************************************************************************/
697 bool FGGroundNetwork::checkTransmissionState(int minState, int maxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId,
700 int state = i->getState();
701 if ((state >= minState) && (state <= maxState) && available) {
702 if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
703 //cerr << "Checking state " << state << " for " << i->getAircraft()->getCallSign() << endl;
704 static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
705 int n = trans_num->getIntValue();
707 trans_num->setIntValue(-1);
709 //cerr << "Selected transmission message " << n << endl;
710 FGATCManager *atc = (FGATCManager*) globals->get_subsystem("atc");
711 FGATCDialogNew::instance()->removeEntry(1);
713 //cerr << "creating message for " << i->getAircraft()->getCallSign() << endl;
714 transmit(&(*i), msgId, msgDir, false);
718 transmit(&(*i), msgId, msgDir, true);
720 lastTransmission = now;
727 void FGGroundNetwork::updateAircraftInformation(int id, double lat, double lon,
728 double heading, double speed, double alt,
731 time_t currentTime = time(NULL);
732 if (nextSave < currentTime) {
733 saveElevationCache();
734 nextSave = currentTime + 100 + rand() % 200;
736 // Check whether aircraft are on hold due to a preceding pushback. If so, make sure to
737 // Transmit air-to-ground "Ready to taxi request:
738 // Transmit ground to air approval / hold
739 // Transmit confirmation ...
740 // Probably use a status mechanism similar to the Engine start procedure in the startup controller.
743 TrafficVectorIterator i = activeTraffic.begin();
744 // Search search if the current id has an entry
745 // This might be faster using a map instead of a vector, but let's start by taking a safe route
746 TrafficVectorIterator current, closest;
747 if (activeTraffic.size()) {
748 //while ((i->getId() != id) && i != activeTraffic.end()) {
749 while (i != activeTraffic.end()) {
750 if (i->getId() == id) {
756 // update position of the current aircraft
757 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
758 SG_LOG(SG_GENERAL, SG_ALERT,
759 "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
761 i->setPositionAndHeading(lat, lon, heading, speed, alt);
767 // Update every three secs, but add some randomness
768 // to prevent all IA objects doing this in synchrony
769 //if (getDt() < (3.0) + (rand() % 10))
773 current->clearResolveCircularWait();
774 current->setWaitsForId(0);
775 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
776 bool needsTaxiClearance = current->getAircraft()->getTaxiClearanceRequest();
777 if (!needsTaxiClearance) {
778 checkHoldPosition(id, lat, lon, heading, speed, alt);
779 //if (checkForCircularWaits(id)) {
780 // i->setResolveCircularWait();
783 current->setHoldPosition(true);
784 int state = current->getState();
785 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
786 if ((now - lastTransmission) > 15) {
789 if (checkTransmissionState(0,2, current, now, MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) {
790 current->setState(3);
792 if (checkTransmissionState(3,3, current, now, MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR)) {
793 current->setState(4);
795 if (checkTransmissionState(4,4, current, now, MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) {
796 current->setState(5);
798 if ((state == 5) && available) {
799 current->setState(0);
800 current->getAircraft()->setTaxiClearanceRequest(false);
801 current->setHoldPosition(false);
809 Scan for a speed adjustment change. Find the nearest aircraft that is in front
810 and adjust speed when we get too close. Only do this when current position and/or
811 intentions of the current aircraft match current taxiroute position of the proximate
812 aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
813 instruction. See below for the hold position instruction.
815 Note that there currently still is one flaw in the logic that needs to be addressed.
816 There can be situations where one aircraft is in front of the current aircraft, on a separate
817 route, but really close after an intersection coming off the current route. This
818 aircraft is still close enough to block the current aircraft. This situation is currently
819 not addressed yet, but should be.
822 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
823 double lon, double heading,
824 double speed, double alt)
827 TrafficVectorIterator current, closest, closestOnNetwork;
828 TrafficVectorIterator i = activeTraffic.begin();
829 bool otherReasonToSlowDown = false;
830 bool previousInstruction;
831 if (activeTraffic.size()) {
832 //while ((i->getId() != id) && (i != activeTraffic.end()))
833 while (i != activeTraffic.end()) {
834 if (i->getId() == id) {
842 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
843 SG_LOG(SG_GENERAL, SG_ALERT,
844 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment at " << SG_ORIGIN);
849 previousInstruction = current->getSpeedAdjustment();
850 double mindist = HUGE_VAL;
851 if (activeTraffic.size()) {
852 double course, dist, bearing, minbearing, az2;
853 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
854 //TrafficVector iterator closest;
856 for (TrafficVectorIterator i = activeTraffic.begin();
857 i != activeTraffic.end(); i++) {
862 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
865 SGGeodesy::inverse(curr, other, course, az2, dist);
866 bearing = fabs(heading - course);
868 bearing = 360 - bearing;
869 if ((dist < mindist) && (bearing < 60.0)) {
872 closestOnNetwork = i;
873 minbearing = bearing;
877 //Check traffic at the tower controller
878 if (towerController->hasActiveTraffic()) {
879 for (TrafficVectorIterator i =
880 towerController->getActiveTraffic().begin();
881 i != towerController->getActiveTraffic().end(); i++) {
882 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
883 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
886 SGGeodesy::inverse(curr, other, course, az2, dist);
887 bearing = fabs(heading - course);
889 bearing = 360 - bearing;
890 if ((dist < mindist) && (bearing < 60.0)) {
891 //cerr << "Current aircraft " << current->getAircraft()->getTrafficRef()->getCallSign()
892 // << " is closest to " << i->getAircraft()->getTrafficRef()->getCallSign()
893 // << ", which has status " << i->getAircraft()->isScheduledForTakeoff()
897 minbearing = bearing;
898 otherReasonToSlowDown = true;
902 // Finally, check UserPosition
903 // Note, as of 2011-08-01, this should no longer be necessecary.
905 double userLatitude = fgGetDouble("/position/latitude-deg");
906 double userLongitude = fgGetDouble("/position/longitude-deg");
907 SGGeod user(SGGeod::fromDeg(userLongitude, userLatitude));
908 SGGeodesy::inverse(curr, user, course, az2, dist);
910 bearing = fabs(heading - course);
912 bearing = 360 - bearing;
913 if ((dist < mindist) && (bearing < 60.0)) {
916 minbearing = bearing;
917 otherReasonToSlowDown = true;
920 current->clearSpeedAdjustment();
921 bool needBraking = false;
922 if (current->checkPositionAndIntentions(*closest)
923 || otherReasonToSlowDown) {
924 double maxAllowableDistance =
925 (1.1 * current->getRadius()) +
926 (1.1 * closest->getRadius());
927 if (mindist < 2 * maxAllowableDistance) {
928 if (current->getId() == closest->getWaitsForId())
931 current->setWaitsForId(closest->getId());
932 if (closest->getId() != current->getId()) {
933 current->setSpeedAdjustment(closest->getSpeed() *
938 // closest->getAircraft()->getTakeOffStatus() &&
939 // (current->getAircraft()->getTrafficRef()->getDepartureAirport() == closest->getAircraft()->getTrafficRef()->getDepartureAirport()) &&
940 // (current->getAircraft()->GetFlightPlan()->getRunway() == closest->getAircraft()->GetFlightPlan()->getRunway())
942 // current->getAircraft()->scheduleForATCTowerDepartureControl(1);
944 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
946 if (mindist < maxAllowableDistance) {
947 //double newSpeed = (maxAllowableDistance-mindist);
948 //current->setSpeedAdjustment(newSpeed);
949 //if (mindist < 0.5* maxAllowableDistance)
951 current->setSpeedAdjustment(0);
956 if ((closest == closestOnNetwork) && (current->getPriority() < closest->getPriority()) && needBraking) {
957 swap(current, closest);
963 Check for "Hold position instruction".
964 The hold position should be issued under the following conditions:
965 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
966 2) For taxiing aircraft that use one taxiway in opposite directions
967 3) For crossing or merging taxiroutes.
970 void FGGroundNetwork::checkHoldPosition(int id, double lat,
971 double lon, double heading,
972 double speed, double alt)
974 TrafficVectorIterator current;
975 TrafficVectorIterator i = activeTraffic.begin();
976 if (activeTraffic.size()) {
977 //while ((i->getId() != id) && i != activeTraffic.end())
978 while (i != activeTraffic.end()) {
979 if (i->getId() == id) {
987 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
988 SG_LOG(SG_GENERAL, SG_ALERT,
989 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition at " << SG_ORIGIN);
993 if (current->getAircraft()->getTakeOffStatus() == 1) {
994 current->setHoldPosition(true);
997 if (current->getAircraft()->getTakeOffStatus() == 2) {
998 current->setHoldPosition(false);
1001 bool origStatus = current->hasHoldPosition();
1002 current->setHoldPosition(false);
1003 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
1004 int currentRoute = i->getCurrentPosition();
1006 if (i->getIntentions().size()) {
1007 nextRoute = (*(i->getIntentions().begin()));
1011 if (currentRoute > 0) {
1012 FGTaxiSegment *tx = findSegment(currentRoute);
1015 nx = findSegment(nextRoute);
1019 if (tx->hasBlock() || nx->hasBlock() ) {
1020 current->setHoldPosition(true);
1025 /* for (i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1026 if (i->getId() != current->getId()) {
1027 int node = current->crosses(this, *i);
1029 FGTaxiNode *taxiNode = findNode(node);
1031 // Determine whether it's save to continue or not.
1032 // If we have a crossing route, there are two possibilities:
1033 // 1) This is an interestion
1034 // 2) This is oncoming two-way traffic, using the same taxiway.
1035 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
1037 SGGeod other(SGGeod::
1038 fromDegM(i->getLongitude(), i->getLatitude(),
1042 if (current->isOpposing(this, *i, node)) {
1045 //cerr << "Hold check 2 : " << node << " has opposing segment " << endl;
1046 // issue a "Hold Position" as soon as we're close to the offending node
1047 // For now, I'm doing this as long as the other aircraft doesn't
1048 // have a hold instruction as soon as we're within a reasonable
1049 // distance from the offending node.
1050 // This may be a bit of a conservative estimate though, as it may
1051 // be well possible that both aircraft can both continue to taxi
1052 // without crashing into each other.
1055 if (SGGeodesy::distanceM(other, taxiNode->getGeod()) > 200) // 2.0*i->getRadius())
1057 needsToWait = false;
1058 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
1062 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
1067 SGGeodesy::distanceM(curr, taxiNode->getGeod());
1068 if (!(i->hasHoldPosition())) {
1070 if ((dist < 200) && //2.5*current->getRadius()) &&
1071 (needsToWait) && (i->onRoute(this, *current)) &&
1072 //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
1073 (!(current->getId() == i->getWaitsForId())))
1074 //(!(i->getSpeedAdjustment()))) // &&
1075 //(!(current->getSpeedAdjustment())))
1078 if (!(isUserAircraft(i->getAircraft()))) { // test code. Don't wait for the user, let the user wait for you.
1079 current->setHoldPosition(true);
1080 current->setWaitsForId(i->getId());
1082 //cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
1083 // << dist << " meters. Waiting for " << i->getCallSign();
1085 //cerr <<" [opposing] " << endl;
1087 // cerr << "[non-opposing] " << endl;
1088 //if (i->hasSpeefAdjustment())
1090 // cerr << " (which in turn waits for ) " << i->
1092 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
1098 bool currStatus = current->hasHoldPosition();
1099 current->setHoldPosition(origStatus);
1100 // Either a Hold Position or a resume taxi transmission has been issued
1101 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1102 if ((now - lastTransmission) > 2) {
1105 if (current->getState() == 0) {
1106 if ((origStatus != currStatus) && available) {
1107 //cerr << "Issueing hold short instrudtion " << currStatus << " " << available << endl;
1108 if (currStatus == true) { // No has a hold short instruction
1109 transmit(&(*current), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR, true);
1110 //cerr << "Transmittin hold short instrudtion " << currStatus << " " << available << endl;
1111 current->setState(1);
1113 transmit(&(*current), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR, true);
1114 //cerr << "Transmittig resume instrudtion " << currStatus << " " << available << endl;
1115 current->setState(2);
1117 lastTransmission = now;
1119 // Don't act on the changed instruction until the transmission is confirmed
1120 // So set back to original status
1121 //cerr << "Current state " << current->getState() << endl;
1125 // 6 = Report runway
1126 // 7 = Acknowledge report runway
1127 // 8 = Switch tower frequency
1128 //9 = Acknowledge switch tower frequency
1130 //int state = current->getState();
1131 if (checkTransmissionState(1,1, current, now, MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND)) {
1132 current->setState(0);
1133 current->setHoldPosition(true);
1135 if (checkTransmissionState(2,2, current, now, MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND)) {
1136 current->setState(0);
1137 current->setHoldPosition(false);
1139 if (current->getAircraft()->getTakeOffStatus() && (current->getState() == 0)) {
1140 //cerr << "Scheduling " << current->getAircraft()->getCallSign() << " for hold short" << endl;
1141 current->setState(6);
1143 if (checkTransmissionState(6,6, current, now, MSG_REPORT_RUNWAY_HOLD_SHORT, ATC_AIR_TO_GROUND)) {
1145 if (checkTransmissionState(7,7, current, now, MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT, ATC_GROUND_TO_AIR)) {
1147 if (checkTransmissionState(8,8, current, now, MSG_SWITCH_TOWER_FREQUENCY, ATC_GROUND_TO_AIR)) {
1149 if (checkTransmissionState(9,9, current, now, MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY, ATC_AIR_TO_GROUND)) {
1154 //current->setState(0);
1158 * Check whether situations occur where the current aircraft is waiting for itself
1159 * due to higher order interactions.
1160 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
1161 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
1162 * through this list of waiting aircraft, we can check if we'd eventually end back
1163 * at the current aircraft.
1165 * Note that we should consider the situation where we are actually checking aircraft
1166 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
1167 * the looping aircraft. If we don't check for that, this function will get stuck into
1171 bool FGGroundNetwork::checkForCircularWaits(int id)
1173 //cerr << "Performing Wait check " << id << endl;
1175 TrafficVectorIterator current, other;
1176 TrafficVectorIterator i = activeTraffic.begin();
1177 int trafficSize = activeTraffic.size();
1179 while (i != activeTraffic.end()) {
1180 if (i->getId() == id) {
1188 if (i == activeTraffic.end() || (trafficSize == 0)) {
1189 SG_LOG(SG_GENERAL, SG_ALERT,
1190 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits at " << SG_ORIGIN);
1194 target = current->getWaitsForId();
1195 //bool printed = false; // Note that this variable is for debugging purposes only.
1199 //cerr << "aircraft waits for user" << endl;
1204 while ((target > 0) && (target != id) && counter++ < trafficSize) {
1206 TrafficVectorIterator i = activeTraffic.begin();
1208 //while ((i->getId() != id) && i != activeTraffic.end())
1209 while (i != activeTraffic.end()) {
1210 if (i->getId() == target) {
1218 if (i == activeTraffic.end() || (trafficSize == 0)) {
1219 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
1220 // The target id is not found on the current network, which means it's at the tower
1221 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
1225 target = other->getWaitsForId();
1227 // actually this trap isn't as impossible as it first seemed:
1228 // the setWaitsForID(id) is set to current when the aircraft
1229 // is waiting for the user controlled aircraft.
1230 //if (current->getId() == other->getId()) {
1231 // cerr << "Caught the impossible trap" << endl;
1232 // cerr << "Current = " << current->getId() << endl;
1233 // cerr << "Other = " << other ->getId() << endl;
1234 // for (TrafficVectorIterator at = activeTraffic.begin();
1235 // at != activeTraffic.end();
1237 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
1240 if (current->getId() == other->getId())
1243 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
1244 // << " (" << other->getId() << "); " << endl;;
1254 // cerr << "[done] " << endl << endl;;
1256 SG_LOG(SG_GENERAL, SG_WARN,
1257 "Detected circular wait condition: Id = " << id <<
1258 "target = " << target);
1265 // Note that this function is probably obsolete...
1266 bool FGGroundNetwork::hasInstruction(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: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1284 return i->hasInstruction();
1289 FGATCInstruction FGGroundNetwork::getInstruction(int id)
1291 TrafficVectorIterator i = activeTraffic.begin();
1292 // Search search if the current id has an entry
1293 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1294 if (activeTraffic.size()) {
1295 //while ((i->getId() != id) && i != activeTraffic.end()) {
1296 while (i != activeTraffic.end()) {
1297 if (i->getId() == id) {
1303 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1304 SG_LOG(SG_GENERAL, SG_ALERT,
1305 "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1307 return i->getInstruction();
1309 return FGATCInstruction();
1312 // Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
1313 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
1314 double lon, double elev, double hdg, double slope)
1316 SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
1317 obj_pos = geod.makeZUpFrame();
1318 // hdg is not a compass heading, but a counter-clockwise rotation
1319 // around the Z axis
1320 obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
1322 obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
1329 void FGGroundNetwork::render(bool visible)
1332 SGMaterialLib *matlib = globals->get_matlib();
1335 globals->get_scenery()->get_scene_graph()->removeChild(group);
1336 //while (group->getNumChildren()) {
1337 // cerr << "Number of children: " << group->getNumChildren() << endl;
1338 //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
1339 //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
1340 //geode->releaseGLObjects();
1341 //group->removeChild(geode);
1346 group = new osg::Group;
1347 FGScenery * local_scenery = globals->get_scenery();
1348 double elevation_meters = 0.0;
1349 double elevation_feet = 0.0;
1350 //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
1352 for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1353 // Handle start point
1354 int pos = i->getCurrentPosition() - 1;
1357 SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1358 SGGeod end (SGGeod::fromDeg(segments[pos]->getEnd()->getLongitude(), segments[pos]->getEnd()->getLatitude()));
1360 double length = SGGeodesy::distanceM(start, end);
1361 //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
1363 double az2, heading; //, distanceM;
1364 SGGeodesy::inverse(start, end, heading, az2, length);
1365 double coveredDistance = length * 0.5;
1367 SGGeodesy::direct(start, heading, coveredDistance, center, az2);
1368 //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
1369 ///////////////////////////////////////////////////////////////////////////////
1370 // Make a helper function out of this
1371 osg::Matrix obj_pos;
1372 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1373 obj_trans->setDataVariance(osg::Object::STATIC);
1374 // Experimental: Calculate slope here, based on length, and the individual elevations
1375 double elevationStart;
1376 if (isUserAircraft((i)->getAircraft())) {
1377 elevationStart = fgGetDouble("/position/ground-elev-m");
1379 elevationStart = ((i)->getAircraft()->_getAltitude());
1381 double elevationEnd = segments[pos]->getEnd()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
1382 //cerr << "Using elevation " << elevationEnd << endl;
1384 if ((elevationEnd == 0) || (elevationEnd = parent->getElevation())) {
1385 SGGeod center2 = end;
1386 center2.setElevationM(SG_MAX_ELEVATION_M);
1387 if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1388 elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1389 //elevation_meters += 0.5;
1392 elevationEnd = parent->getElevation();
1394 segments[pos]->getEnd()->setElevation(elevationEnd);
1396 double elevationMean = (elevationStart + elevationEnd) / 2.0;
1397 double elevDiff = elevationEnd - elevationStart;
1399 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1401 //cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
1403 WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean+ 0.5, -(heading), slope );
1405 obj_trans->setMatrix( obj_pos );
1406 //osg::Vec3 center(0, 0, 0)
1408 float width = length /2.0;
1409 osg::Vec3 corner(-width, 0, 0.25f);
1410 osg::Vec3 widthVec(2*width + 1, 0, 0);
1411 osg::Vec3 heightVec(0, 1, 0);
1412 osg::Geometry* geometry;
1413 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1414 simgear::EffectGeode* geode = new simgear::EffectGeode;
1415 geode->setName("test");
1416 geode->addDrawable(geometry);
1417 //osg::Node *custom_obj;
1419 if (segments[pos]->hasBlock()) {
1420 mat = matlib->find("UnidirectionalTaperRed");
1422 mat = matlib->find("UnidirectionalTaperGreen");
1425 geode->setEffect(mat->get_effect());
1426 obj_trans->addChild(geode);
1427 // wire as much of the scene graph together as we can
1428 //->addChild( obj_trans );
1429 group->addChild( obj_trans );
1430 /////////////////////////////////////////////////////////////////////
1432 //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
1434 for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
1435 osg::Matrix obj_pos;
1438 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1439 obj_trans->setDataVariance(osg::Object::STATIC);
1441 // Experimental: Calculate slope here, based on length, and the individual elevations
1442 double elevationStart = segments[k]->getStart()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
1443 double elevationEnd = segments[k]->getEnd ()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
1444 if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
1445 SGGeod center2 = segments[k]->getStart()->getGeod();
1446 center2.setElevationM(SG_MAX_ELEVATION_M);
1447 if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
1448 elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
1449 //elevation_meters += 0.5;
1452 elevationStart = parent->getElevation();
1454 segments[k]->getStart()->setElevation(elevationStart);
1456 if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1457 SGGeod center2 = segments[k]->getEnd()->getGeod();
1458 center2.setElevationM(SG_MAX_ELEVATION_M);
1459 if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1460 elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1461 //elevation_meters += 0.5;
1464 elevationEnd = parent->getElevation();
1466 segments[k]->getEnd()->setElevation(elevationEnd);
1469 double elevationMean = (elevationStart + elevationEnd) / 2.0;
1470 double elevDiff = elevationEnd - elevationStart;
1471 double length = segments[k]->getLength();
1472 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1474 // cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
1477 WorldCoordinate( obj_pos, segments[k]->getLatitude(), segments[k]->getLongitude(), elevationMean+ 0.5, -(segments[k]->getHeading()), slope );
1479 obj_trans->setMatrix( obj_pos );
1480 //osg::Vec3 center(0, 0, 0)
1482 float width = segments[k]->getLength() /2.0;
1483 osg::Vec3 corner(-width, 0, 0.25f);
1484 osg::Vec3 widthVec(2*width + 1, 0, 0);
1485 osg::Vec3 heightVec(0, 1, 0);
1486 osg::Geometry* geometry;
1487 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1488 simgear::EffectGeode* geode = new simgear::EffectGeode;
1489 geode->setName("test");
1490 geode->addDrawable(geometry);
1491 //osg::Node *custom_obj;
1493 if (segments[k]->hasBlock()) {
1494 mat = matlib->find("UnidirectionalTaperRed");
1496 mat = matlib->find("UnidirectionalTaperGreen");
1499 geode->setEffect(mat->get_effect());
1500 obj_trans->addChild(geode);
1501 // wire as much of the scene graph together as we can
1502 //->addChild( obj_trans );
1503 group->addChild( obj_trans );
1508 globals->get_scenery()->get_scene_graph()->addChild(group);
1512 string FGGroundNetwork::getName() {
1513 return string(parent->getId() + "-ground");
1516 void FGGroundNetwork::update(double dt)
1518 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1522 //sort(activeTraffic.begin(), activeTraffic.end(), compare_trafficrecords);
1523 // Handle traffic that is under ground control first; this way we'll prevent clutter at the gate areas.
1524 // Don't allow an aircraft to pushback when a taxiing aircraft is currently using part of the intended route.
1525 for (TrafficVectorIterator i = parent->getDynamics()->getStartupController()->getActiveTraffic().begin();
1526 i != parent->getDynamics()->getStartupController()->getActiveTraffic().end(); i++) {
1528 i->setPriority(priority++);
1529 if (i->isActive(60)) {
1531 // Check for all active aircraft whether it's current pos segment is
1532 // an opposite of one of the departing aircraft's intentions
1533 for (TrafficVectorIterator j = activeTraffic.begin(); j != activeTraffic.end(); j++) {
1534 int pos = j->getCurrentPosition();
1536 FGTaxiSegment *seg = segments[pos-1]->opposite();
1538 int posReverse = seg->getIndex();
1539 for (intVecIterator k = i->getIntentions().begin(); k != i->getIntentions().end(); k++) {
1540 if ((*k) == posReverse) {
1542 segments[posReverse-1]->block();
1548 // if the current aircraft is still allowed to pushback, we can start reserving a route for if by blocking all the entry taxiways.
1549 if (i->pushBackAllowed()) {
1550 int pos = i->getCurrentPosition();
1552 FGTaxiSegment *seg = segments[pos-1];
1553 FGTaxiNode *node = seg->getEnd();
1554 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1555 if (((*tsi)->getEnd() == node) && ((*tsi) != seg)) {
1560 for (intVecIterator j = i->getIntentions().begin(); j != i->getIntentions().end(); 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)) {
1575 for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1576 i->setPriority(priority++);
1577 int pos = i->getCurrentPosition();
1579 if (segments[pos-1]->hasBlock()) {
1580 //SG_LOG(SG_GENERAL, SG_ALERT, "Taxiway incursion for AI aircraft" << i->getAircraft()->getCallSign());
1585 for (ivi = i->getIntentions().begin(); ivi != i->getIntentions().end(); ivi++) {
1586 int segIndex = (*ivi);
1588 if (segments[segIndex-1]->hasBlock())
1592 //after this, ivi points just behind the last valid unblocked taxi segment.
1593 for (intVecIterator j = i->getIntentions().begin(); j != ivi; j++) {
1596 FGTaxiSegment *seg = segments[pos-1];
1597 FGTaxiNode *node = seg->getEnd();
1598 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1599 if (((*tsi)->getEnd() == node) && ((*tsi) != seg)) {