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/performancedata.hxx>
47 #include <AIModel/AIFlightPlan.hxx>
49 #include <ATC/atc_mgr.hxx>
51 #include <Scenery/scenery.hxx>
53 #include "groundnetwork.hxx"
56 /***************************************************************************
58 **************************************************************************/
60 void FGTaxiSegment::setStart(FGTaxiNodeVector * nodes)
62 FGTaxiNodeVectorIterator i = nodes->begin();
63 while (i != nodes->end()) {
64 //cerr << "Scanning start node index" << (*i)->getIndex() << endl;
65 if ((*i)->getIndex() == startNode) {
66 start = (*i)->getAddress();
67 (*i)->addSegment(this);
72 SG_LOG(SG_GENERAL, SG_ALERT,
73 "Could not find start node " << startNode << endl);
76 void FGTaxiSegment::setEnd(FGTaxiNodeVector * nodes)
78 FGTaxiNodeVectorIterator i = nodes->begin();
79 while (i != nodes->end()) {
80 //cerr << "Scanning end node index" << (*i)->getIndex() << endl;
81 if ((*i)->getIndex() == endNode) {
82 end = (*i)->getAddress();
87 SG_LOG(SG_GENERAL, SG_ALERT,
88 "Could not find end node " << endNode << endl);
93 // There is probably a computationally cheaper way of
95 void FGTaxiSegment::setDimensions(double elevation)
97 length = SGGeodesy::distanceM(start->getGeod(), end->getGeod());
98 //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
100 double az2; //, distanceM;
101 SGGeodesy::inverse(start->getGeod(), end->getGeod(), heading, az2, length);
102 double coveredDistance = length * 0.5;
103 SGGeodesy::direct(start->getGeod(), heading, coveredDistance, center, az2);
104 //start->setElevation(elevation);
105 //end->setElevation(elevation);
106 //cerr << "Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
110 //void FGTaxiSegment::setCourseDiff(double crse)
112 // headingDiff = fabs(course - crse);
114 // if (headingDiff > 180)
115 // headingDiff = fabs(headingDiff - 360);
118 void FGTaxiSegment::block(int id, time_t blockTime, time_t now)
120 BlockListIterator i = blockTimes.begin();
121 while (i != blockTimes.end()) {
122 if (i->getId() == id)
126 if (i == blockTimes.end()) {
127 blockTimes.push_back(Block(id, blockTime, now));
128 sort(blockTimes.begin(), blockTimes.end());
130 i->updateTimeStamps(blockTime, now);
134 // The segment has a block if any of the block times listed in the block list is
135 // smaller than the current time.
136 bool FGTaxiSegment::hasBlock(time_t now)
138 for (BlockListIterator i = blockTimes.begin(); i != blockTimes.end(); i++) {
139 if (i->getBlockTime() < now)
145 void FGTaxiSegment::unblock(time_t now)
147 if (blockTimes.size()) {
148 BlockListIterator i = blockTimes.begin();
149 if (i->getTimeStamp() < (now - 30)) {
157 /***************************************************************************
159 **************************************************************************/
160 bool FGTaxiRoute::next(int *nde)
162 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
163 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
164 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
165 //if (currNode != nodes.end())
166 // cerr << "true" << endl;
168 // cerr << "false" << endl;
169 //if (nodes.size() != (routes.size()) +1)
170 // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
172 if (currNode == nodes.end())
175 if (currNode != nodes.begin()) // make sure route corresponds to the end node
181 bool FGTaxiRoute::next(int *nde, int *rte)
183 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
184 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
185 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
186 //if (currNode != nodes.end())
187 // cerr << "true" << endl;
189 // cerr << "false" << endl;
190 if (nodes.size() != (routes.size()) + 1) {
191 SG_LOG(SG_GENERAL, SG_ALERT,
192 "ALERT: Misconfigured TaxiRoute : " << nodes.
193 size() << " " << routes.size());
196 if (currNode == nodes.end())
199 //*rte = *(currRoute);
200 if (currNode != nodes.begin()) // Make sure route corresponds to the end node
205 // If currNode points to the first node, this means the aircraft is not on the taxi node
206 // yet. Make sure to return a unique identifyer in this situation though, because otherwise
207 // the speed adjust AI code may be unable to resolve whether two aircraft are on the same
208 // taxi route or not. the negative of the preceding route seems a logical choice, as it is
209 // unique for any starting location.
210 // Note that this is probably just a temporary fix until I get Parking / tower control working.
211 *rte = -1 * *(currRoute);
218 void FGTaxiRoute::rewind(int route)
224 if (!(next(&currPoint, &currRoute))) {
225 SG_LOG(SG_GENERAL, SG_ALERT,
226 "Error in rewinding TaxiRoute: current" << currRoute <<
229 } while (currRoute != route);
235 /***************************************************************************
237 **************************************************************************/
238 bool compare_nodes(FGTaxiNode * a, FGTaxiNode * b)
243 bool compare_segments(FGTaxiSegment * a, FGTaxiSegment * b)
248 bool compare_trafficrecords(FGTrafficRecord a, FGTrafficRecord b)
250 return (a.getIntentions().size() < b.getIntentions().size());
253 FGGroundNetwork::FGGroundNetwork()
261 currTraffic = activeTraffic.begin();
264 networkInitialized = false;
268 FGGroundNetwork::~FGGroundNetwork()
270 //cerr << "Running Groundnetwork Destructor " << endl;
271 bool saveData = false;
273 if (fgGetBool("/sim/ai/groundnet-cache")) {
274 SGPath cacheData(fgGetString("/sim/fg-home"));
275 cacheData.append("ai");
276 string airport = parent->getId();
278 if ((airport) != "") {
280 ::snprintf(buffer, 128, "%c/%c/%c/",
281 airport[0], airport[1], airport[2]);
282 cacheData.append(buffer);
283 if (!cacheData.exists()) {
284 cacheData.create_dir(0777);
286 cacheData.append(airport + "-groundnet-cache.txt");
287 cachefile.open(cacheData.str().c_str());
291 cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl;
292 for (FGTaxiNodeVectorIterator node = nodes.begin();
293 node != nodes.end(); node++) {
295 cachefile << (*node)->getIndex () << " "
296 << (*node)->getElevationM (parent->getElevation()*SG_FEET_TO_METER) << " "
302 pushBackNodes.clear();
303 for (FGTaxiSegmentVectorIterator seg = segments.begin();
304 seg != segments.end(); seg++) {
313 void FGGroundNetwork::saveElevationCache() {
314 //cerr << "Running Groundnetwork Destructor " << endl;
315 bool saveData = false;
317 if (fgGetBool("/sim/ai/groundnet-cache")) {
318 SGPath cacheData(fgGetString("/sim/fg-home"));
319 cacheData.append("ai");
320 string airport = parent->getId();
322 if ((airport) != "") {
324 ::snprintf(buffer, 128, "%c/%c/%c/",
325 airport[0], airport[1], airport[2]);
326 cacheData.append(buffer);
327 if (!cacheData.exists()) {
328 cacheData.create_dir(0777);
330 cacheData.append(airport + "-groundnet-cache.txt");
331 cachefile.open(cacheData.str().c_str());
335 cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl;
336 for (FGTaxiNodeVectorIterator node = nodes.begin();
337 node != nodes.end(); node++) {
339 cachefile << (*node)->getIndex () << " "
340 << (*node)->getElevationM (parent->getElevation()*SG_FEET_TO_METER) << " "
349 void FGGroundNetwork::addSegment(const FGTaxiSegment & seg)
351 segments.push_back(new FGTaxiSegment(seg));
354 void FGGroundNetwork::addNode(const FGTaxiNode & node)
356 nodes.push_back(new FGTaxiNode(node));
359 void FGGroundNetwork::addNodes(FGParkingVec * parkings)
362 FGParkingVecIterator i = parkings->begin();
363 while (i != parkings->end()) {
364 n.setIndex(i->getIndex());
365 n.setLatitude(i->getLatitude());
366 n.setLongitude(i->getLongitude());
367 n.setElevation(parent->getElevation()*SG_FEET_TO_METER);
368 nodes.push_back(new FGTaxiNode(n));
376 void FGGroundNetwork::init()
378 if (networkInitialized) {
379 FGATCController::init();
380 //cerr << "FGground network already initialized" << endl;
386 sort(nodes.begin(), nodes.end(), compare_nodes);
387 //sort(segments.begin(), segments.end(), compare_segments());
388 FGTaxiSegmentVectorIterator i = segments.begin();
389 while (i != segments.end()) {
390 (*i)->setStart(&nodes);
391 (*i)->setEnd(&nodes);
392 (*i)->setDimensions(parent->getElevation() * SG_FEET_TO_METER);
393 (*i)->setIndex(index);
394 if ((*i)->isPushBack()) {
395 pushBackNodes.push_back((*i)->getEnd());
397 //SG_LOG(SG_GENERAL, SG_BULK, "initializing segment " << (*i)->getIndex() << endl);
398 //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = " << (*i)->getLength() << endl);
399 //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from " << (*i)->getStart()->getIndex() << " to "
400 // << (*i)->getEnd()->getIndex() << endl);
405 i = segments.begin();
406 while (i != segments.end()) {
407 FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute();
408 while (j != (*i)->getEnd()->getEndRoute()) {
409 if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex()) {
410 // int start1 = (*i)->getStart()->getIndex();
411 // int end1 = (*i)->getEnd() ->getIndex();
412 // int start2 = (*j)->getStart()->getIndex();
413 // int end2 = (*j)->getEnd()->getIndex();
414 // int oppIndex = (*j)->getIndex();
415 //cerr << "Opposite of " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
416 // << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl;
417 (*i)->setOpposite(*j);
424 //FGTaxiNodeVectorIterator j = nodes.begin();
425 //while (j != nodes.end()) {
426 // if ((*j)->getHoldPointType() == 3) {
427 // pushBackNodes.push_back((*j));
431 //cerr << "Done initializing ground network" << endl;
433 if (fgGetBool("/sim/ai/groundnet-cache")) {
434 SGPath cacheData(fgGetString("/sim/fg-home"));
435 cacheData.append("ai");
436 string airport = parent->getId();
438 if ((airport) != "") {
440 ::snprintf(buffer, 128, "%c/%c/%c/",
441 airport[0], airport[1], airport[2]);
442 cacheData.append(buffer);
443 if (!cacheData.exists()) {
444 cacheData.create_dir(0777);
448 cacheData.append(airport + "-groundnet-cache.txt");
449 if (cacheData.exists()) {
450 ifstream data(cacheData.c_str());
453 if (revisionStr != "[GroundNetcachedata:ref:2011:09:04]") {
454 SG_LOG(SG_GENERAL, SG_ALERT,"GroundNetwork Warning: discarding outdated cachefile " <<
455 cacheData.c_str() << " for Airport " << airport);
457 for (FGTaxiNodeVectorIterator i = nodes.begin();
460 (*i)->setElevation(parent->getElevation() * SG_FEET_TO_METER);
461 data >> index >> elev;
464 if (index != (*i)->getIndex()) {
465 SG_LOG(SG_GENERAL, SG_ALERT, "Index read from ground network cache at airport " << airport << " does not match index in the network itself");
467 (*i)->setElevation(elev);
474 //cerr << "Finished initializing " << parent->getId() << " groundnetwork " << endl;
475 networkInitialized = true;
478 int FGGroundNetwork::findNearestNode(const SGGeod & aGeod)
480 double minDist = HUGE_VAL;
483 for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end();
485 double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod());
488 index = (*itr)->getIndex();
489 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
496 int FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod)
498 double minDist = HUGE_VAL;
501 for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end();
503 if (!((*itr)->getIsOnRunway())) {
506 double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod());
509 index = (*itr)->getIndex();
510 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
518 int FGGroundNetwork::findNearestNode(double lat, double lon)
520 return findNearestNode(SGGeod::fromDeg(lon, lat));
523 FGTaxiNode *FGGroundNetwork::findNode(unsigned idx)
525 for (FGTaxiNodeVectorIterator
527 itr != nodes.end(); itr++)
529 if (itr->getIndex() == idx)
530 return itr->getAddress();
533 if ((idx >= 0) && (idx < nodes.size()))
534 return nodes[idx]->getAddress();
539 FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx)
541 for (FGTaxiSegmentVectorIterator
542 itr = segments.begin();
543 itr != segments.end(); itr++)
545 if (itr->getIndex() == idx)
546 return itr->getAddress();
549 if ((idx > 0) && (idx <= segments.size()))
550 return segments[idx - 1]->getAddress();
552 //cerr << "Alert: trying to find invalid segment " << idx << endl;
558 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end,
561 //implements Dijkstra's algorithm to find shortest distance route from start to end
562 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
564 //double INFINITE = 100000000000.0;
565 // initialize scoring values
566 int nParkings = parent->getDynamics()->getNrOfParkings();
567 FGTaxiNodeVector *currNodesSet;
569 currNodesSet = &nodes;
571 currNodesSet = &pushBackNodes;
574 for (FGTaxiNodeVectorIterator
575 itr = currNodesSet->begin(); itr != currNodesSet->end(); itr++) {
576 (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
577 (*itr)->setPreviousNode(0); //
578 (*itr)->setPreviousSeg(0); //
581 FGTaxiNode *firstNode = findNode(start);
582 firstNode->setPathScore(0);
584 FGTaxiNode *lastNode = findNode(end);
586 FGTaxiNodeVector unvisited(*currNodesSet); // working copy
588 while (!unvisited.empty()) {
589 FGTaxiNode *best = *(unvisited.begin());
590 for (FGTaxiNodeVectorIterator
591 itr = unvisited.begin(); itr != unvisited.end(); itr++) {
592 if ((*itr)->getPathScore() < best->getPathScore())
596 FGTaxiNodeVectorIterator newend =
597 remove(unvisited.begin(), unvisited.end(), best);
598 unvisited.erase(newend, unvisited.end());
600 if (best == lastNode) { // found route or best not connected
603 for (FGTaxiSegmentVectorIterator
604 seg = best->getBeginRoute();
605 seg != best->getEndRoute(); seg++) {
606 if (fullSearch || (*seg)->isPushBack()) {
607 FGTaxiNode *tgt = (*seg)->getEnd();
609 best->getPathScore() + (*seg)->getLength() +
610 (*seg)->getPenalty(nParkings);
611 if (alt < tgt->getPathScore()) { // Relax (u,v)
612 tgt->setPathScore(alt);
613 tgt->setPreviousNode(best);
614 tgt->setPreviousSeg(*seg); //
617 // // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
623 if (lastNode->getPathScore() == HUGE_VAL) {
624 // no valid route found
626 SG_LOG(SG_GENERAL, SG_ALERT,
627 "Failed to find route from waypoint " << start << " to "
628 << end << " at " << parent->getId());
632 //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
634 // assemble route from backtrace information
635 intVec nodes, routes;
636 FGTaxiNode *bt = lastNode;
637 while (bt->getPreviousNode() != 0) {
638 nodes.push_back(bt->getIndex());
639 routes.push_back(bt->getPreviousSegment()->getIndex());
640 bt = bt->getPreviousNode();
642 nodes.push_back(start);
643 reverse(nodes.begin(), nodes.end());
644 reverse(routes.begin(), routes.end());
646 return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
650 int FGTaxiSegment::getPenalty(int nGates)
653 if (end->getIndex() < nGates) {
656 if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
662 /* ATC Related Functions */
664 void FGGroundNetwork::announcePosition(int id,
665 FGAIFlightPlan * intendedRoute,
666 int currentPosition, double lat,
667 double lon, double heading,
668 double speed, double alt,
669 double radius, int leg,
670 FGAIAircraft * aircraft)
673 TrafficVectorIterator i = activeTraffic.begin();
674 // Search search if the current id alread has an entry
675 // This might be faster using a map instead of a vector, but let's start by taking a safe route
676 if (activeTraffic.size()) {
677 //while ((i->getId() != id) && i != activeTraffic.end()) {
678 while (i != activeTraffic.end()) {
679 if (i->getId() == id) {
685 // Add a new TrafficRecord if no one exsists for this aircraft.
686 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
690 rec.setPositionAndIntentions(currentPosition, intendedRoute);
691 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
692 rec.setRadius(radius); // only need to do this when creating the record.
693 rec.setAircraft(aircraft);
695 activeTraffic.push_front(rec);
697 activeTraffic.push_back(rec);
701 i->setPositionAndIntentions(currentPosition, intendedRoute);
702 i->setPositionAndHeading(lat, lon, heading, speed, alt);
707 void FGGroundNetwork::signOff(int id)
709 TrafficVectorIterator i = activeTraffic.begin();
710 // Search search if the current id alread has an entry
711 // This might be faster using a map instead of a vector, but let's start by taking a safe route
712 if (activeTraffic.size()) {
713 //while ((i->getId() != id) && i != activeTraffic.end()) {
714 while (i != activeTraffic.end()) {
715 if (i->getId() == id) {
721 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
722 SG_LOG(SG_GENERAL, SG_ALERT,
723 "AI error: Aircraft without traffic record is signing off at " << SG_ORIGIN);
725 i = activeTraffic.erase(i);
729 * The ground network can deal with the following states:
730 * 0 = Normal; no action required
731 * 1 = "Acknowledge "Hold position
732 * 2 = "Acknowledge "Resume taxi".
733 * 3 = "Issue TaxiClearance"
734 * 4 = Acknowledge Taxi Clearance"
735 * 5 = Post acknowlegde taxiclearance: Start taxiing
737 * 7 = Acknowledge report runway
738 * 8 = Switch tower frequency
739 * 9 = Acknowledge switch tower frequency
740 *************************************************************************************************************************/
741 bool FGGroundNetwork::checkTransmissionState(int minState, int maxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId,
744 int state = i->getState();
745 if ((state >= minState) && (state <= maxState) && available) {
746 if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
747 //cerr << "Checking state " << state << " for " << i->getAircraft()->getCallSign() << endl;
748 static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
749 int n = trans_num->getIntValue();
751 trans_num->setIntValue(-1);
753 //cerr << "Selected transmission message " << n << endl;
754 //FGATCManager *atc = (FGATCManager*) globals->get_subsystem("atc");
755 FGATCDialogNew::instance()->removeEntry(1);
757 //cerr << "creating message for " << i->getAircraft()->getCallSign() << endl;
758 transmit(&(*i), msgId, msgDir, false);
762 transmit(&(*i), msgId, msgDir, true);
764 lastTransmission = now;
771 void FGGroundNetwork::updateAircraftInformation(int id, double lat, double lon,
772 double heading, double speed, double alt,
775 time_t currentTime = time(NULL);
776 if (nextSave < currentTime) {
777 saveElevationCache();
778 nextSave = currentTime + 100 + rand() % 200;
780 // Check whether aircraft are on hold due to a preceding pushback. If so, make sure to
781 // Transmit air-to-ground "Ready to taxi request:
782 // Transmit ground to air approval / hold
783 // Transmit confirmation ...
784 // Probably use a status mechanism similar to the Engine start procedure in the startup controller.
787 TrafficVectorIterator i = activeTraffic.begin();
788 // Search search if the current id has an entry
789 // This might be faster using a map instead of a vector, but let's start by taking a safe route
790 TrafficVectorIterator current, closest;
791 if (activeTraffic.size()) {
792 //while ((i->getId() != id) && i != activeTraffic.end()) {
793 while (i != activeTraffic.end()) {
794 if (i->getId() == id) {
800 // update position of the current aircraft
801 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
802 SG_LOG(SG_GENERAL, SG_ALERT,
803 "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
805 i->setPositionAndHeading(lat, lon, heading, speed, alt);
811 // Update every three secs, but add some randomness
812 // to prevent all IA objects doing this in synchrony
813 //if (getDt() < (3.0) + (rand() % 10))
817 current->clearResolveCircularWait();
818 current->setWaitsForId(0);
819 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
820 bool needsTaxiClearance = current->getAircraft()->getTaxiClearanceRequest();
821 if (!needsTaxiClearance) {
822 checkHoldPosition(id, lat, lon, heading, speed, alt);
823 //if (checkForCircularWaits(id)) {
824 // i->setResolveCircularWait();
827 current->setHoldPosition(true);
828 int state = current->getState();
829 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
830 if ((now - lastTransmission) > 15) {
833 if (checkTransmissionState(0,2, current, now, MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) {
834 current->setState(3);
836 if (checkTransmissionState(3,3, current, now, MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR)) {
837 current->setState(4);
839 if (checkTransmissionState(4,4, current, now, MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) {
840 current->setState(5);
842 if ((state == 5) && available) {
843 current->setState(0);
844 current->getAircraft()->setTaxiClearanceRequest(false);
845 current->setHoldPosition(false);
853 Scan for a speed adjustment change. Find the nearest aircraft that is in front
854 and adjust speed when we get too close. Only do this when current position and/or
855 intentions of the current aircraft match current taxiroute position of the proximate
856 aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
857 instruction. See below for the hold position instruction.
859 Note that there currently still is one flaw in the logic that needs to be addressed.
860 There can be situations where one aircraft is in front of the current aircraft, on a separate
861 route, but really close after an intersection coming off the current route. This
862 aircraft is still close enough to block the current aircraft. This situation is currently
863 not addressed yet, but should be.
866 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
867 double lon, double heading,
868 double speed, double alt)
871 TrafficVectorIterator current, closest, closestOnNetwork;
872 TrafficVectorIterator i = activeTraffic.begin();
873 bool otherReasonToSlowDown = false;
874 bool previousInstruction;
875 if (activeTraffic.size()) {
876 //while ((i->getId() != id) && (i != activeTraffic.end()))
877 while (i != activeTraffic.end()) {
878 if (i->getId() == id) {
886 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
887 SG_LOG(SG_GENERAL, SG_ALERT,
888 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment at " << SG_ORIGIN);
893 previousInstruction = current->getSpeedAdjustment();
894 double mindist = HUGE_VAL;
895 if (activeTraffic.size()) {
896 double course, dist, bearing, minbearing, az2;
897 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
898 //TrafficVector iterator closest;
900 closestOnNetwork = current;
901 for (TrafficVectorIterator i = activeTraffic.begin();
902 i != activeTraffic.end(); i++) {
907 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
910 SGGeodesy::inverse(curr, other, course, az2, dist);
911 bearing = fabs(heading - course);
913 bearing = 360 - bearing;
914 if ((dist < mindist) && (bearing < 60.0)) {
917 closestOnNetwork = i;
918 minbearing = bearing;
922 //Check traffic at the tower controller
923 if (towerController->hasActiveTraffic()) {
924 for (TrafficVectorIterator i =
925 towerController->getActiveTraffic().begin();
926 i != towerController->getActiveTraffic().end(); i++) {
927 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
928 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
931 SGGeodesy::inverse(curr, other, course, az2, dist);
932 bearing = fabs(heading - course);
934 bearing = 360 - bearing;
935 if ((dist < mindist) && (bearing < 60.0)) {
936 //cerr << "Current aircraft " << current->getAircraft()->getTrafficRef()->getCallSign()
937 // << " is closest to " << i->getAircraft()->getTrafficRef()->getCallSign()
938 // << ", which has status " << i->getAircraft()->isScheduledForTakeoff()
942 minbearing = bearing;
943 otherReasonToSlowDown = true;
947 // Finally, check UserPosition
948 // Note, as of 2011-08-01, this should no longer be necessecary.
950 double userLatitude = fgGetDouble("/position/latitude-deg");
951 double userLongitude = fgGetDouble("/position/longitude-deg");
952 SGGeod user(SGGeod::fromDeg(userLongitude, userLatitude));
953 SGGeodesy::inverse(curr, user, course, az2, dist);
955 bearing = fabs(heading - course);
957 bearing = 360 - bearing;
958 if ((dist < mindist) && (bearing < 60.0)) {
961 minbearing = bearing;
962 otherReasonToSlowDown = true;
965 current->clearSpeedAdjustment();
966 bool needBraking = false;
967 if (current->checkPositionAndIntentions(*closest)
968 || otherReasonToSlowDown) {
969 double maxAllowableDistance =
970 (1.1 * current->getRadius()) +
971 (1.1 * closest->getRadius());
972 if (mindist < 2 * maxAllowableDistance) {
973 if (current->getId() == closest->getWaitsForId())
976 current->setWaitsForId(closest->getId());
977 if (closest->getId() != current->getId()) {
978 current->setSpeedAdjustment(closest->getSpeed() *
983 // closest->getAircraft()->getTakeOffStatus() &&
984 // (current->getAircraft()->getTrafficRef()->getDepartureAirport() == closest->getAircraft()->getTrafficRef()->getDepartureAirport()) &&
985 // (current->getAircraft()->GetFlightPlan()->getRunway() == closest->getAircraft()->GetFlightPlan()->getRunway())
987 // current->getAircraft()->scheduleForATCTowerDepartureControl(1);
989 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
991 if (mindist < maxAllowableDistance) {
992 //double newSpeed = (maxAllowableDistance-mindist);
993 //current->setSpeedAdjustment(newSpeed);
994 //if (mindist < 0.5* maxAllowableDistance)
996 current->setSpeedAdjustment(0);
1001 if ((closest->getId() == closestOnNetwork->getId()) && (current->getPriority() < closest->getPriority()) && needBraking) {
1002 swap(current, closest);
1008 Check for "Hold position instruction".
1009 The hold position should be issued under the following conditions:
1010 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
1011 2) For taxiing aircraft that use one taxiway in opposite directions
1012 3) For crossing or merging taxiroutes.
1015 void FGGroundNetwork::checkHoldPosition(int id, double lat,
1016 double lon, double heading,
1017 double speed, double alt)
1019 TrafficVectorIterator current;
1020 TrafficVectorIterator i = activeTraffic.begin();
1021 if (activeTraffic.size()) {
1022 //while ((i->getId() != id) && i != activeTraffic.end())
1023 while (i != activeTraffic.end()) {
1024 if (i->getId() == id) {
1032 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1033 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1034 SG_LOG(SG_GENERAL, SG_ALERT,
1035 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition at " << SG_ORIGIN);
1039 if (current->getAircraft()->getTakeOffStatus() == 1) {
1040 current->setHoldPosition(true);
1043 if (current->getAircraft()->getTakeOffStatus() == 2) {
1044 //cerr << current->getAircraft()->getCallSign() << ". Taxi in position and hold" << endl;
1045 current->setHoldPosition(false);
1046 current->clearSpeedAdjustment();
1049 bool origStatus = current->hasHoldPosition();
1050 current->setHoldPosition(false);
1051 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
1052 int currentRoute = i->getCurrentPosition();
1054 if (i->getIntentions().size()) {
1055 nextRoute = (*(i->getIntentions().begin()));
1059 if (currentRoute > 0) {
1060 FGTaxiSegment *tx = findSegment(currentRoute);
1063 nx = findSegment(nextRoute);
1067 //if (tx->hasBlock(now) || nx->hasBlock(now) ) {
1068 // current->setHoldPosition(true);
1070 SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1071 SGGeod end (SGGeod::fromDeg(nx->getStart()->getLongitude(), nx->getStart()->getLatitude()));
1073 double distance = SGGeodesy::distanceM(start, end);
1074 if (nx->hasBlock(now) && (distance < i->getRadius() * 4)) {
1075 current->setHoldPosition(true);
1077 intVecIterator ivi = i->getIntentions().begin();
1078 while (ivi != i->getIntentions().end()) {
1080 distance += segments[(*ivi)-1]->getLength();
1081 if ((segments[(*ivi)-1]->hasBlock(now)) && (distance < i->getRadius() * 4)) {
1082 current->setHoldPosition(true);
1090 bool currStatus = current->hasHoldPosition();
1091 current->setHoldPosition(origStatus);
1092 // Either a Hold Position or a resume taxi transmission has been issued
1093 if ((now - lastTransmission) > 2) {
1096 if (current->getState() == 0) {
1097 if ((origStatus != currStatus) && available) {
1098 //cerr << "Issueing hold short instrudtion " << currStatus << " " << available << endl;
1099 if (currStatus == true) { // No has a hold short instruction
1100 transmit(&(*current), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR, true);
1101 //cerr << "Transmittin hold short instrudtion " << currStatus << " " << available << endl;
1102 current->setState(1);
1104 transmit(&(*current), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR, true);
1105 //cerr << "Transmittig resume instrudtion " << currStatus << " " << available << endl;
1106 current->setState(2);
1108 lastTransmission = now;
1110 // Don't act on the changed instruction until the transmission is confirmed
1111 // So set back to original status
1112 //cerr << "Current state " << current->getState() << endl;
1116 // 6 = Report runway
1117 // 7 = Acknowledge report runway
1118 // 8 = Switch tower frequency
1119 //9 = Acknowledge switch tower frequency
1121 //int state = current->getState();
1122 if (checkTransmissionState(1,1, current, now, MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND)) {
1123 current->setState(0);
1124 current->setHoldPosition(true);
1126 if (checkTransmissionState(2,2, current, now, MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND)) {
1127 current->setState(0);
1128 current->setHoldPosition(false);
1130 if (current->getAircraft()->getTakeOffStatus() && (current->getState() == 0)) {
1131 //cerr << "Scheduling " << current->getAircraft()->getCallSign() << " for hold short" << endl;
1132 current->setState(6);
1134 if (checkTransmissionState(6,6, current, now, MSG_REPORT_RUNWAY_HOLD_SHORT, ATC_AIR_TO_GROUND)) {
1136 if (checkTransmissionState(7,7, current, now, MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT, ATC_GROUND_TO_AIR)) {
1138 if (checkTransmissionState(8,8, current, now, MSG_SWITCH_TOWER_FREQUENCY, ATC_GROUND_TO_AIR)) {
1140 if (checkTransmissionState(9,9, current, now, MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY, ATC_AIR_TO_GROUND)) {
1145 //current->setState(0);
1149 * Check whether situations occur where the current aircraft is waiting for itself
1150 * due to higher order interactions.
1151 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
1152 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
1153 * through this list of waiting aircraft, we can check if we'd eventually end back
1154 * at the current aircraft.
1156 * Note that we should consider the situation where we are actually checking aircraft
1157 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
1158 * the looping aircraft. If we don't check for that, this function will get stuck into
1162 bool FGGroundNetwork::checkForCircularWaits(int id)
1164 //cerr << "Performing Wait check " << id << endl;
1166 TrafficVectorIterator current, other;
1167 TrafficVectorIterator i = activeTraffic.begin();
1168 int trafficSize = activeTraffic.size();
1170 while (i != activeTraffic.end()) {
1171 if (i->getId() == id) {
1179 if (i == activeTraffic.end() || (trafficSize == 0)) {
1180 SG_LOG(SG_GENERAL, SG_ALERT,
1181 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits at " << SG_ORIGIN);
1185 target = current->getWaitsForId();
1186 //bool printed = false; // Note that this variable is for debugging purposes only.
1190 //cerr << "aircraft waits for user" << endl;
1195 while ((target > 0) && (target != id) && counter++ < trafficSize) {
1197 TrafficVectorIterator i = activeTraffic.begin();
1199 //while ((i->getId() != id) && i != activeTraffic.end())
1200 while (i != activeTraffic.end()) {
1201 if (i->getId() == target) {
1209 if (i == activeTraffic.end() || (trafficSize == 0)) {
1210 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
1211 // The target id is not found on the current network, which means it's at the tower
1212 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
1216 target = other->getWaitsForId();
1218 // actually this trap isn't as impossible as it first seemed:
1219 // the setWaitsForID(id) is set to current when the aircraft
1220 // is waiting for the user controlled aircraft.
1221 //if (current->getId() == other->getId()) {
1222 // cerr << "Caught the impossible trap" << endl;
1223 // cerr << "Current = " << current->getId() << endl;
1224 // cerr << "Other = " << other ->getId() << endl;
1225 // for (TrafficVectorIterator at = activeTraffic.begin();
1226 // at != activeTraffic.end();
1228 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
1231 if (current->getId() == other->getId())
1234 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
1235 // << " (" << other->getId() << "); " << endl;;
1245 // cerr << "[done] " << endl << endl;;
1247 SG_LOG(SG_GENERAL, SG_WARN,
1248 "Detected circular wait condition: Id = " << id <<
1249 "target = " << target);
1256 // Note that this function is probably obsolete...
1257 bool FGGroundNetwork::hasInstruction(int id)
1259 TrafficVectorIterator i = activeTraffic.begin();
1260 // Search search if the current id has an entry
1261 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1262 if (activeTraffic.size()) {
1263 //while ((i->getId() != id) && i != activeTraffic.end()) {
1264 while (i != activeTraffic.end()) {
1265 if (i->getId() == id) {
1271 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1272 SG_LOG(SG_GENERAL, SG_ALERT,
1273 "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1275 return i->hasInstruction();
1280 FGATCInstruction FGGroundNetwork::getInstruction(int id)
1282 TrafficVectorIterator i = activeTraffic.begin();
1283 // Search search if the current id has an entry
1284 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1285 if (activeTraffic.size()) {
1286 //while ((i->getId() != id) && i != activeTraffic.end()) {
1287 while (i != activeTraffic.end()) {
1288 if (i->getId() == id) {
1294 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1295 SG_LOG(SG_GENERAL, SG_ALERT,
1296 "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1298 return i->getInstruction();
1300 return FGATCInstruction();
1303 // Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
1304 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
1305 double lon, double elev, double hdg, double slope)
1307 SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
1308 obj_pos = geod.makeZUpFrame();
1309 // hdg is not a compass heading, but a counter-clockwise rotation
1310 // around the Z axis
1311 obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
1313 obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
1320 void FGGroundNetwork::render(bool visible)
1323 SGMaterialLib *matlib = globals->get_matlib();
1326 globals->get_scenery()->get_scene_graph()->removeChild(group);
1327 //while (group->getNumChildren()) {
1328 // cerr << "Number of children: " << group->getNumChildren() << endl;
1329 //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
1330 //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
1331 //geode->releaseGLObjects();
1332 //group->removeChild(geode);
1337 group = new osg::Group;
1338 FGScenery * local_scenery = globals->get_scenery();
1339 double elevation_meters = 0.0;
1340 double elevation_feet = 0.0;
1341 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1342 //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
1344 for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1345 // Handle start point
1346 int pos = i->getCurrentPosition() - 1;
1349 SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1350 SGGeod end (SGGeod::fromDeg(segments[pos]->getEnd()->getLongitude(), segments[pos]->getEnd()->getLatitude()));
1352 double length = SGGeodesy::distanceM(start, end);
1353 //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
1355 double az2, heading; //, distanceM;
1356 SGGeodesy::inverse(start, end, heading, az2, length);
1357 double coveredDistance = length * 0.5;
1359 SGGeodesy::direct(start, heading, coveredDistance, center, az2);
1360 //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
1361 ///////////////////////////////////////////////////////////////////////////////
1362 // Make a helper function out of this
1363 osg::Matrix obj_pos;
1364 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1365 obj_trans->setDataVariance(osg::Object::STATIC);
1366 // Experimental: Calculate slope here, based on length, and the individual elevations
1367 double elevationStart;
1368 if (isUserAircraft((i)->getAircraft())) {
1369 elevationStart = fgGetDouble("/position/ground-elev-m");
1371 elevationStart = ((i)->getAircraft()->_getAltitude());
1373 double elevationEnd = segments[pos]->getEnd()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
1374 //cerr << "Using elevation " << elevationEnd << endl;
1376 if ((elevationEnd == 0) || (elevationEnd = parent->getElevation())) {
1377 SGGeod center2 = end;
1378 center2.setElevationM(SG_MAX_ELEVATION_M);
1379 if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1380 elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1381 //elevation_meters += 0.5;
1384 elevationEnd = parent->getElevation();
1386 segments[pos]->getEnd()->setElevation(elevationEnd);
1388 double elevationMean = (elevationStart + elevationEnd) / 2.0;
1389 double elevDiff = elevationEnd - elevationStart;
1391 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1393 //cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
1395 WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean+ 0.5, -(heading), slope );
1397 obj_trans->setMatrix( obj_pos );
1398 //osg::Vec3 center(0, 0, 0)
1400 float width = length /2.0;
1401 osg::Vec3 corner(-width, 0, 0.25f);
1402 osg::Vec3 widthVec(2*width + 1, 0, 0);
1403 osg::Vec3 heightVec(0, 1, 0);
1404 osg::Geometry* geometry;
1405 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1406 simgear::EffectGeode* geode = new simgear::EffectGeode;
1407 geode->setName("test");
1408 geode->addDrawable(geometry);
1409 //osg::Node *custom_obj;
1411 if (segments[pos]->hasBlock(now)) {
1412 mat = matlib->find("UnidirectionalTaperRed");
1414 mat = matlib->find("UnidirectionalTaperGreen");
1417 geode->setEffect(mat->get_effect());
1418 obj_trans->addChild(geode);
1419 // wire as much of the scene graph together as we can
1420 //->addChild( obj_trans );
1421 group->addChild( obj_trans );
1422 /////////////////////////////////////////////////////////////////////
1424 //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
1426 for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
1427 osg::Matrix obj_pos;
1430 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1431 obj_trans->setDataVariance(osg::Object::STATIC);
1433 // Experimental: Calculate slope here, based on length, and the individual elevations
1434 double elevationStart = segments[k]->getStart()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
1435 double elevationEnd = segments[k]->getEnd ()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
1436 if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
1437 SGGeod center2 = segments[k]->getStart()->getGeod();
1438 center2.setElevationM(SG_MAX_ELEVATION_M);
1439 if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
1440 elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
1441 //elevation_meters += 0.5;
1444 elevationStart = parent->getElevation();
1446 segments[k]->getStart()->setElevation(elevationStart);
1448 if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1449 SGGeod center2 = segments[k]->getEnd()->getGeod();
1450 center2.setElevationM(SG_MAX_ELEVATION_M);
1451 if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1452 elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1453 //elevation_meters += 0.5;
1456 elevationEnd = parent->getElevation();
1458 segments[k]->getEnd()->setElevation(elevationEnd);
1461 double elevationMean = (elevationStart + elevationEnd) / 2.0;
1462 double elevDiff = elevationEnd - elevationStart;
1463 double length = segments[k]->getLength();
1464 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1466 // cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
1469 WorldCoordinate( obj_pos, segments[k]->getLatitude(), segments[k]->getLongitude(), elevationMean+ 0.5, -(segments[k]->getHeading()), slope );
1471 obj_trans->setMatrix( obj_pos );
1472 //osg::Vec3 center(0, 0, 0)
1474 float width = segments[k]->getLength() /2.0;
1475 osg::Vec3 corner(-width, 0, 0.25f);
1476 osg::Vec3 widthVec(2*width + 1, 0, 0);
1477 osg::Vec3 heightVec(0, 1, 0);
1478 osg::Geometry* geometry;
1479 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1480 simgear::EffectGeode* geode = new simgear::EffectGeode;
1481 geode->setName("test");
1482 geode->addDrawable(geometry);
1483 //osg::Node *custom_obj;
1485 if (segments[k]->hasBlock(now)) {
1486 mat = matlib->find("UnidirectionalTaperRed");
1488 mat = matlib->find("UnidirectionalTaperGreen");
1491 geode->setEffect(mat->get_effect());
1492 obj_trans->addChild(geode);
1493 // wire as much of the scene graph together as we can
1494 //->addChild( obj_trans );
1495 group->addChild( obj_trans );
1500 globals->get_scenery()->get_scene_graph()->addChild(group);
1504 string FGGroundNetwork::getName() {
1505 return string(parent->getId() + "-ground");
1508 void FGGroundNetwork::update(double dt)
1510 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1511 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1512 (*tsi)->unblock(now);
1515 //sort(activeTraffic.begin(), activeTraffic.end(), compare_trafficrecords);
1516 // Handle traffic that is under ground control first; this way we'll prevent clutter at the gate areas.
1517 // Don't allow an aircraft to pushback when a taxiing aircraft is currently using part of the intended route.
1518 for (TrafficVectorIterator i = parent->getDynamics()->getStartupController()->getActiveTraffic().begin();
1519 i != parent->getDynamics()->getStartupController()->getActiveTraffic().end(); i++) {
1521 i->setPriority(priority++);
1522 // in meters per second;
1523 double vTaxi = (i->getAircraft()->getPerformance()->vTaxi() * SG_NM_TO_METER) / 3600;
1524 if (i->isActive(60)) {
1526 // Check for all active aircraft whether it's current pos segment is
1527 // an opposite of one of the departing aircraft's intentions
1528 for (TrafficVectorIterator j = activeTraffic.begin(); j != activeTraffic.end(); j++) {
1529 int pos = j->getCurrentPosition();
1531 FGTaxiSegment *seg = segments[pos-1]->opposite();
1533 int posReverse = seg->getIndex();
1534 for (intVecIterator k = i->getIntentions().begin(); k != i->getIntentions().end(); k++) {
1535 if ((*k) == posReverse) {
1537 segments[posReverse-1]->block(i->getId(), now, now);
1543 // if the current aircraft is still allowed to pushback, we can start reserving a route for if by blocking all the entry taxiways.
1544 if (i->pushBackAllowed()) {
1546 int pos = i->getCurrentPosition();
1548 FGTaxiSegment *seg = segments[pos-1];
1549 FGTaxiNode *node = seg->getEnd();
1550 length = seg->getLength();
1551 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1552 if (((*tsi)->getEnd() == node) && ((*tsi) != seg)) {
1553 (*tsi)->block(i->getId(), now, now);
1557 for (intVecIterator j = i->getIntentions().begin(); j != i->getIntentions().end(); j++) {
1560 FGTaxiSegment *seg = segments[pos-1];
1561 FGTaxiNode *node = seg->getEnd();
1562 length += seg->getLength();
1563 time_t blockTime = now + (length / vTaxi);
1564 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1565 if (((*tsi)->getEnd() == node) && ((*tsi) != seg)) {
1566 (*tsi)->block(i->getId(), blockTime-30, now);
1574 for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1576 double vTaxi = (i->getAircraft()->getPerformance()->vTaxi() * SG_NM_TO_METER) / 3600;
1577 i->setPriority(priority++);
1578 int pos = i->getCurrentPosition();
1580 length = segments[pos-1]->getLength();
1581 if (segments[pos-1]->hasBlock(now)) {
1582 //SG_LOG(SG_GENERAL, SG_ALERT, "Taxiway incursion for AI aircraft" << i->getAircraft()->getCallSign());
1587 for (ivi = i->getIntentions().begin(); ivi != i->getIntentions().end(); ivi++) {
1588 int segIndex = (*ivi);
1590 if (segments[segIndex-1]->hasBlock(now))
1594 //after this, ivi points just behind the last valid unblocked taxi segment.
1595 for (intVecIterator j = i->getIntentions().begin(); j != ivi; j++) {
1598 FGTaxiSegment *seg = segments[pos-1];
1599 FGTaxiNode *node = seg->getEnd();
1600 length += seg->getLength();
1601 for (FGTaxiSegmentVectorIterator tsi = segments.begin(); tsi != segments.end(); tsi++) {
1602 if (((*tsi)->getEnd() == node) && ((*tsi) != seg)) {
1603 time_t blockTime = now + (length / vTaxi);
1604 (*tsi)->block(i->getId(), blockTime - 30, now);