1 // groundnet.cxx - Implimentation of the FlightGear airport ground handling code
3 // Written by Durk Talsma, started June 2005.
5 // Copyright (C) 2004 Durk Talsma.
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 //#include <plib/sg.h>
31 //#include <plib/ul.h>
33 //#include <Environment/environment_mgr.hxx>
34 //#include <Environment/environment.hxx>
35 //#include <simgear/misc/sg_path.hxx>
36 //#include <simgear/props/props.hxx>
37 //#include <simgear/structure/subsystem_mgr.hxx>
38 #include <simgear/debug/logstream.hxx>
39 #include <simgear/route/waypoint.hxx>
40 //#include <Main/globals.hxx>
41 //#include <Main/fg_props.hxx>
42 //#include <Airports/runways.hxx>
44 #include <AIModel/AIFlightPlan.hxx>
48 #include "groundnetwork.hxx"
52 /**************************************************************************
54 *************************************************************************/
55 FGTaxiNode::FGTaxiNode()
59 /***************************************************************************
61 **************************************************************************/
62 FGTaxiSegment::FGTaxiSegment()
66 void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
68 FGTaxiNodeVectorIterator i = nodes->begin();
69 while (i != nodes->end())
71 if (i->getIndex() == startNode)
73 start = i->getAddress();
81 void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
83 FGTaxiNodeVectorIterator i = nodes->begin();
84 while (i != nodes->end())
86 if (i->getIndex() == endNode)
88 end = i->getAddress();
95 // There is probably a computationally cheaper way of
97 void FGTaxiSegment::setTrackDistance()
100 SGWayPoint first (start->getLongitude(),
101 start->getLatitude(),
103 SGWayPoint second (end->getLongitude(),
106 first.CourseAndDistance(second, &course, &length);
108 /***************************************************************************
110 **************************************************************************/
111 bool FGTaxiRoute::next(int *nde)
113 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
114 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
115 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
116 //if (currNode != nodes.end())
117 // cerr << "true" << endl;
119 // cerr << "false" << endl;
120 //if (nodes.size() != (routes.size()) +1)
121 // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
123 if (currNode == nodes.end())
126 if (currNode != nodes.begin()) // make sure route corresponds to the end node
132 bool FGTaxiRoute::next(int *nde, int *rte)
134 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
135 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
136 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
137 //if (currNode != nodes.end())
138 // cerr << "true" << endl;
140 // cerr << "false" << endl;
141 if (nodes.size() != (routes.size()) +1) {
142 SG_LOG(SG_GENERAL, SG_ALERT, "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size());
145 if (currNode == nodes.end())
148 //*rte = *(currRoute);
149 if (currNode != nodes.begin()) // Make sure route corresponds to the end node
156 // If currNode points to the first node, this means the aircraft is not on the taxi node
157 // yet. Make sure to return a unique identifyer in this situation though, because otherwise
158 // the speed adjust AI code may be unable to resolve whether two aircraft are on the same
159 // taxi route or not. the negative of the preceding route seems a logical choice, as it is
160 // unique for any starting location.
161 // Note that this is probably just a temporary fix until I get Parking / tower control working.
162 *rte = -1 * *(currRoute);
168 void FGTaxiRoute::rewind(int route)
174 if (!(next(&currPoint, &currRoute))) {
175 SG_LOG(SG_GENERAL,SG_ALERT, "Error in rewinding TaxiRoute: current" << currRoute
176 << " goal " << route);
178 } while (currRoute != route);
181 /***************************************************************************
183 **************************************************************************/
184 void FGTrafficRecord::setPositionAndIntentions(int pos, FGAIFlightPlan *route)
188 if (intentions.size()) {
189 if (*intentions.begin() != pos) {
190 SG_LOG(SG_GENERAL, SG_ALERT, "Error in FGTrafficRecord::setPositionAndIntentions");
191 cerr << "Pos : " << pos << " Curr " << *(intentions.begin()) << endl;
192 for (intVecIterator i = intentions.begin(); i != intentions.end() ; i++) {
197 intentions.erase(intentions.begin());
199 //int legNr, routeNr;
200 //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
201 int size = route->getNrOfWayPoints();
202 cerr << "Setting pos" << pos << " ";
203 cerr << "setting intentions ";
204 for (int i = 0; i < size; i++) {
205 int val = route->getRouteIndex(i);
207 if ((val) && (val != pos))
209 intentions.push_back(val);
214 //while (route->next(&legNr, &routeNr)) {
215 //intentions.push_back(routeNr);
217 //route->rewind(currentPos);
222 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord &other)
225 //cerr << "Start check 1" << endl;
226 if (currentPos == other.currentPos)
228 //cerr << "Check Position and intentions: current matches" << endl;
231 // else if (other.intentions.size())
233 // cerr << "Start check 2" << endl;
234 // intVecIterator i = other.intentions.begin();
235 // while (!((i == other.intentions.end()) || ((*i) == currentPos)))
237 // if (i != other.intentions.end()) {
238 // cerr << "Check Position and intentions: current matches other.intentions" << endl;
241 else if (intentions.size()) {
242 //cerr << "Start check 3" << endl;
243 intVecIterator i = intentions.begin();
244 while (!((i == intentions.end()) || ((*i) == other.currentPos)))
246 if (i != intentions.end()) {
247 //cerr << "Check Position and intentions: .other.current matches" << endl;
251 //cerr << "Done !!" << endl;
255 void FGTrafficRecord::setPositionAndHeading(double lat, double lon, double hdg,
256 double spd, double alt)
265 /***************************************************************************
267 **************************************************************************/
269 FGGroundNetwork::FGGroundNetwork()
275 currTraffic = activeTraffic.begin();
278 void FGGroundNetwork::addSegment(const FGTaxiSegment &seg)
280 segments.push_back(seg);
283 void FGGroundNetwork::addNode(const FGTaxiNode &node)
285 nodes.push_back(node);
288 void FGGroundNetwork::addNodes(FGParkingVec *parkings)
291 FGParkingVecIterator i = parkings->begin();
292 while (i != parkings->end())
294 n.setIndex(i->getIndex());
295 n.setLatitude(i->getLatitude());
296 n.setLongitude(i->getLongitude());
305 void FGGroundNetwork::init()
309 FGTaxiSegmentVectorIterator i = segments.begin();
310 while(i != segments.end()) {
311 //cerr << "initializing node " << i->getIndex() << endl;
314 i->setTrackDistance();
315 i->setIndex(index++);
316 //cerr << "Track distance = " << i->getLength() << endl;
317 //cerr << "Track ends at" << i->getEnd()->getIndex() << endl;
323 int FGGroundNetwork::findNearestNode(double lat, double lon)
325 double minDist = HUGE_VAL;
328 SGWayPoint first (lon,
332 for (FGTaxiNodeVectorIterator
334 itr != nodes.end(); itr++)
337 SGWayPoint second (itr->getLongitude(),
340 first.CourseAndDistance(second, &course, &dist);
344 index = itr->getIndex();
345 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
351 FGTaxiNode *FGGroundNetwork::findNode(int idx)
353 for (FGTaxiNodeVectorIterator
355 itr != nodes.end(); itr++)
357 if (itr->getIndex() == idx)
358 return itr->getAddress();
363 FGTaxiSegment *FGGroundNetwork::findSegment(int idx)
365 for (FGTaxiSegmentVectorIterator
366 itr = segments.begin();
367 itr != segments.end(); itr++)
369 if (itr->getIndex() == idx)
370 return itr->getAddress();
375 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end)
379 FGTaxiNode *firstNode = findNode(start);
380 FGTaxiNode *lastNode = findNode(end);
381 //prevNode = prevPrevNode = -1;
387 trace(firstNode, end, 0, 0);
392 SG_LOG( SG_GENERAL, SG_INFO, "Failed to find route from waypoint " << start << " to " << end );
395 sort(routes.begin(), routes.end());
396 //for (intVecIterator i = route.begin(); i != route.end(); i++)
398 // rte->push_back(*i);
401 if (routes.begin() != routes.end())
402 return *(routes.begin());
408 void FGGroundNetwork::trace(FGTaxiNode *currNode, int end, int depth, double distance)
410 // Just check some preconditions of the trace algorithm
411 if (nodesStack.size() != routesStack.size())
413 SG_LOG(SG_GENERAL, SG_ALERT, "size of nodesStack and routesStack is not equal. NodesStack :"
414 << nodesStack.size() << ". RoutesStack : " << routesStack.size());
416 nodesStack.push_back(currNode->getIndex());
417 totalDistance += distance;
418 //cerr << "Starting trace " << depth << " total distance: " << totalDistance<< endl;
419 //<< currNode->getIndex() << endl;
421 // If the current route matches the required end point we found a valid route
422 // So we can add this to the routing table
423 if (currNode->getIndex() == end)
425 //cerr << "Found route : " << totalDistance << "" << " " << *(nodesStack.end()-1) << endl;
426 routes.push_back(FGTaxiRoute(nodesStack,routesStack,totalDistance));
427 if (nodesStack.empty() || routesStack.empty())
429 printRoutingError(string("while finishing route"));
431 nodesStack.pop_back();
432 routesStack.pop_back();
434 maxDistance = totalDistance;
436 if (totalDistance < maxDistance)
437 maxDistance = totalDistance;
439 totalDistance -= distance;
444 // search if the currentNode has been encountered before
445 // if so, we should step back one level, because it is
446 // rather rediculous to proceed further from here.
447 // if the current node has not been encountered before,
448 // i should point to nodesStack.end()-1; and we can continue
449 // if i is not nodesStack.end, the previous node was found,
450 // and we should return.
451 // This only works at trace levels of 1 or higher though
453 intVecIterator i = nodesStack.begin();
454 while ((*i) != currNode->getIndex()) {
455 //cerr << "Route so far : " << (*i) << endl;
458 if (i != nodesStack.end()-1) {
459 if (nodesStack.empty() || routesStack.empty())
461 printRoutingError(string("while returning from an already encountered node"));
463 nodesStack.pop_back();
464 routesStack.pop_back();
465 totalDistance -= distance;
468 // If the total distance from start to the current waypoint
469 // is longer than that of a route we can also stop this trace
470 // and go back one level.
471 if ((totalDistance > maxDistance) && foundRoute)
473 //cerr << "Stopping rediculously long trace: " << totalDistance << endl;
474 if (nodesStack.empty() || routesStack.empty())
476 printRoutingError(string("while returning from finding a rediculously long route"));
478 nodesStack.pop_back();
479 routesStack.pop_back();
480 totalDistance -= distance;
485 //cerr << "2" << endl;
486 if (currNode->getBeginRoute() != currNode->getEndRoute())
488 //cerr << "3" << endl;
489 for (FGTaxiSegmentPointerVectorIterator
490 i = currNode->getBeginRoute();
491 i != currNode->getEndRoute();
494 //cerr << (*i)->getLength() << endl;
495 //cerr << (*i)->getIndex() << endl;
496 int idx = (*i)->getIndex();
497 routesStack.push_back((*i)->getIndex());
498 trace((*i)->getEnd(), end, depth+1, (*i)->getLength());
500 // // cerr << currNode -> getIndex() << " ";
501 // route.push_back(currNode->getIndex());
508 //SG_LOG( SG_GENERAL, SG_DEBUG, "4" );
510 if (nodesStack.empty())
512 printRoutingError(string("while finishing trace"));
514 nodesStack.pop_back();
515 // Make sure not to dump the level-zero routesStack entry, because that was never created.
518 routesStack.pop_back();
519 //cerr << "leaving trace " << routesStack.size() << endl;
521 totalDistance -= distance;
525 void FGGroundNetwork::printRoutingError(string mess)
527 SG_LOG(SG_GENERAL, SG_ALERT, "Error in ground network trace algorithm " << mess);
528 if (nodesStack.empty())
530 SG_LOG(SG_GENERAL, SG_ALERT, " nodesStack is empty. Dumping routesStack");
531 for (intVecIterator i = routesStack.begin() ; i != routesStack.end(); i++)
532 SG_LOG(SG_GENERAL, SG_ALERT, "Route " << (*i));
534 if (routesStack.empty())
536 SG_LOG(SG_GENERAL, SG_ALERT, " routesStack is empty. Dumping nodesStack");
537 for (intVecIterator i = nodesStack.begin() ; i != nodesStack.end(); i++)
538 SG_LOG(SG_GENERAL, SG_ALERT, "Node " << (*i));
544 void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
545 double lat, double lon, double heading,
546 double speed, double alt, double radius)
548 TrafficVectorIterator i = activeTraffic.begin();
549 // Search search if the current id alread has an entry
550 // This might be faster using a map instead of a vector, but let's start by taking a safe route
551 if (activeTraffic.size()) {
552 while ((i->getId() != id) && i != activeTraffic.end()) {
556 // Add a new TrafficRecord if no one exsists for this aircraft.
557 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
560 rec.setPositionAndIntentions(currentPosition, intendedRoute);
561 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
562 rec.setRadius(radius); // only need to do this when creating the record.
563 activeTraffic.push_back(rec);
565 i->setPositionAndIntentions(currentPosition, intendedRoute);
566 i->setPositionAndHeading(lat, lon, heading, speed, alt);
570 void FGGroundNetwork::signOff(int id) {
571 TrafficVectorIterator i = activeTraffic.begin();
572 // Search search if the current id alread has an entry
573 // This might be faster using a map instead of a vector, but let's start by taking a safe route
574 if (activeTraffic.size()) {
575 while ((i->getId() != id) && i != activeTraffic.end()) {
579 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
580 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off");
582 i = activeTraffic.erase(i);
586 void FGGroundNetwork::update(int id, double lat, double lon, double heading, double speed, double alt) {
587 TrafficVectorIterator i = activeTraffic.begin();
588 // Search search if the current id has an entry
589 // This might be faster using a map instead of a vector, but let's start by taking a safe route
590 TrafficVectorIterator current, closest;
591 if (activeTraffic.size()) {
592 while ((i->getId() != id) && i != activeTraffic.end()) {
596 // update position of the current aircraft
597 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
598 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
600 i->setPositionAndHeading(lat, lon, heading, speed, alt);
604 // Scan for a speed adjustment change. Find the nearest aircraft that is in front
605 // and adjust speed when we get too close. Only do this when current position and/or
606 // intentions of the current aircraft match current taxiroute position of the proximate
608 double mindist = HUGE;
609 if (activeTraffic.size())
611 double course, dist, bearing, minbearing;
613 //TrafficVector iterator closest;
614 for (TrafficVectorIterator i = activeTraffic.begin();
615 i != activeTraffic.end(); i++)
618 SGWayPoint curr (lon,
621 SGWayPoint other (i->getLongitude (),
624 other.CourseAndDistance(curr, &course, &dist);
625 bearing = fabs(heading-course);
627 bearing = 360-bearing;
628 if ((dist < mindist) && (bearing < 60.0))
632 minbearing = bearing;
636 //cerr << "Distance : " << dist << " bearing : " << bearing << " heading : " << heading
637 // << " course : " << course << endl;
638 current->clearSpeedAdjustment();
639 // Only clear the heading adjustment at positive speeds, otherwise the waypoint following
642 current->clearHeadingAdjustment();
646 //current->clearSpeedAdjustment();
647 //current->clearHeadingAdjustment();
651 if (current->getId() == closest->getWaitsForId())
654 current->setWaitsForId(closest->getId());
656 // Getting close: Slow down to a bit less than the other aircraft
657 double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
658 if (mindist > maxAllowableDistance)
660 if (current->checkPositionAndIntentions(*closest))
662 // Adjust speed, but don't let it drop to below 1 knots
663 //if (fabs(speed) > 1)
664 if (!(current->hasHeadingAdjustment()))
666 current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
667 //cerr << "Adjusting speed to " << closest->getSpeed() * (mindist / 100) << " "
668 // << "Bearing = " << minbearing << " Distance = " << mindist
669 // << " Latitude = " <<lat << " longitude = " << lon << endl;
670 //<< " Latitude = " <<closest->getLatitude()
671 //<< " longitude = " << closest->getLongitude()
676 double newSpeed = (maxAllowableDistance-mindist);
677 current->setSpeedAdjustment(newSpeed);
683 if (!(current->hasHeadingAdjustment()))
688 current->setSpeedAdjustment(newSpeed);
690 newSpeed = -1 * (maxAllowableDistance-mindist);
691 current->setSpeedAdjustment(newSpeed);
692 current->setHeadingAdjustment(heading);
693 // if (mindist < 5) {
694 // double bank_sense = 0;
695 // current->setSpeedAdjustment(-0.1);
696 // // Do a heading adjustment
697 // double diff = fabs(heading - bearing);
699 // diff = fabs(diff - 360);
701 // double sum = heading + diff;
704 // if (fabs(sum - bearing) < 1.0) {
705 // bank_sense = -1.0; // turn left for evasive action
707 // bank_sense = 1.0; // turn right for evasive action
709 // double newHeading = heading + bank_sense;
710 // if (newHeading < 0) newHeading += 360;
711 // if (newHeading > 360) newHeading -= 360;
712 // current->setHeadingAdjustment(newHeading);
713 // //cerr << "Yikes: TOOOO close. backing up and turning to heading " << newHeading
715 // cerr << "Troubleshooting: " << current->getId() << " Closest : " << closest->getId()
725 bool FGGroundNetwork::hasInstruction(int id)
727 TrafficVectorIterator i = activeTraffic.begin();
728 // Search search if the current id has an entry
729 // This might be faster using a map instead of a vector, but let's start by taking a safe route
730 if (activeTraffic.size()) {
731 while ((i->getId() != id) && i != activeTraffic.end()) {
735 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
736 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
738 return i->hasInstruction();
742 FGATCInstruction FGGroundNetwork::getInstruction(int id)
744 TrafficVectorIterator i = activeTraffic.begin();
745 // Search search if the current id has an entry
746 // This might be faster using a map instead of a vector, but let's start by taking a safe route
747 if (activeTraffic.size()) {
748 while ((i->getId() != id) && i != activeTraffic.end()) {
752 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
753 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
755 return i->getInstruction();
762 /***************************************************************************
765 * This class is really out of place here, and should be combined with
766 * FGATC controller and go into it's own file / directory
767 * I'm leaving it for now though, because I'm testing this stuff quite
769 **************************************************************************/
770 FGATCInstruction::FGATCInstruction()
773 holdPosition = false;
775 changeHeading = false;
776 changeAltitude = false;
783 bool FGATCInstruction::hasInstruction()
785 return (holdPattern || holdPosition || changeSpeed || changeHeading || changeAltitude);