]> git.mxchange.org Git - flightgear.git/blob - src/Airports/groundnetwork.cxx
Merge branch 'next' into durk-atc
[flightgear.git] / src / Airports / groundnetwork.cxx
1
2 // groundnet.cxx - Implimentation of the FlightGear airport ground handling code
3 //
4 // Written by Durk Talsma, started June 2005.
5 //
6 // Copyright (C) 2004 Durk Talsma.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 // $Id$
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <math.h>
29 #include <algorithm>
30
31
32 #include <osg/Geode>
33 #include <osg/Geometry>
34 #include <osg/MatrixTransform>
35 #include <osg/Shape>
36
37 #include <simgear/debug/logstream.hxx>
38 #include <simgear/route/waypoint.hxx>
39 #include <simgear/scene/material/EffectGeode.hxx>
40 #include <simgear/scene/material/matlib.hxx>
41 #include <simgear/scene/material/mat.hxx>
42
43 #include <Airports/simple.hxx>
44 #include <Airports/dynamics.hxx>
45
46 #include <AIModel/AIAircraft.hxx>
47 #include <AIModel/AIFlightPlan.hxx>
48
49 #include <ATC/atc_mgr.hxx>
50
51 #include <Scenery/scenery.hxx>
52
53 #include "groundnetwork.hxx"
54
55 /***************************************************************************
56  * FGTaxiSegment
57  **************************************************************************/
58
59 void FGTaxiSegment::setStart(FGTaxiNodeVector * nodes)
60 {
61     FGTaxiNodeVectorIterator i = nodes->begin();
62     while (i != nodes->end()) {
63         //cerr << "Scanning start node index" << (*i)->getIndex() << endl;
64         if ((*i)->getIndex() == startNode) {
65             start = (*i)->getAddress();
66             (*i)->addSegment(this);
67             return;
68         }
69         i++;
70     }
71     SG_LOG(SG_GENERAL, SG_ALERT,
72            "Could not find start node " << startNode << endl);
73 }
74
75 void FGTaxiSegment::setEnd(FGTaxiNodeVector * nodes)
76 {
77     FGTaxiNodeVectorIterator i = nodes->begin();
78     while (i != nodes->end()) {
79         //cerr << "Scanning end node index" << (*i)->getIndex() << endl;
80         if ((*i)->getIndex() == endNode) {
81             end = (*i)->getAddress();
82             return;
83         }
84         i++;
85     }
86     SG_LOG(SG_GENERAL, SG_ALERT,
87            "Could not find end node " << endNode << endl);
88 }
89
90
91
92 // There is probably a computationally cheaper way of 
93 // doing this.
94 void FGTaxiSegment::setDimensions(double elevation)
95 {
96     length = SGGeodesy::distanceM(start->getGeod(), end->getGeod());
97     //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
98
99     double az2; //, distanceM;
100     SGGeodesy::inverse(start->getGeod(), end->getGeod(), heading, az2, length);
101     double coveredDistance = length * 0.5;
102     SGGeodesy::direct(start->getGeod(), heading, coveredDistance, center, az2);
103     //cerr << "Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
104 }
105
106
107 //void FGTaxiSegment::setCourseDiff(double crse)
108 //{
109 //    headingDiff = fabs(course - crse);
110
111 //    if (headingDiff > 180)
112 //        headingDiff = fabs(headingDiff - 360);
113 //}
114
115
116 /***************************************************************************
117  * FGTaxiRoute
118  **************************************************************************/
119 bool FGTaxiRoute::next(int *nde)
120 {
121     //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
122     //  cerr << "FGTaxiRoute contains : " << *(i) << endl;
123     //cerr << "Offset from end: " << nodes.end() - currNode << endl;
124     //if (currNode != nodes.end())
125     //  cerr << "true" << endl;
126     //else
127     //  cerr << "false" << endl;
128     //if (nodes.size() != (routes.size()) +1)
129     //  cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
130
131     if (currNode == nodes.end())
132         return false;
133     *nde = *(currNode);
134     if (currNode != nodes.begin())      // make sure route corresponds to the end node
135         currRoute++;
136     currNode++;
137     return true;
138 };
139
140 bool FGTaxiRoute::next(int *nde, int *rte)
141 {
142     //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
143     //  cerr << "FGTaxiRoute contains : " << *(i) << endl;
144     //cerr << "Offset from end: " << nodes.end() - currNode << endl;
145     //if (currNode != nodes.end())
146     //  cerr << "true" << endl;
147     //else
148     //  cerr << "false" << endl;
149     if (nodes.size() != (routes.size()) + 1) {
150         SG_LOG(SG_GENERAL, SG_ALERT,
151                "ALERT: Misconfigured TaxiRoute : " << nodes.
152                size() << " " << routes.size());
153         exit(1);
154     }
155     if (currNode == nodes.end())
156         return false;
157     *nde = *(currNode);
158     //*rte = *(currRoute);
159     if (currNode != nodes.begin())      // Make sure route corresponds to the end node
160     {
161         *rte = *(currRoute);
162         currRoute++;
163     } else {
164         // If currNode points to the first node, this means the aircraft is not on the taxi node
165         // yet. Make sure to return a unique identifyer in this situation though, because otherwise
166         // the speed adjust AI code may be unable to resolve whether two aircraft are on the same 
167         // taxi route or not. the negative of the preceding route seems a logical choice, as it is 
168         // unique for any starting location. 
169         // Note that this is probably just a temporary fix until I get Parking / tower control working.
170         *rte = -1 * *(currRoute);
171     }
172     currNode++;
173     return true;
174 };
175
176
177 void FGTaxiRoute::rewind(int route)
178 {
179     int currPoint;
180     int currRoute;
181     first();
182     do {
183         if (!(next(&currPoint, &currRoute))) {
184             SG_LOG(SG_GENERAL, SG_ALERT,
185                    "Error in rewinding TaxiRoute: current" << currRoute <<
186                    " goal " << route);
187         }
188     } while (currRoute != route);
189 }
190
191
192
193
194 /***************************************************************************
195  * FGGroundNetwork()
196  **************************************************************************/
197 bool compare_nodes(FGTaxiNode * a, FGTaxiNode * b)
198 {
199     return (*a) < (*b);
200 }
201
202 bool compare_segments(FGTaxiSegment * a, FGTaxiSegment * b)
203 {
204     return (*a) < (*b);
205 }
206
207 FGGroundNetwork::FGGroundNetwork()
208 {
209     hasNetwork = false;
210     foundRoute = false;
211     totalDistance = 0;
212     maxDistance = 0;
213     //maxDepth    = 1000;
214     count = 0;
215     currTraffic = activeTraffic.begin();
216     group = 0;
217
218 }
219
220 FGGroundNetwork::~FGGroundNetwork()
221 {
222     for (FGTaxiNodeVectorIterator node = nodes.begin();
223          node != nodes.end(); node++) {
224         delete(*node);
225     }
226     nodes.clear();
227     pushBackNodes.clear();
228     for (FGTaxiSegmentVectorIterator seg = segments.begin();
229          seg != segments.end(); seg++) {
230         delete(*seg);
231     }
232     segments.clear();
233 }
234
235 void FGGroundNetwork::addSegment(const FGTaxiSegment & seg)
236 {
237     segments.push_back(new FGTaxiSegment(seg));
238 }
239
240 void FGGroundNetwork::addNode(const FGTaxiNode & node)
241 {
242     nodes.push_back(new FGTaxiNode(node));
243 }
244
245 void FGGroundNetwork::addNodes(FGParkingVec * parkings)
246 {
247     FGTaxiNode n;
248     FGParkingVecIterator i = parkings->begin();
249     while (i != parkings->end()) {
250         n.setIndex(i->getIndex());
251         n.setLatitude(i->getLatitude());
252         n.setLongitude(i->getLongitude());
253         n.setElevation(parent->getElevation());
254         nodes.push_back(new FGTaxiNode(n));
255
256         i++;
257     }
258 }
259
260
261
262 void FGGroundNetwork::init()
263 {
264     hasNetwork = true;
265     int index = 1;
266     sort(nodes.begin(), nodes.end(), compare_nodes);
267     //sort(segments.begin(), segments.end(), compare_segments());
268     FGTaxiSegmentVectorIterator i = segments.begin();
269     while (i != segments.end()) {
270         (*i)->setStart(&nodes);
271         (*i)->setEnd(&nodes);
272         (*i)->setDimensions(parent->getElevation());
273         (*i)->setIndex(index);
274         if ((*i)->isPushBack()) {
275             pushBackNodes.push_back((*i)->getEnd());
276         }
277         //SG_LOG(SG_GENERAL, SG_BULK,  "initializing segment " << (*i)->getIndex() << endl);
278         //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = "     << (*i)->getLength() << endl);
279         //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from "      << (*i)->getStart()->getIndex() << " to "
280         //                                                    << (*i)->getEnd()->getIndex() << endl);
281         i++;
282         index++;
283     }
284
285     i = segments.begin();
286     while (i != segments.end()) {
287         FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute();
288         while (j != (*i)->getEnd()->getEndRoute()) {
289             if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex()) {
290 //          int start1 = (*i)->getStart()->getIndex();
291 //          int end1   = (*i)->getEnd()  ->getIndex();
292 //          int start2 = (*j)->getStart()->getIndex();
293 //          int end2   = (*j)->getEnd()->getIndex();
294 //          int oppIndex = (*j)->getIndex();
295                 //cerr << "Opposite of  " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
296                 //   << "happens to be " << oppIndex      << " (" << start2 << "," << end2 << ") " << endl;
297                 (*i)->setOpposite(*j);
298                 break;
299             }
300             j++;
301         }
302         i++;
303     }
304     //FGTaxiNodeVectorIterator j = nodes.begin();
305     //while (j != nodes.end()) {
306     //    if ((*j)->getHoldPointType() == 3) {
307     //        pushBackNodes.push_back((*j));
308     //    }
309     //    j++;
310     //}
311     //cerr << "Done initializing ground network" << endl;
312     //exit(1);
313 }
314
315 int FGGroundNetwork::findNearestNode(const SGGeod & aGeod)
316 {
317     double minDist = HUGE_VAL;
318     int index = -1;
319
320     for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end();
321          itr++) {
322         double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod());
323         if (d < minDist) {
324             minDist = d;
325             index = (*itr)->getIndex();
326             //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
327         }
328     }
329
330     return index;
331 }
332
333 int FGGroundNetwork::findNearestNode(double lat, double lon)
334 {
335     return findNearestNode(SGGeod::fromDeg(lon, lat));
336 }
337
338 FGTaxiNode *FGGroundNetwork::findNode(unsigned idx)
339 {                               /*
340                                    for (FGTaxiNodeVectorIterator 
341                                    itr = nodes.begin();
342                                    itr != nodes.end(); itr++)
343                                    {
344                                    if (itr->getIndex() == idx)
345                                    return itr->getAddress();
346                                    } */
347
348     if ((idx >= 0) && (idx < nodes.size()))
349         return nodes[idx]->getAddress();
350     else
351         return 0;
352 }
353
354 FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx)
355 {                               /*
356                                    for (FGTaxiSegmentVectorIterator 
357                                    itr = segments.begin();
358                                    itr != segments.end(); itr++)
359                                    {
360                                    if (itr->getIndex() == idx)
361                                    return itr->getAddress();
362                                    } 
363                                  */
364     if ((idx > 0) && (idx <= segments.size()))
365         return segments[idx - 1]->getAddress();
366     else {
367         //cerr << "Alert: trying to find invalid segment " << idx << endl;
368         return 0;
369     }
370 }
371
372
373 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end,
374                                                bool fullSearch)
375 {
376 //implements Dijkstra's algorithm to find shortest distance route from start to end
377 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
378
379     //double INFINITE = 100000000000.0;
380     // initialize scoring values
381     int nParkings = parent->getDynamics()->getNrOfParkings();
382     FGTaxiNodeVector *currNodesSet;
383     if (fullSearch) {
384         currNodesSet = &nodes;
385     } else {
386         currNodesSet = &pushBackNodes;
387     }
388
389     for (FGTaxiNodeVectorIterator
390          itr = currNodesSet->begin(); itr != currNodesSet->end(); itr++) {
391         (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
392         (*itr)->setPreviousNode(0);     //
393         (*itr)->setPreviousSeg(0);      //
394     }
395
396     FGTaxiNode *firstNode = findNode(start);
397     firstNode->setPathScore(0);
398
399     FGTaxiNode *lastNode = findNode(end);
400
401     FGTaxiNodeVector unvisited(*currNodesSet);  // working copy
402
403     while (!unvisited.empty()) {
404         FGTaxiNode *best = *(unvisited.begin());
405         for (FGTaxiNodeVectorIterator
406              itr = unvisited.begin(); itr != unvisited.end(); itr++) {
407             if ((*itr)->getPathScore() < best->getPathScore())
408                 best = (*itr);
409         }
410
411         FGTaxiNodeVectorIterator newend =
412             remove(unvisited.begin(), unvisited.end(), best);
413         unvisited.erase(newend, unvisited.end());
414
415         if (best == lastNode) { // found route or best not connected
416             break;
417         } else {
418             for (FGTaxiSegmentVectorIterator
419                  seg = best->getBeginRoute();
420                  seg != best->getEndRoute(); seg++) {
421                 if (fullSearch || (*seg)->isPushBack()) {
422                     FGTaxiNode *tgt = (*seg)->getEnd();
423                     double alt =
424                         best->getPathScore() + (*seg)->getLength() +
425                         (*seg)->getPenalty(nParkings);
426                     if (alt < tgt->getPathScore()) {    // Relax (u,v)
427                         tgt->setPathScore(alt);
428                         tgt->setPreviousNode(best);
429                         tgt->setPreviousSeg(*seg);      //
430                     }
431                 } else {
432                     //   // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
433                 }
434             }
435         }
436     }
437
438     if (lastNode->getPathScore() == HUGE_VAL) {
439         // no valid route found
440         if (fullSearch) {
441             SG_LOG(SG_GENERAL, SG_ALERT,
442                    "Failed to find route from waypoint " << start << " to "
443                    << end << " at " << parent->getId());
444         }
445         FGTaxiRoute empty;
446         return empty;
447         //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
448     } else {
449         // assemble route from backtrace information
450         intVec nodes, routes;
451         FGTaxiNode *bt = lastNode;
452         while (bt->getPreviousNode() != 0) {
453             nodes.push_back(bt->getIndex());
454             routes.push_back(bt->getPreviousSegment()->getIndex());
455             bt = bt->getPreviousNode();
456         }
457         nodes.push_back(start);
458         reverse(nodes.begin(), nodes.end());
459         reverse(routes.begin(), routes.end());
460
461         return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
462     }
463 }
464
465 int FGTaxiSegment::getPenalty(int nGates)
466 {
467     int penalty = 0;
468     if (end->getIndex() < nGates) {
469         penalty += 10000;
470     }
471     if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
472         penalty += 1000;
473     }
474     return penalty;
475 }
476
477 /* ATC Related Functions */
478
479 void FGGroundNetwork::announcePosition(int id,
480                                        FGAIFlightPlan * intendedRoute,
481                                        int currentPosition, double lat,
482                                        double lon, double heading,
483                                        double speed, double alt,
484                                        double radius, int leg,
485                                        FGAIAircraft * aircraft)
486 {
487     init();
488     TrafficVectorIterator i = activeTraffic.begin();
489     // Search search if the current id alread has an entry
490     // This might be faster using a map instead of a vector, but let's start by taking a safe route
491     if (activeTraffic.size()) {
492         //while ((i->getId() != id) && i != activeTraffic.end()) {
493         while (i != activeTraffic.end()) {
494             if (i->getId() == id) {
495                 break;
496             }
497             i++;
498         }
499     }
500     // Add a new TrafficRecord if no one exsists for this aircraft.
501     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
502         FGTrafficRecord rec;
503         rec.setId(id);
504         rec.setLeg(leg);
505         rec.setPositionAndIntentions(currentPosition, intendedRoute);
506         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
507         rec.setRadius(radius);  // only need to do this when creating the record.
508         rec.setAircraft(aircraft);
509         activeTraffic.push_back(rec);
510     } else {
511         i->setPositionAndIntentions(currentPosition, intendedRoute);
512         i->setPositionAndHeading(lat, lon, heading, speed, alt);
513     }
514 }
515
516
517 void FGGroundNetwork::signOff(int id)
518 {
519     TrafficVectorIterator i = activeTraffic.begin();
520     // Search search if the current id alread has an entry
521     // This might be faster using a map instead of a vector, but let's start by taking a safe route
522     if (activeTraffic.size()) {
523         //while ((i->getId() != id) && i != activeTraffic.end()) {
524         while (i != activeTraffic.end()) {
525             if (i->getId() == id) {
526                 break;
527             }
528             i++;
529         }
530     }
531     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
532         SG_LOG(SG_GENERAL, SG_ALERT,
533                "AI error: Aircraft without traffic record is signing off");
534     } else {
535         i = activeTraffic.erase(i);
536     }
537 }
538 /**
539  * The ground network can deal with the following states:
540  * 0 =  Normal; no action required
541  * 1 = "Acknowledge "Hold position
542  * 2 = "Acknowledge "Resume taxi".
543  * 3 = "Issue TaxiClearance"
544  * 4 = =Acknowledge Taxi Clearance"
545  *
546  *************************************************************************************************************************/
547 bool FGGroundNetwork::checkTransmissionState(int minState, int maxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId,
548                                AtcMsgDir msgDir)
549 {
550     int state = i->getState();
551     if ((state >= minState) && (state <= maxState) && available) {
552         if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
553             //cerr << "Checking state " << state << " for " << i->getAircraft()->getCallSign() << endl;
554             static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
555             int n = trans_num->getIntValue();
556             if (n == 0) {
557                 trans_num->setIntValue(-1);
558                  // PopupCallback(n);
559                  cerr << "Selected transmission message " << n << endl;
560                  FGATCManager *atc = (FGATCManager*) globals->get_subsystem("atc");
561                  atc->getATCDialog()->removeEntry(1);
562             } else {
563                 //cerr << "creating message for " << i->getAircraft()->getCallSign() << endl;
564                 transmit(&(*i), msgId, msgDir, false);
565                 return false;
566             }
567         }
568         transmit(&(*i), msgId, msgDir, true);
569         i->updateState();
570         lastTransmission = now;
571         available = false;
572         return true;
573     }
574     return false;
575 }
576
577 void FGGroundNetwork::updateAircraftInformation(int id, double lat, double lon,
578                                                 double heading, double speed, double alt,
579                                                 double dt)
580 {
581     // Check whether aircraft are on hold due to a preceding pushback. If so, make sure to 
582     // Transmit air-to-ground "Ready to taxi request:
583     // Transmit ground to air approval / hold
584     // Transmit confirmation ... 
585     // Probably use a status mechanism similar to the Engine start procedure in the startup controller.
586
587
588     TrafficVectorIterator i = activeTraffic.begin();
589     // Search search if the current id has an entry
590     // This might be faster using a map instead of a vector, but let's start by taking a safe route
591     TrafficVectorIterator current, closest;
592     if (activeTraffic.size()) {
593         //while ((i->getId() != id) && i != activeTraffic.end()) {
594         while (i != activeTraffic.end()) {
595             if (i->getId() == id) {
596                 break;
597             }
598             i++;
599         }
600     }
601     // update position of the current aircraft
602     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
603         SG_LOG(SG_GENERAL, SG_ALERT,
604                "AI error: updating aircraft without traffic record");
605     } else {
606         i->setPositionAndHeading(lat, lon, heading, speed, alt);
607         current = i;
608     }
609
610     setDt(getDt() + dt);
611
612     // Update every three secs, but add some randomness
613     // to prevent all IA objects doing this in synchrony
614     //if (getDt() < (3.0) + (rand() % 10))
615     //  return;
616     //else
617     //  setDt(0);
618     current->clearResolveCircularWait();
619     current->setWaitsForId(0);
620     checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
621     bool needsTaxiClearance = current->getAircraft()->getTaxiClearanceRequest();
622     if (!needsTaxiClearance) {
623         checkHoldPosition(id, lat, lon, heading, speed, alt);
624         if (checkForCircularWaits(id)) {
625             i->setResolveCircularWait();
626         }
627     } else {
628         current->setHoldPosition(true);
629         int state = current->getState();
630         time_t now = time(NULL) + fgGetLong("/sim/time/warp");
631         if ((now - lastTransmission) > 15) {
632             available = true;
633         }
634         if (checkTransmissionState(0,2, current, now, MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) {
635             current->setState(3);
636         }
637         if (checkTransmissionState(3,3, current, now, MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR)) {
638             current->setState(4);
639         }
640         if (checkTransmissionState(4,4, current, now, MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) {
641             current->setState(5);
642         }
643         if ((state == 5) && available) {
644             current->setState(0);
645             current->getAircraft()->setTaxiClearanceRequest(false);
646             current->setHoldPosition(false);
647             available = false;
648         }
649
650     }
651
652 }
653
654 /**
655    Scan for a speed adjustment change. Find the nearest aircraft that is in front
656    and adjust speed when we get too close. Only do this when current position and/or
657    intentions of the current aircraft match current taxiroute position of the proximate
658    aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
659    instruction. See below for the hold position instruction.
660
661    Note that there currently still is one flaw in the logic that needs to be addressed. 
662    There can be situations where one aircraft is in front of the current aircraft, on a separate
663    route, but really close after an intersection coming off the current route. This
664    aircraft is still close enough to block the current aircraft. This situation is currently
665    not addressed yet, but should be.
666 */
667
668 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
669                                            double lon, double heading,
670                                            double speed, double alt)
671 {
672
673     TrafficVectorIterator current, closest;
674     TrafficVectorIterator i = activeTraffic.begin();
675     bool otherReasonToSlowDown = false;
676     bool previousInstruction;
677     if (activeTraffic.size()) {
678         //while ((i->getId() != id) && (i != activeTraffic.end()))
679         while (i != activeTraffic.end()) {
680             if (i->getId() == id) {
681                 break;
682             }
683             i++;
684         }
685     } else {
686         return;
687     }
688     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
689         SG_LOG(SG_GENERAL, SG_ALERT,
690                "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
691     }
692     current = i;
693     //closest = current;
694
695     previousInstruction = current->getSpeedAdjustment();
696     double mindist = HUGE_VAL;
697     if (activeTraffic.size()) {
698         double course, dist, bearing, minbearing, az2;
699         SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
700         //TrafficVector iterator closest;
701         closest = current;
702         for (TrafficVectorIterator i = activeTraffic.begin();
703              i != activeTraffic.end(); i++) {
704             if (i == current) {
705                 continue;
706             }
707
708             SGGeod other(SGGeod::fromDegM(i->getLongitude(),
709                                           i->getLatitude(),
710                                           i->getAltitude()));
711             SGGeodesy::inverse(curr, other, course, az2, dist);
712             bearing = fabs(heading - course);
713             if (bearing > 180)
714                 bearing = 360 - bearing;
715             if ((dist < mindist) && (bearing < 60.0)) {
716                 mindist = dist;
717                 closest = i;
718                 minbearing = bearing;
719             }
720         }
721         //Check traffic at the tower controller
722         if (towerController->hasActiveTraffic()) {
723             for (TrafficVectorIterator i =
724                  towerController->getActiveTraffic().begin();
725                  i != towerController->getActiveTraffic().end(); i++) {
726                 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
727                 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
728                                               i->getLatitude(),
729                                               i->getAltitude()));
730                 SGGeodesy::inverse(curr, other, course, az2, dist);
731                 bearing = fabs(heading - course);
732                 if (bearing > 180)
733                     bearing = 360 - bearing;
734                 if ((dist < mindist) && (bearing < 60.0)) {
735                     //cerr << "Current aircraft " << current->getAircraft()->getTrafficRef()->getCallSign()
736                     //     << " is closest to " << i->getAircraft()->getTrafficRef()->getCallSign() 
737                     //     << ", which has status " << i->getAircraft()->isScheduledForTakeoff() 
738                     //     << endl;
739                     mindist = dist;
740                     closest = i;
741                     minbearing = bearing;
742                     otherReasonToSlowDown = true;
743                 }
744             }
745         }
746         // Finally, check UserPosition
747         // Note, as of 2011-08-01, this should no longer be necessecary.
748         /*
749         double userLatitude = fgGetDouble("/position/latitude-deg");
750         double userLongitude = fgGetDouble("/position/longitude-deg");
751         SGGeod user(SGGeod::fromDeg(userLongitude, userLatitude));
752         SGGeodesy::inverse(curr, user, course, az2, dist);
753
754         bearing = fabs(heading - course);
755         if (bearing > 180)
756             bearing = 360 - bearing;
757         if ((dist < mindist) && (bearing < 60.0)) {
758             mindist = dist;
759             //closest = i;
760             minbearing = bearing;
761             otherReasonToSlowDown = true;
762         }
763         */
764         current->clearSpeedAdjustment();
765
766         if (current->checkPositionAndIntentions(*closest)
767             || otherReasonToSlowDown) {
768             double maxAllowableDistance =
769                 (1.1 * current->getRadius()) +
770                 (1.1 * closest->getRadius());
771             if (mindist < 2 * maxAllowableDistance) {
772                 if (current->getId() == closest->getWaitsForId())
773                     return;
774                 else
775                     current->setWaitsForId(closest->getId());
776                 if (closest->getId() != current->getId()) {
777                     current->setSpeedAdjustment(closest->getSpeed() *
778                                                 (mindist / 100));
779                     if ( 
780                         closest->getAircraft()->getTakeOffStatus() && 
781                         (current->getAircraft()->getTrafficRef()->getDepartureAirport() ==  closest->getAircraft()->getTrafficRef()->getDepartureAirport()) &&
782                         (current->getAircraft()->GetFlightPlan()->getRunway() == closest->getAircraft()->GetFlightPlan()->getRunway())
783                        )
784                             current->getAircraft()->scheduleForATCTowerDepartureControl(1); 
785                 } else {
786                     current->setSpeedAdjustment(0);     // This can only happen when the user aircraft is the one closest
787                 }
788                 if (mindist < maxAllowableDistance) {
789                     //double newSpeed = (maxAllowableDistance-mindist);
790                     //current->setSpeedAdjustment(newSpeed);
791                     //if (mindist < 0.5* maxAllowableDistance)
792                     //  {
793                     current->setSpeedAdjustment(0);
794                     //  }
795                 }
796             }
797         }
798     }
799 }
800
801 /**
802    Check for "Hold position instruction".
803    The hold position should be issued under the following conditions:
804    1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
805    2) For taxiing aircraft that use one taxiway in opposite directions
806    3) For crossing or merging taxiroutes.
807 */
808
809 void FGGroundNetwork::checkHoldPosition(int id, double lat,
810                                         double lon, double heading,
811                                         double speed, double alt)
812 {
813     TrafficVectorIterator current;
814     TrafficVectorIterator i = activeTraffic.begin();
815     if (activeTraffic.size()) {
816         //while ((i->getId() != id) && i != activeTraffic.end()) 
817         while (i != activeTraffic.end()) {
818             if (i->getId() == id) {
819                 break;
820             }
821             i++;
822         }
823     } else {
824         return;
825     }
826     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
827         SG_LOG(SG_GENERAL, SG_ALERT,
828                "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
829     }
830     current = i;
831     bool origStatus = current->hasHoldPosition();
832     current->setHoldPosition(false);
833     SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
834
835     for (i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
836         if (i->getId() != current->getId()) {
837             int node = current->crosses(this, *i);
838             if (node != -1) {
839                 FGTaxiNode *taxiNode = findNode(node);
840
841                 // Determine whether it's save to continue or not. 
842                 // If we have a crossing route, there are two possibilities:
843                 // 1) This is an interestion
844                 // 2) This is oncoming two-way traffic, using the same taxiway.
845                 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
846
847                 SGGeod other(SGGeod::
848                              fromDegM(i->getLongitude(), i->getLatitude(),
849                                       i->getAltitude()));
850                 bool needsToWait;
851                 bool opposing;
852                 if (current->isOpposing(this, *i, node)) {
853                     needsToWait = true;
854                     opposing = true;
855                     //cerr << "Hold check 2 : " << node << "  has opposing segment " << endl;
856                     // issue a "Hold Position" as soon as we're close to the offending node
857                     // For now, I'm doing this as long as the other aircraft doesn't
858                     // have a hold instruction as soon as we're within a reasonable 
859                     // distance from the offending node.
860                     // This may be a bit of a conservative estimate though, as it may
861                     // be well possible that both aircraft can both continue to taxi 
862                     // without crashing into each other.
863                 } else {
864                     opposing = false;
865                     if (SGGeodesy::distanceM(other, taxiNode->getGeod()) > 200) // 2.0*i->getRadius())
866                     {
867                         needsToWait = false;
868                         //cerr << "Hold check 3 : " << id <<"  Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue " 
869                         //           << endl;
870                     } else {
871                         needsToWait = true;
872                         //cerr << "Hold check 4: " << id << "  Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
873                     }
874                 }
875
876                 double dist =
877                     SGGeodesy::distanceM(curr, taxiNode->getGeod());
878                 if (!(i->hasHoldPosition())) {
879
880                     if ((dist < 200) && //2.5*current->getRadius()) && 
881                         (needsToWait) && (i->onRoute(this, *current)) &&
882                         //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
883                         (!(current->getId() == i->getWaitsForId())))
884                         //(!(i->getSpeedAdjustment()))) // &&
885                         //(!(current->getSpeedAdjustment())))
886
887                     {
888                         if (!(isUserAircraft(i->getAircraft()))) { // test code. Don't wait for the user, let the user wait for you.
889                             current->setHoldPosition(true);
890                             current->setWaitsForId(i->getId());
891                         }
892                         //cerr << "Hold check 5: " << current->getCallSign() <<"  Setting Hold Position: distance to node ("  << node << ") "
893                         //           << dist << " meters. Waiting for " << i->getCallSign();
894                         //if (opposing)
895                         //cerr <<" [opposing] " << endl;
896                         //else
897                         //        cerr << "[non-opposing] " << endl;
898                         //if (i->hasSpeefAdjustment())
899                         //        {
900                         //          cerr << " (which in turn waits for ) " << i->
901                     } else {
902                         //cerr << "Hold check 6: " << id << "  No need to hold yet: Distance to node : " << dist << " nm"<< endl;
903                     }
904                 }
905             }
906         }
907     }
908     bool currStatus = current->hasHoldPosition();
909     current->setHoldPosition(origStatus);
910     // Either a Hold Position or a resume taxi transmission has been issued
911     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
912     if ((now - lastTransmission) > 2) {
913         available = true;
914     }
915     if (current->getState() == 0) {
916         if ((origStatus != currStatus) && available) {
917             cerr << "Issueing hold short instrudtion " << currStatus << " " << available << endl;
918             if (currStatus == true) { // No has a hold short instruction
919                 transmit(&(*current), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR, true);
920                 cerr << "Transmittin hold short instrudtion " << currStatus << " " << available << endl;
921                 current->setState(1);
922             } else {
923                 transmit(&(*current), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR, true);
924                 cerr << "Transmittig resume instrudtion " << currStatus << " " << available << endl;
925                 current->setState(2);
926             }
927             lastTransmission = now;
928             available = false;
929             // Don't act on the changed instruction until the transmission is confirmed
930             // So set back to original status
931             //cerr << "Current state " << current->getState() << endl;
932         }
933     }
934     //int state = current->getState();
935     if (checkTransmissionState(1,1, current, now, MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND)) {
936             current->setState(0);
937             current->setHoldPosition(true);
938     }
939     if (checkTransmissionState(2,2, current, now, MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND)) {
940             current->setState(0);
941             current->setHoldPosition(false);
942     }
943
944
945 /**
946  * Check whether situations occur where the current aircraft is waiting for itself
947  * due to higher order interactions. 
948  * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
949  * for a. Ideally each aircraft only waits for one other aircraft, so by tracing 
950  * through this list of waiting aircraft, we can check if we'd eventually end back 
951  * at the current aircraft.
952  *
953  * Note that we should consider the situation where we are actually checking aircraft
954  * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
955  * the looping aircraft. If we don't check for that, this function will get stuck into
956  * endless loop.
957  */
958
959 bool FGGroundNetwork::checkForCircularWaits(int id)
960 {
961     //cerr << "Performing Wait check " << id << endl;
962     int target = 0;
963     TrafficVectorIterator current, other;
964     TrafficVectorIterator i = activeTraffic.begin();
965     int trafficSize = activeTraffic.size();
966     if (trafficSize) {
967         while (i != activeTraffic.end()) {
968             if (i->getId() == id) {
969                 break;
970             }
971             i++;
972         }
973     } else {
974         return false;
975     }
976     if (i == activeTraffic.end() || (trafficSize == 0)) {
977         SG_LOG(SG_GENERAL, SG_ALERT,
978                "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
979     }
980
981     current = i;
982     target = current->getWaitsForId();
983     //bool printed = false; // Note that this variable is for debugging purposes only.
984     int counter = 0;
985
986     if (id == target) {
987         //cerr << "aircraft waits for user" << endl;
988         return false;
989     }
990
991
992     while ((target > 0) && (target != id) && counter++ < trafficSize) {
993         //printed = true;
994         TrafficVectorIterator i = activeTraffic.begin();
995         if (trafficSize) {
996             //while ((i->getId() != id) && i != activeTraffic.end()) 
997             while (i != activeTraffic.end()) {
998                 if (i->getId() == target) {
999                     break;
1000                 }
1001                 i++;
1002             }
1003         } else {
1004             return false;
1005         }
1006         if (i == activeTraffic.end() || (trafficSize == 0)) {
1007             //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
1008             // The target id is not found on the current network, which means it's at the tower
1009             //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
1010             return false;
1011         }
1012         other = i;
1013         target = other->getWaitsForId();
1014
1015         // actually this trap isn't as impossible as it first seemed:
1016         // the setWaitsForID(id) is set to current when the aircraft
1017         // is waiting for the user controlled aircraft. 
1018         //if (current->getId() == other->getId()) {
1019         //    cerr << "Caught the impossible trap" << endl;
1020         //    cerr << "Current = " << current->getId() << endl;
1021         //    cerr << "Other   = " << other  ->getId() << endl;
1022         //    for (TrafficVectorIterator at = activeTraffic.begin();
1023         //          at != activeTraffic.end();
1024         //          at++) {
1025         //        cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
1026         //    }
1027         //    exit(1);
1028         if (current->getId() == other->getId())
1029             return false;
1030         //}
1031         //cerr << current->getCallSign() << " (" << current->getId()  << ") " << " -> " << other->getCallSign() 
1032         //     << " (" << other->getId()  << "); " << endl;;
1033         //current = other;
1034     }
1035
1036
1037
1038
1039
1040
1041     //if (printed)
1042     //   cerr << "[done] " << endl << endl;;
1043     if (id == target) {
1044         SG_LOG(SG_GENERAL, SG_WARN,
1045                "Detected circular wait condition: Id = " << id <<
1046                "target = " << target);
1047         return true;
1048     } else {
1049         return false;
1050     }
1051 }
1052
1053 // Note that this function is probably obsolete...
1054 bool FGGroundNetwork::hasInstruction(int id)
1055 {
1056     TrafficVectorIterator i = activeTraffic.begin();
1057     // Search search if the current id has an entry
1058     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1059     if (activeTraffic.size()) {
1060         //while ((i->getId() != id) && i != activeTraffic.end()) {
1061         while (i != activeTraffic.end()) {
1062             if (i->getId() == id) {
1063                 break;
1064             }
1065             i++;
1066         }
1067     }
1068     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1069         SG_LOG(SG_GENERAL, SG_ALERT,
1070                "AI error: checking ATC instruction for aircraft without traffic record");
1071     } else {
1072         return i->hasInstruction();
1073     }
1074     return false;
1075 }
1076
1077 FGATCInstruction FGGroundNetwork::getInstruction(int id)
1078 {
1079     TrafficVectorIterator i = activeTraffic.begin();
1080     // Search search if the current id has an entry
1081     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1082     if (activeTraffic.size()) {
1083         //while ((i->getId() != id) && i != activeTraffic.end()) {
1084         while (i != activeTraffic.end()) {
1085             if (i->getId() == id) {
1086                 break;
1087             }
1088             i++;
1089         }
1090     }
1091     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1092         SG_LOG(SG_GENERAL, SG_ALERT,
1093                "AI error: requesting ATC instruction for aircraft without traffic record");
1094     } else {
1095         return i->getInstruction();
1096     }
1097     return FGATCInstruction();
1098 }
1099
1100 // Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
1101 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
1102                             double lon, double elev, double hdg)
1103 {
1104     SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
1105     obj_pos = geod.makeZUpFrame();
1106     // hdg is not a compass heading, but a counter-clockwise rotation
1107     // around the Z axis
1108     obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
1109                                         0.0, 0.0, 1.0));
1110 }
1111
1112
1113
1114
1115 void FGGroundNetwork::render(bool visible)
1116 {
1117
1118     SGMaterialLib *matlib = globals->get_matlib();
1119     if (group) {
1120         //int nr = ;
1121         globals->get_scenery()->get_scene_graph()->removeChild(group);
1122         //while (group->getNumChildren()) {
1123         //  cerr << "Number of children: " << group->getNumChildren() << endl;
1124         simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
1125           //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
1126            //geode->releaseGLObjects();
1127            //group->removeChild(geode);
1128            //delete geode;
1129         group = 0;
1130     }
1131     if (visible) {
1132         group = new osg::Group;
1133
1134         //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
1135         double dx = 0;
1136         for   (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1137             // Handle start point
1138             int pos = i->getCurrentPosition() - 1;
1139             if (pos >= 0) {
1140             
1141                 SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1142                 SGGeod end  (SGGeod::fromDeg(segments[pos]->getEnd()->getLongitude(), segments[pos]->getEnd()->getLatitude()));
1143
1144                 double length = SGGeodesy::distanceM(start, end);
1145                 //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
1146
1147                 double az2, heading; //, distanceM;
1148                 SGGeodesy::inverse(start, end, heading, az2, length);
1149                 double coveredDistance = length * 0.5;
1150                 SGGeod center;
1151                 SGGeodesy::direct(start, heading, coveredDistance, center, az2);
1152                 //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
1153             ///////////////////////////////////////////////////////////////////////////////
1154                 // Make a helper function out of this
1155                 osg::Matrix obj_pos;
1156                 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1157                 obj_trans->setDataVariance(osg::Object::STATIC);
1158
1159                 WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), parent->elevation()+8+dx, -(heading) );
1160
1161                 obj_trans->setMatrix( obj_pos );
1162                 //osg::Vec3 center(0, 0, 0)
1163
1164                 float width = length /2.0;
1165                 osg::Vec3 corner(-width, 0, 0.25f);
1166                 osg::Vec3 widthVec(2*width + 1, 0, 0);
1167                 osg::Vec3 heightVec(0, 1, 0);
1168                 osg::Geometry* geometry;
1169                 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1170                 simgear::EffectGeode* geode = new simgear::EffectGeode;
1171                 geode->setName("test");
1172                 geode->addDrawable(geometry);
1173                 //osg::Node *custom_obj;
1174                 SGMaterial *mat = matlib->find("UnidirectionalTaper");
1175                 if (mat)
1176                     geode->setEffect(mat->get_effect());
1177                 obj_trans->addChild(geode);
1178                 // wire as much of the scene graph together as we can
1179                 //->addChild( obj_trans );
1180                 group->addChild( obj_trans );
1181             /////////////////////////////////////////////////////////////////////
1182             } else {
1183                 //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
1184             }
1185             for(intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
1186                 osg::Matrix obj_pos;
1187                 int k = (*j)-1;
1188                 if (k >= 0) {
1189                     osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1190                     obj_trans->setDataVariance(osg::Object::STATIC);
1191
1192                     WorldCoordinate( obj_pos, segments[k]->getLatitude(), segments[k]->getLongitude(), parent->elevation()+8+dx, -(segments[k]->getHeading()) );
1193
1194                     obj_trans->setMatrix( obj_pos );
1195                     //osg::Vec3 center(0, 0, 0)
1196
1197                     float width = segments[k]->getLength() /2.0;
1198                     osg::Vec3 corner(-width, 0, 0.25f);
1199                     osg::Vec3 widthVec(2*width + 1, 0, 0);
1200                     osg::Vec3 heightVec(0, 1, 0);
1201                     osg::Geometry* geometry;
1202                     geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1203                     simgear::EffectGeode* geode = new simgear::EffectGeode;
1204                     geode->setName("test");
1205                     geode->addDrawable(geometry);
1206                     //osg::Node *custom_obj;
1207                     SGMaterial *mat = matlib->find("UnidirectionalTaper");
1208                     if (mat)
1209                         geode->setEffect(mat->get_effect());
1210                     obj_trans->addChild(geode);
1211                     // wire as much of the scene graph together as we can
1212                     //->addChild( obj_trans );
1213                     group->addChild( obj_trans );
1214                 }
1215             }
1216             //dx += 0.1;
1217         }
1218         globals->get_scenery()->get_scene_graph()->addChild(group);
1219     }
1220 }
1221
1222 string FGGroundNetwork::getName() {
1223     return string(parent->getId() + "-ground");
1224 }