]> git.mxchange.org Git - flightgear.git/blob - src/ATC/trafficcontrol.cxx
Merge branch 'next' of gitorious.org:fg/flightgear into next
[flightgear.git] / src / ATC / trafficcontrol.cxx
1 // trafficrecord.cxx - Implementation of AIModels ATC code.
2 //
3 // Written by Durk Talsma, started September 2006.
4 //
5 // Copyright (C) 2006 Durk Talsma.
6 //
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.
11 //
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.
16 //
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.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #include <algorithm>
28
29 #include <osg/Geode>
30 #include <osg/Geometry>
31 #include <osg/MatrixTransform>
32 #include <osg/Shape>
33
34 #include <simgear/scene/material/EffectGeode.hxx>
35 #include <simgear/scene/material/matlib.hxx>
36 #include <simgear/scene/material/mat.hxx>
37 #include <Scenery/scenery.hxx>
38
39 #include "trafficcontrol.hxx"
40 #include "atc_mgr.hxx"
41 #include <AIModel/AIAircraft.hxx>
42 #include <AIModel/AIFlightPlan.hxx>
43 #include <AIModel/performancedata.hxx>
44 #include <AIModel/performancedb.hxx>
45 #include <ATC/atc_mgr.hxx>
46 #include <Traffic/TrafficMgr.hxx>
47 #include <Airports/groundnetwork.hxx>
48 #include <Airports/dynamics.hxx>
49 #include <Airports/simple.hxx>
50
51 using std::sort;
52
53 /***************************************************************************
54  * ActiveRunway
55  **************************************************************************/
56 time_t ActiveRunway::requestTimeSlot(time_t eta)
57 {
58     time_t newEta;
59     time_t separation = 90;
60     bool found = false;
61     if (estimatedArrivalTimes.size() == 0) {
62         estimatedArrivalTimes.push_back(eta);
63         return eta;
64     } else {
65         TimeVectorIterator i = estimatedArrivalTimes.begin();
66         //cerr << "Checking eta slots " << eta << ": " << endl;
67         for (i = estimatedArrivalTimes.begin();
68                 i != estimatedArrivalTimes.end(); i++) {
69             //cerr << "Stored time : " << (*i) << endl;
70         }
71         i = estimatedArrivalTimes.begin();
72         if ((eta + separation) < (*i)) {
73             newEta = eta;
74             found = true;
75             //cerr << "Storing at beginning" << endl;
76         }
77         while ((i != estimatedArrivalTimes.end()) && (!found)) {
78             TimeVectorIterator j = i + 1;
79             if (j == estimatedArrivalTimes.end()) {
80                 if (((*i) + separation) < eta) {
81                     //cerr << "Storing at end" << endl;
82                     newEta = eta;
83                 } else {
84                     newEta = (*i) + separation;
85                     //cerr << "Storing at end + separation" << endl;
86                 }
87             } else {
88                 if ((((*j) - (*i)) > (separation * 2))) {       // found a potential slot
89                     // now check whether this slot is usable:
90                     // 1) eta should fall between the two points
91                     //    i.e. eta > i AND eta < j
92                     //
93                     //cerr << "Found potential slot after " << (*i) << endl;
94                     if (eta > (*i) && (eta < (*j))) {
95                         found = true;
96                         if (eta < ((*i) + separation)) {
97                             newEta = (*i) + separation;
98                             //cerr << "Using  original" << (*i) << " + separation " << endl;
99                         } else {
100                             newEta = eta;
101                             //cerr << "Using original after " << (*i) << endl;
102                         }
103                     } else if (eta < (*i)) {
104                         found = true;
105                         newEta = (*i) + separation;
106                         //cerr << "Using delayed slot after " << (*i) << endl;
107                     }
108                     /*
109                        if (((*j) - separation) < eta) {
110                        found = true;
111                        if (((*i) + separation) < eta) {
112                        newEta = eta;
113                        cerr << "Using original after " << (*i) << endl;
114                        } else {
115                        newEta = (*i) + separation;
116                        cerr << "Using  " << (*i) << " + separation " << endl;
117                        }
118                        } */
119                 }
120             }
121             i++;
122         }
123     }
124     //cerr << ". done. New ETA : " << newEta << endl;
125
126     estimatedArrivalTimes.push_back(newEta);
127     sort(estimatedArrivalTimes.begin(), estimatedArrivalTimes.end());
128     // do some housekeeping : remove any timestamps that are past
129     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
130     TimeVectorIterator i = estimatedArrivalTimes.begin();
131     while (i != estimatedArrivalTimes.end()) {
132         if ((*i) < now) {
133             //cerr << "Deleting timestamp " << (*i) << " (now = " << now << "). " << endl;
134             estimatedArrivalTimes.erase(i);
135             i = estimatedArrivalTimes.begin();
136         } else {
137             i++;
138         }
139     }
140     return newEta;
141 }
142
143 /***************************************************************************
144  * FGTrafficRecord
145  **************************************************************************/
146 FGTrafficRecord::FGTrafficRecord():
147         id(0), waitsForId(0),
148         currentPos(0),
149         leg(0),
150         frequencyId(0),
151         state(0),
152         allowTransmission(true),
153         allowPushback(true),
154         priority(0),
155         latitude(0), longitude(0), heading(0), speed(0), altitude(0), radius(0)
156 {
157 }
158
159 void FGTrafficRecord::setPositionAndIntentions(int pos,
160         FGAIFlightPlan * route)
161 {
162
163     currentPos = pos;
164     if (intentions.size()) {
165         intVecIterator i = intentions.begin();
166         if ((*i) != pos) {
167             SG_LOG(SG_GENERAL, SG_ALERT,
168                    "Error in FGTrafficRecord::setPositionAndIntentions");
169             //cerr << "Pos : " << pos << " Curr " << *(intentions.begin())  << endl;
170             for (intVecIterator i = intentions.begin();
171                     i != intentions.end(); i++) {
172                 //cerr << (*i) << " ";
173             }
174             //cerr << endl;
175         }
176         intentions.erase(i);
177     } else {
178         //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
179         int size = route->getNrOfWayPoints();
180         //cerr << "Setting pos" << pos << " ";
181         //cerr << "setting intentions ";
182         for (int i = 0; i < size; i++) {
183             int val = route->getRouteIndex(i);
184             //cerr << val<< " ";
185             if ((val) && (val != pos)) {
186                 intentions.push_back(val);
187                 //cerr << "[set] ";
188             }
189         }
190         //cerr << endl;
191         //while (route->next(&legNr, &routeNr)) {
192         //intentions.push_back(routeNr);
193         //}
194         //route->rewind(currentPos);
195     }
196     //exit(1);
197 }
198 /**
199  * Check if another aircraft is ahead of the current one, and on the same
200  * return true / false is the is/isn't the case.
201  *
202  ****************************************************************************/
203
204 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other)
205 {
206     bool result = false;
207     //cerr << "Start check 1" << endl;
208     if (currentPos == other.currentPos) {
209         //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
210         result = true;
211     }
212     //  else if (other.intentions.size())
213     //     {
214     //       cerr << "Start check 2" << endl;
215     //       intVecIterator i = other.intentions.begin();
216     //       while (!((i == other.intentions.end()) || ((*i) == currentPos)))
217     //     i++;
218     //       if (i != other.intentions.end()) {
219     //     cerr << "Check Position and intentions: current matches other.intentions" << endl;
220     //     result = true;
221     //       }
222     else if (intentions.size()) {
223         //cerr << "Start check 3" << endl;
224         intVecIterator i = intentions.begin();
225         //while (!((i == intentions.end()) || ((*i) == other.currentPos)))
226         while (i != intentions.end()) {
227             if ((*i) == other.currentPos) {
228                 break;
229             }
230             i++;
231         }
232         if (i != intentions.end()) {
233             //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
234             result = true;
235         }
236     }
237     //cerr << "Done !!" << endl;
238     return result;
239 }
240
241 void FGTrafficRecord::setPositionAndHeading(double lat, double lon,
242         double hdg, double spd,
243         double alt)
244 {
245     latitude = lat;
246     longitude = lon;
247     heading = hdg;
248     speed = spd;
249     altitude = alt;
250 }
251
252 int FGTrafficRecord::crosses(FGGroundNetwork * net,
253                              FGTrafficRecord & other)
254 {
255     if (checkPositionAndIntentions(other)
256             || (other.checkPositionAndIntentions(*this)))
257         return -1;
258     intVecIterator i, j;
259     int currentTargetNode = 0, otherTargetNode = 0;
260     if (currentPos > 0)
261         currentTargetNode = net->findSegment(currentPos)->getEnd()->getIndex(); // OKAY,...
262     if (other.currentPos > 0)
263         otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex();     // OKAY,...
264     if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
265         return currentTargetNode;
266     if (intentions.size()) {
267         for (i = intentions.begin(); i != intentions.end(); i++) {
268             if ((*i) > 0) {
269                 if ((currentTargetNode ==
270                         net->findSegment(*i)->getEnd()->getIndex())) {
271                     //cerr << "Current crosses at " << currentTargetNode <<endl;
272                     return currentTargetNode;
273                 }
274             }
275         }
276     }
277     if (other.intentions.size()) {
278         for (i = other.intentions.begin(); i != other.intentions.end();
279                 i++) {
280             if ((*i) > 0) {
281                 if (otherTargetNode ==
282                         net->findSegment(*i)->getEnd()->getIndex()) {
283                     //cerr << "Other crosses at " << currentTargetNode <<endl;
284                     return otherTargetNode;
285                 }
286             }
287         }
288     }
289     if (intentions.size() && other.intentions.size()) {
290         for (i = intentions.begin(); i != intentions.end(); i++) {
291             for (j = other.intentions.begin(); j != other.intentions.end();
292                     j++) {
293                 //cerr << "finding segment " << *i << " and " << *j << endl;
294                 if (((*i) > 0) && ((*j) > 0)) {
295                     currentTargetNode =
296                         net->findSegment(*i)->getEnd()->getIndex();
297                     otherTargetNode =
298                         net->findSegment(*j)->getEnd()->getIndex();
299                     if (currentTargetNode == otherTargetNode) {
300                         //cerr << "Routes will cross at " << currentTargetNode << endl;
301                         return currentTargetNode;
302                     }
303                 }
304             }
305         }
306     }
307     return -1;
308 }
309
310 bool FGTrafficRecord::onRoute(FGGroundNetwork * net,
311                               FGTrafficRecord & other)
312 {
313     int node = -1, othernode = -1;
314     if (currentPos > 0)
315         node = net->findSegment(currentPos)->getEnd()->getIndex();
316     if (other.currentPos > 0)
317         othernode =
318             net->findSegment(other.currentPos)->getEnd()->getIndex();
319     if ((node == othernode) && (node != -1))
320         return true;
321     if (other.intentions.size()) {
322         for (intVecIterator i = other.intentions.begin();
323                 i != other.intentions.end(); i++) {
324             if (*i > 0) {
325                 othernode = net->findSegment(*i)->getEnd()->getIndex();
326                 if ((node == othernode) && (node > -1))
327                     return true;
328             }
329         }
330     }
331     //if (other.currentPos > 0)
332     //  othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
333     //if (intentions.size())
334     //  {
335     //    for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
336     //    {
337     //      if (*i > 0)
338     //        {
339     //          node = net->findSegment(*i)->getEnd()->getIndex();
340     //          if ((node == othernode) && (node > -1))
341     //            return true;
342     //        }
343     //    }
344     //  }
345     return false;
346 }
347
348
349 bool FGTrafficRecord::isOpposing(FGGroundNetwork * net,
350                                  FGTrafficRecord & other, int node)
351 {
352     // Check if current segment is the reverse segment for the other aircraft
353     FGTaxiSegment *opp;
354     //cerr << "Current segment " << currentPos << endl;
355     if ((currentPos > 0) && (other.currentPos > 0)) {
356         opp = net->findSegment(currentPos)->opposite();
357         if (opp) {
358             if (opp->getIndex() == other.currentPos)
359                 return true;
360         }
361
362         for (intVecIterator i = intentions.begin(); i != intentions.end();
363                 i++) {
364             if ((opp = net->findSegment(other.currentPos)->opposite())) {
365                 if ((*i) > 0)
366                     if (opp->getIndex() ==
367                             net->findSegment(*i)->getIndex()) {
368                         if (net->findSegment(*i)->getStart()->getIndex() ==
369                                 node) {
370                             {
371                                 //cerr << "Found the node " << node << endl;
372                                 return true;
373                             }
374                         }
375                     }
376             }
377             if (other.intentions.size()) {
378                 for (intVecIterator j = other.intentions.begin();
379                         j != other.intentions.end(); j++) {
380                     // cerr << "Current segment 1 " << (*i) << endl;
381                     if ((*i) > 0) {
382                         if ((opp = net->findSegment(*i)->opposite())) {
383                             if (opp->getIndex() ==
384                                     net->findSegment(*j)->getIndex()) {
385                                 //cerr << "Nodes " << net->findSegment(*i)->getIndex()
386                                 //   << " and  " << net->findSegment(*j)->getIndex()
387                                 //   << " are opposites " << endl;
388                                 if (net->findSegment(*i)->getStart()->
389                                         getIndex() == node) {
390                                     {
391                                         //cerr << "Found the node " << node << endl;
392                                         return true;
393                                     }
394                                 }
395                             }
396                         }
397                     }
398                 }
399             }
400         }
401     }
402     return false;
403 }
404
405 bool FGTrafficRecord::isActive(int margin)
406 {
407     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
408     time_t deptime = aircraft->getTrafficRef()->getDepartureTime();
409     return ((now + margin) > deptime);
410 }
411
412
413 void FGTrafficRecord::setSpeedAdjustment(double spd)
414 {
415     instruction.setChangeSpeed(true);
416     instruction.setSpeed(spd);
417 }
418
419 void FGTrafficRecord::setHeadingAdjustment(double heading)
420 {
421     instruction.setChangeHeading(true);
422     instruction.setHeading(heading);
423 }
424
425 bool FGTrafficRecord::pushBackAllowed()
426 {
427     return allowPushback;
428 }
429
430
431
432
433 /***************************************************************************
434  * FGATCInstruction
435  *
436  **************************************************************************/
437 FGATCInstruction::FGATCInstruction()
438 {
439     holdPattern = false;
440     holdPosition = false;
441     changeSpeed = false;
442     changeHeading = false;
443     changeAltitude = false;
444     resolveCircularWait = false;
445
446     speed = 0;
447     heading = 0;
448     alt = 0;
449 }
450
451
452 bool FGATCInstruction::hasInstruction()
453 {
454     return (holdPattern || holdPosition || changeSpeed || changeHeading
455             || changeAltitude || resolveCircularWait);
456 }
457
458 /***************************************************************************
459  * FGATCController
460  *
461  **************************************************************************/
462
463
464
465
466 FGATCController::FGATCController()
467 {
468     //cerr << "running FGATController constructor" << endl;
469     dt_count = 0;
470     available = true;
471     lastTransmission = 0;
472     initialized = false;
473 }
474
475 FGATCController::~FGATCController()
476 {
477     //cerr << "running FGATController destructor" << endl;
478 }
479
480 string FGATCController::getGateName(FGAIAircraft * ref)
481 {
482     return ref->atGate();
483 }
484
485 bool FGATCController::isUserAircraft(FGAIAircraft* ac)
486 {
487     return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false;
488 };
489
490 void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId,
491                                AtcMsgDir msgDir, bool audible)
492 {
493     string sender, receiver;
494     int stationFreq = 0;
495     int taxiFreq = 0;
496     int towerFreq = 0;
497     int freqId = 0;
498     string atisInformation;
499     string text;
500     string taxiFreqStr;
501     string towerFreqStr;
502     double heading = 0;
503     string activeRunway;
504     string fltType;
505     string rwyClass;
506     string SID;
507     string transponderCode;
508     FGAIFlightPlan *fp;
509     string fltRules;
510     string instructionText;
511
512     //double commFreqD;
513     sender = rec->getAircraft()->getTrafficRef()->getCallSign();
514     if (rec->getAircraft()->getTaxiClearanceRequest()) {
515         instructionText = "push-back and taxi";
516     } else {
517         instructionText = "taxi";
518     }
519     //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
520     switch (rec->getLeg()) {
521     case 1:
522     case 2:
523         freqId = rec->getNextFrequency();
524         stationFreq =
525             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
526             getDynamics()->getGroundFrequency(rec->getLeg() + freqId);
527         taxiFreq =
528             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
529             getDynamics()->getGroundFrequency(2);
530         towerFreq =
531             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
532             getDynamics()->getTowerFrequency(2);
533         receiver =
534             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
535             getName() + "-Ground";
536         atisInformation =
537             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
538             getDynamics()->getAtisSequence();
539         break;
540     case 3:
541         receiver =
542             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
543             getName() + "-Tower";
544         break;
545     }
546     // Swap sender and receiver value in case of a ground to air transmission
547     if (msgDir == ATC_GROUND_TO_AIR) {
548         string tmp = sender;
549         sender = receiver;
550         receiver = tmp;
551     }
552     switch (msgId) {
553     case MSG_ANNOUNCE_ENGINE_START:
554         text = sender + ". Ready to Start up";
555         break;
556     case MSG_REQUEST_ENGINE_START:
557         text =
558             receiver + ", This is " + sender + ". Position " +
559             getGateName(rec->getAircraft()) + ". Information " +
560             atisInformation + ". " +
561             rec->getAircraft()->getTrafficRef()->getFlightRules() +
562             " to " +
563             rec->getAircraft()->getTrafficRef()->getArrivalAirport()->
564             getName() + ". Request start-up";
565         break;
566         // Acknowledge engine startup permission
567         // Assign departure runway
568         // Assign SID, if necessery (TODO)
569     case MSG_PERMIT_ENGINE_START:
570         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
571
572         heading = rec->getAircraft()->getTrafficRef()->getCourse();
573         fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
574         rwyClass =
575             rec->getAircraft()->GetFlightPlan()->
576             getRunwayClassFromTrafficType(fltType);
577
578         rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
579         getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
580                                        heading);
581         rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
582         fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
583              getDynamics()->getSID(activeRunway, heading);
584         rec->getAircraft()->GetFlightPlan()->setSID(fp);
585         if (fp) {
586             SID = fp->getName() + " departure";
587         } else {
588             SID = "fly runway heading ";
589         }
590         //snprintf(buffer, 7, "%3.2f", heading);
591         fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
592         transponderCode = genTransponderCode(fltRules);
593         rec->getAircraft()->SetTransponderCode(transponderCode);
594         text =
595             receiver + ". Start-up approved. " + atisInformation +
596             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
597             transponderCode + ". " +
598             "For "+ instructionText + " clearance call " + taxiFreqStr + ". " +
599             sender + " control.";
600         break;
601     case MSG_DENY_ENGINE_START:
602         text = receiver + ". Standby";
603         break;
604     case MSG_ACKNOWLEDGE_ENGINE_START:
605         fp = rec->getAircraft()->GetFlightPlan()->getSID();
606         if (fp) {
607             SID =
608                 rec->getAircraft()->GetFlightPlan()->getSID()->getName() +
609                 " departure";
610         } else {
611             SID = "fly runway heading ";
612         }
613         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
614         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
615         transponderCode = rec->getAircraft()->GetTransponderCode();
616
617         text =
618             receiver + ". Start-up approved. " + atisInformation +
619             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
620             transponderCode + ". " +
621             "For " + instructionText + " clearance call " + taxiFreqStr + ". " +
622             sender;
623         break;
624     case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
625         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
626         text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
627         break;
628     case MSG_INITIATE_CONTACT:
629         text = receiver + ". With you. " + sender;
630         break;
631     case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
632         text = receiver + ". Roger. " + sender;
633         break;
634     case MSG_REQUEST_PUSHBACK_CLEARANCE:
635         if (rec->getAircraft()->getTaxiClearanceRequest()) {
636             text = receiver + ". Request push-back. " + sender;
637         } else {
638             text = receiver + ". Request Taxi clearance. " + sender;
639         }
640         break;
641     case MSG_PERMIT_PUSHBACK_CLEARANCE:
642         if (rec->getAircraft()->getTaxiClearanceRequest()) {
643             text = receiver + ". Push-back approved. " + sender;
644         } else {
645             text = receiver + ". Cleared to Taxi." + sender;
646         }
647         break;
648     case MSG_HOLD_PUSHBACK_CLEARANCE:
649         text = receiver + ". Standby. " + sender;
650         break;
651     case MSG_REQUEST_TAXI_CLEARANCE:
652         text = receiver + ". Ready to Taxi. " + sender;
653         break;
654     case MSG_ISSUE_TAXI_CLEARANCE:
655         text = receiver + ". Cleared to taxi. " + sender;
656         break;
657     case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
658         text = receiver + ". Cleared to taxi. " + sender;
659         break;
660     case MSG_HOLD_POSITION:
661         text = receiver + ". Hold Position. " + sender;
662         break;
663     case MSG_ACKNOWLEDGE_HOLD_POSITION:
664         text = receiver + ". Holding Position. " + sender;
665         break;
666     case MSG_RESUME_TAXI:
667         text = receiver + ". Resume Taxiing. " + sender;
668         break;
669     case MSG_ACKNOWLEDGE_RESUME_TAXI:
670         text = receiver + ". Continuing Taxi. " + sender;
671         break;
672     case MSG_REPORT_RUNWAY_HOLD_SHORT:
673         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
674         //activeRunway = "test";
675         text = receiver + ". Holding short runway "
676                + activeRunway
677                + ". " + sender;
678         //text = "test1";
679         //cerr << "1 Currently at leg " << rec->getLeg() << endl;
680         break;
681     case MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT:
682         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
683         text = receiver + "Roger. Holding short runway "
684                //                + activeRunway
685                + ". " + sender;
686         //text = "test2";
687         //cerr << "2 Currently at leg " << rec->getLeg() << endl;
688         break;
689     case MSG_SWITCH_TOWER_FREQUENCY:
690         towerFreqStr = formatATCFrequency3_2(towerFreq);
691         text = receiver + "Contact Tower at " + towerFreqStr + ". " + sender;
692         //text = "test3";
693         //cerr << "3 Currently at leg " << rec->getLeg() << endl;
694         break;
695     case MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY:
696         towerFreqStr = formatATCFrequency3_2(towerFreq);
697         text = receiver + "Roger, switching to tower at " + towerFreqStr + ". " + sender;
698         //text = "test4";
699         //cerr << "4 Currently at leg " << rec->getLeg() << endl;
700         break;
701     default:
702         //text = "test3";
703         text = text + sender + ". Transmitting unknown Message";
704         break;
705     }
706     if (audible) {
707         double onBoardRadioFreq0 =
708             fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
709         double onBoardRadioFreq1 =
710             fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
711         int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
712         int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
713         //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl;
714
715         // Display ATC message only when one of the radios is tuned
716         // the relevant frequency.
717         // Note that distance attenuation is currently not yet implemented
718         if ((onBoardRadioFreqI0 == stationFreq)
719                 || (onBoardRadioFreqI1 == stationFreq)) {
720             if (rec->allowTransmissions()) {
721                 fgSetString("/sim/messages/atc", text.c_str());
722             }
723         }
724     } else {
725         FGATCDialogNew::instance()->addEntry(1, text);
726     }
727 }
728
729 string FGATCController::formatATCFrequency3_2(int freq)
730 {
731     char buffer[7];
732     snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
733     return string(buffer);
734 }
735
736 // TODO: Set transponder codes according to real-world routes.
737 // The current version just returns a random string of four octal numbers.
738 string FGATCController::genTransponderCode(string fltRules)
739 {
740     if (fltRules == "VFR") {
741         return string("1200");
742     } else {
743         char buffer[5];
744         snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
745                  rand() % 8);
746         return string(buffer);
747     }
748 }
749
750 void FGATCController::init()
751 {
752     if (!initialized) {
753         FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
754         mgr->addController(this);
755         initialized = true;
756     }
757 }
758
759 /***************************************************************************
760  * class FGTowerController
761  *
762  **************************************************************************/
763 FGTowerController::FGTowerController(FGAirportDynamics *par) :
764         FGATCController()
765 {
766     parent = par;
767 }
768
769 //
770 void FGTowerController::announcePosition(int id,
771         FGAIFlightPlan * intendedRoute,
772         int currentPosition, double lat,
773         double lon, double heading,
774         double speed, double alt,
775         double radius, int leg,
776         FGAIAircraft * ref)
777 {
778     init();
779     TrafficVectorIterator i = activeTraffic.begin();
780     // Search whether the current id alread has an entry
781     // This might be faster using a map instead of a vector, but let's start by taking a safe route
782     if (activeTraffic.size()) {
783         //while ((i->getId() != id) && i != activeTraffic.end()) {
784         while (i != activeTraffic.end()) {
785             if (i->getId() == id) {
786                 break;
787             }
788             i++;
789         }
790     }
791     // Add a new TrafficRecord if no one exsists for this aircraft.
792     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
793         FGTrafficRecord rec;
794         rec.setId(id);
795
796         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
797         rec.setRunway(intendedRoute->getRunway());
798         rec.setLeg(leg);
799         //rec.setCallSign(callsign);
800         rec.setRadius(radius);
801         rec.setAircraft(ref);
802         activeTraffic.push_back(rec);
803         // Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway.
804         ActiveRunwayVecIterator rwy = activeRunways.begin();
805         if (activeRunways.size()) {
806             while (rwy != activeRunways.end()) {
807                 if (rwy->getRunwayName() == intendedRoute->getRunway()) {
808                     break;
809                 }
810                 rwy++;
811             }
812         }
813         if (rwy == activeRunways.end()) {
814             ActiveRunway aRwy(intendedRoute->getRunway(), id);
815             aRwy.addToDepartureCue(ref);
816             activeRunways.push_back(aRwy);
817             rwy = (activeRunways.end()-1);
818         } else {
819             rwy->addToDepartureCue(ref);
820         }
821
822         //cerr << ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getDepartureCueSize() <<  " for takeoff " << endl;
823     } else {
824         i->setPositionAndHeading(lat, lon, heading, speed, alt);
825     }
826 }
827
828 void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
829         double heading, double speed, double alt,
830         double dt)
831 {
832     TrafficVectorIterator i = activeTraffic.begin();
833     // Search whether the current id has an entry
834     // This might be faster using a map instead of a vector, but let's start by taking a safe route
835     TrafficVectorIterator current, closest;
836     if (activeTraffic.size()) {
837         //while ((i->getId() != id) && i != activeTraffic.end()) {
838         while (i != activeTraffic.end()) {
839             if (i->getId() == id) {
840                 break;
841             }
842             i++;
843         }
844     }
845 //    // update position of the current aircraft
846     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
847         SG_LOG(SG_GENERAL, SG_ALERT,
848                "AI error: updating aircraft without traffic record");
849     } else {
850         i->setPositionAndHeading(lat, lon, heading, speed, alt);
851         current = i;
852     }
853     setDt(getDt() + dt);
854
855     // see if we already have a clearance record for the currently active runway
856     // NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is
857     // already exists here. So, we can simplify the current code.
858     ActiveRunwayVecIterator rwy = activeRunways.begin();
859     while (rwy != activeRunways.end()) {
860         if (rwy->getRunwayName() == current->getRunway()) {
861             break;
862         }
863         rwy++;
864     }
865
866     // only bother running the following code if the current aircraft is the
867     // first in line for depature
868     /* if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
869         if (rwy->getCleared()) {
870             if (id == rwy->getCleared()) {
871                 current->setHoldPosition(false);
872             } else {
873                 current->setHoldPosition(true);
874             }
875         } else {
876             // For now. At later stages, this will probably be the place to check for inbound traffc.
877             rwy->setCleared(id);
878         }
879     } */
880     // only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control
881     if (current->getAircraft()->getTakeOffStatus() == 2) {
882         current->setHoldPosition(true);
883         int clearanceId = rwy->getCleared();
884         if (clearanceId) {
885             if (id == clearanceId) {
886                 current->setHoldPosition(false);
887             }
888         } else {
889             if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
890                 rwy->setCleared(id);
891             }
892         }
893     } else {
894         
895     }
896 }
897
898
899 void FGTowerController::signOff(int id)
900 {
901     TrafficVectorIterator i = activeTraffic.begin();
902     // Search search if the current id alread has an entry
903     // This might be faster using a map instead of a vector, but let's start by taking a safe route
904     if (activeTraffic.size()) {
905         //while ((i->getId() != id) && i != activeTraffic.end()) {
906         while (i != activeTraffic.end()) {
907             if (i->getId() == id) {
908                 break;
909             }
910             i++;
911         }
912     }
913     // If this aircraft has left the runway, we can clear the departure record for this runway
914     ActiveRunwayVecIterator rwy = activeRunways.begin();
915     if (activeRunways.size()) {
916         //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
917         while (rwy != activeRunways.end()) {
918             if (rwy->getRunwayName() == i->getRunway()) {
919                 break;
920             }
921             rwy++;
922         }
923         if (rwy != activeRunways.end()) {
924             rwy->setCleared(0);
925             rwy->updateDepartureCue();
926         } else {
927             SG_LOG(SG_GENERAL, SG_ALERT,
928                    "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
929         }
930     }
931     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
932         SG_LOG(SG_GENERAL, SG_ALERT,
933                "AI error: Aircraft without traffic record is signing off from tower");
934     } else {
935         i->getAircraft()->resetTakeOffStatus();
936         i = activeTraffic.erase(i);
937         //cerr << "Signing off from tower controller" << endl;
938     }
939 }
940
941 // NOTE:
942 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
943 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
944 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
945 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
946 // Note that this function is probably obsolete
947 bool FGTowerController::hasInstruction(int id)
948 {
949     TrafficVectorIterator i = activeTraffic.begin();
950     // Search search if the current id has an entry
951     // This might be faster using a map instead of a vector, but let's start by taking a safe route
952     if (activeTraffic.size()) {
953         //while ((i->getId() != id) && i != activeTraffic.end()) {
954         while (i != activeTraffic.end()) {
955             if (i->getId() == id) {
956                 break;
957             }
958             i++;
959         }
960     }
961     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
962         SG_LOG(SG_GENERAL, SG_ALERT,
963                "AI error: checking ATC instruction for aircraft without traffic record");
964     } else {
965         return i->hasInstruction();
966     }
967     return false;
968 }
969
970
971 FGATCInstruction FGTowerController::getInstruction(int id)
972 {
973     TrafficVectorIterator i = activeTraffic.begin();
974     // Search search if the current id has an entry
975     // This might be faster using a map instead of a vector, but let's start by taking a safe route
976     if (activeTraffic.size()) {
977         //while ((i->getId() != id) && i != activeTraffic.end()) {
978         while (i != activeTraffic.end()) {
979             if (i->getId() == id) {
980                 break;
981             }
982             i++;
983         }
984     }
985     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
986         SG_LOG(SG_GENERAL, SG_ALERT,
987                "AI error: requesting ATC instruction for aircraft without traffic record");
988     } else {
989         return i->getInstruction();
990     }
991     return FGATCInstruction();
992 }
993
994 void FGTowerController::render(bool visible) {
995     //cerr << "FGTowerController::render function not yet implemented" << endl;
996 }
997
998 string FGTowerController::getName() {
999     return string(parent->getId() + "-tower");
1000 }
1001
1002 void FGTowerController::update(double dt)
1003 {
1004
1005 }
1006
1007
1008
1009 /***************************************************************************
1010  * class FGStartupController
1011  *
1012  **************************************************************************/
1013 FGStartupController::FGStartupController(FGAirportDynamics *par):
1014         FGATCController()
1015 {
1016     parent = par;
1017 }
1018
1019 void FGStartupController::announcePosition(int id,
1020         FGAIFlightPlan * intendedRoute,
1021         int currentPosition, double lat,
1022         double lon, double heading,
1023         double speed, double alt,
1024         double radius, int leg,
1025         FGAIAircraft * ref)
1026 {
1027     init();
1028     TrafficVectorIterator i = activeTraffic.begin();
1029     // Search whether the current id alread has an entry
1030     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1031     if (activeTraffic.size()) {
1032         //while ((i->getId() != id) && i != activeTraffic.end()) {
1033         while (i != activeTraffic.end()) {
1034             if (i->getId() == id) {
1035                 break;
1036             }
1037             i++;
1038         }
1039     }
1040     // Add a new TrafficRecord if no one exsists for this aircraft.
1041     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1042         FGTrafficRecord rec;
1043         rec.setId(id);
1044
1045         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1046         rec.setRunway(intendedRoute->getRunway());
1047         rec.setLeg(leg);
1048         rec.setPositionAndIntentions(currentPosition, intendedRoute);
1049         //rec.setCallSign(callsign);
1050         rec.setAircraft(ref);
1051         rec.setHoldPosition(true);
1052         activeTraffic.push_back(rec);
1053     } else {
1054         i->setPositionAndIntentions(currentPosition, intendedRoute);
1055         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1056
1057     }
1058 }
1059
1060 // NOTE:
1061 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
1062 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
1063 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1064 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1065 // Note that this function is probably obsolete
1066 bool FGStartupController::hasInstruction(int id)
1067 {
1068     TrafficVectorIterator i = activeTraffic.begin();
1069     // Search search if the current id has an entry
1070     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1071     if (activeTraffic.size()) {
1072         //while ((i->getId() != id) && i != activeTraffic.end()) {
1073         while (i != activeTraffic.end()) {
1074             if (i->getId() == id) {
1075                 break;
1076             }
1077             i++;
1078         }
1079     }
1080     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1081         SG_LOG(SG_GENERAL, SG_ALERT,
1082                "AI error: checking ATC instruction for aircraft without traffic record");
1083     } else {
1084         return i->hasInstruction();
1085     }
1086     return false;
1087 }
1088
1089
1090 FGATCInstruction FGStartupController::getInstruction(int id)
1091 {
1092     TrafficVectorIterator i = activeTraffic.begin();
1093     // Search search if the current id has an entry
1094     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1095     if (activeTraffic.size()) {
1096         //while ((i->getId() != id) && i != activeTraffic.end()) {
1097         while (i != activeTraffic.end()) {
1098             if (i->getId() == id) {
1099                 break;
1100             }
1101             i++;
1102         }
1103     }
1104     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1105         SG_LOG(SG_GENERAL, SG_ALERT,
1106                "AI error: requesting ATC instruction for aircraft without traffic record");
1107     } else {
1108         return i->getInstruction();
1109     }
1110     return FGATCInstruction();
1111 }
1112
1113 void FGStartupController::signOff(int id)
1114 {
1115     TrafficVectorIterator i = activeTraffic.begin();
1116     // Search search if the current id alread has an entry
1117     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1118     if (activeTraffic.size()) {
1119         //while ((i->getId() != id) && i != activeTraffic.end()) {
1120         while (i != activeTraffic.end()) {
1121             if (i->getId() == id) {
1122                 break;
1123             }
1124             i++;
1125         }
1126     }
1127     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1128         SG_LOG(SG_GENERAL, SG_ALERT,
1129                "AI error: Aircraft without traffic record is signing off from tower");
1130     } else {
1131         //cerr << i->getAircraft()->getCallSign() << " signing off from startupcontroller" << endl;
1132         i = activeTraffic.erase(i);
1133     }
1134 }
1135
1136 bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId,
1137         AtcMsgDir msgDir)
1138 {
1139     int state = i->getState();
1140     if ((state == st) && available) {
1141         if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
1142
1143             //cerr << "Checking state " << st << " for " << i->getAircraft()->getCallSign() << endl;
1144             static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
1145             int n = trans_num->getIntValue();
1146             if (n == 0) {
1147                 trans_num->setIntValue(-1);
1148                 // PopupCallback(n);
1149                 //cerr << "Selected transmission message " << n << endl;
1150                 FGATCDialogNew::instance()->removeEntry(1);
1151             } else {
1152                 //cerr << "creading message for " << i->getAircraft()->getCallSign() << endl;
1153                 transmit(&(*i), msgId, msgDir, false);
1154                 return false;
1155             }
1156         }
1157         if (now > startTime) {
1158             //cerr << "Transmitting startup msg" << endl;
1159             transmit(&(*i), msgId, msgDir, true);
1160             i->updateState();
1161             lastTransmission = now;
1162             available = false;
1163             return true;
1164         }
1165     }
1166     return false;
1167 }
1168
1169 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
1170         double heading, double speed, double alt,
1171         double dt)
1172 {
1173     TrafficVectorIterator i = activeTraffic.begin();
1174     // Search search if the current id has an entry
1175     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1176     TrafficVectorIterator current, closest;
1177     if (activeTraffic.size()) {
1178         //while ((i->getId() != id) && i != activeTraffic.end()) {
1179         while (i != activeTraffic.end()) {
1180             if (i->getId() == id) {
1181                 break;
1182             }
1183             i++;
1184         }
1185     }
1186 //    // update position of the current aircraft
1187
1188     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1189         SG_LOG(SG_GENERAL, SG_ALERT,
1190                "AI error: updating aircraft without traffic record");
1191     } else {
1192         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1193         current = i;
1194     }
1195     setDt(getDt() + dt);
1196
1197     int state = i->getState();
1198
1199     // The user controlled aircraft should have crased here, because it doesn't have a traffic reference.
1200     // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
1201     time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
1202     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1203     //cerr << i->getAircraft()->getTrafficRef()->getCallSign()
1204     //     << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1205     //     << " at parking " << getGateName(i->getAircraft()) << endl;
1206
1207     if ((now - lastTransmission) > 3 + (rand() % 15)) {
1208         available = true;
1209     }
1210
1211     checkTransmissionState(0, now, (startTime + 0  ), i, MSG_ANNOUNCE_ENGINE_START,                     ATC_AIR_TO_GROUND);
1212     checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START,                      ATC_AIR_TO_GROUND);
1213     checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START,                       ATC_GROUND_TO_AIR);
1214     checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START,                  ATC_AIR_TO_GROUND);
1215     if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,       ATC_AIR_TO_GROUND)) {
1216         i->nextFrequency();
1217     }
1218     checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT,                          ATC_AIR_TO_GROUND);
1219     checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT,              ATC_GROUND_TO_AIR);
1220     checkTransmissionState(7, now, (startTime + 180), i, MSG_REQUEST_PUSHBACK_CLEARANCE,                ATC_AIR_TO_GROUND);
1221
1222
1223
1224     if ((state == 8) && available) {
1225         if (now > startTime + 200) {
1226             if (i->pushBackAllowed()) {
1227                 i->allowRepeatedTransmissions();
1228                 transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE,
1229                          ATC_GROUND_TO_AIR, true);
1230                 i->updateState();
1231             } else {
1232                 transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE,
1233                          ATC_GROUND_TO_AIR, true);
1234                 i->suppressRepeatedTransmissions();
1235             }
1236             lastTransmission = now;
1237             available = false;
1238         }
1239     }
1240     if ((state == 9) && available) {
1241         i->setHoldPosition(false);
1242     }
1243 }
1244
1245 // Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
1246 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
1247                             double lon, double elev, double hdg, double slope)
1248 {
1249     SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
1250     obj_pos = geod.makeZUpFrame();
1251     // hdg is not a compass heading, but a counter-clockwise rotation
1252     // around the Z axis
1253     obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
1254                                         0.0, 0.0, 1.0));
1255     obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
1256                                         0.0, 1.0, 0.0));
1257 }
1258
1259
1260 void FGStartupController::render(bool visible)
1261 {
1262
1263     SGMaterialLib *matlib = globals->get_matlib();
1264     if (group) {
1265         //int nr = ;
1266         globals->get_scenery()->get_scene_graph()->removeChild(group);
1267         //while (group->getNumChildren()) {
1268         //  cerr << "Number of children: " << group->getNumChildren() << endl;
1269         //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
1270         //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
1271         //geode->releaseGLObjects();
1272         //group->removeChild(geode);
1273         //delete geode;
1274         group = 0;
1275     }
1276     if (visible) {
1277         group = new osg::Group;
1278         FGScenery * local_scenery = globals->get_scenery();
1279         double elevation_meters = 0.0;
1280         double elevation_feet = 0.0;
1281
1282
1283         //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
1284         double dx = 0;
1285         for   (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1286             if (i->isActive(300)) {
1287                 // Handle start point
1288                 int pos = i->getCurrentPosition();
1289                 //cerr << "rendering for " << i->getAircraft()->getCallSign() << "pos = " << pos << endl;
1290                 if (pos > 0) {
1291                     FGTaxiSegment *segment  = parent->getGroundNetwork()->findSegment(pos);
1292                     SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1293                     SGGeod end  (SGGeod::fromDeg(segment->getEnd()->getLongitude(), segment->getEnd()->getLatitude()));
1294
1295                     double length = SGGeodesy::distanceM(start, end);
1296                     //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
1297
1298                     double az2, heading; //, distanceM;
1299                     SGGeodesy::inverse(start, end, heading, az2, length);
1300                     double coveredDistance = length * 0.5;
1301                     SGGeod center;
1302                     SGGeodesy::direct(start, heading, coveredDistance, center, az2);
1303                     //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
1304                     ///////////////////////////////////////////////////////////////////////////////
1305                     // Make a helper function out of this
1306                     osg::Matrix obj_pos;
1307                     osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1308                     obj_trans->setDataVariance(osg::Object::STATIC);
1309                     // Experimental: Calculate slope here, based on length, and the individual elevations
1310                     double elevationStart;
1311                     if (isUserAircraft((i)->getAircraft())) {
1312                         elevationStart = fgGetDouble("/position/ground-elev-m");
1313                     } else {
1314                         elevationStart = ((i)->getAircraft()->_getAltitude() * SG_FEET_TO_METER);
1315                     }
1316                     double elevationEnd   = segment->getEnd()->getElevation();
1317                     if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1318                         SGGeod center2 = end;
1319                         center2.setElevationM(SG_MAX_ELEVATION_M);
1320                         if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1321                             elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1322                             //elevation_meters += 0.5;
1323                         }
1324                         else {
1325                             elevationEnd = parent->getElevation();
1326                         }
1327                         segment->getEnd()->setElevation(elevationEnd);
1328                     }
1329
1330                     double elevationMean  = (elevationStart + elevationEnd) / 2.0;
1331                     double elevDiff       = elevationEnd - elevationStart;
1332
1333                     double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1334
1335                     //cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
1336
1337                     WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean + 0.5 + dx, -(heading), slope );
1338                     ;
1339
1340                     obj_trans->setMatrix( obj_pos );
1341                     //osg::Vec3 center(0, 0, 0)
1342
1343                     float width = length /2.0;
1344                     osg::Vec3 corner(-width, 0, 0.25f);
1345                     osg::Vec3 widthVec(2*width + 1, 0, 0);
1346                     osg::Vec3 heightVec(0, 1, 0);
1347                     osg::Geometry* geometry;
1348                     geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1349                     simgear::EffectGeode* geode = new simgear::EffectGeode;
1350                     geode->setName("test");
1351                     geode->addDrawable(geometry);
1352                     //osg::Node *custom_obj;
1353                     SGMaterial *mat;
1354                     if (segment->hasBlock()) {
1355                         mat = matlib->find("UnidirectionalTaperRed");
1356                     } else {
1357                         mat = matlib->find("UnidirectionalTaperGreen");
1358                     }
1359                     if (mat)
1360                         geode->setEffect(mat->get_effect());
1361                     obj_trans->addChild(geode);
1362                     // wire as much of the scene graph together as we can
1363                     //->addChild( obj_trans );
1364                     group->addChild( obj_trans );
1365                     /////////////////////////////////////////////////////////////////////
1366                 } else {
1367                     //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
1368                 }
1369                 for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
1370                     osg::Matrix obj_pos;
1371                     int k = (*j);
1372                     if (k > 0) {
1373                         //cerr << "rendering for " << i->getAircraft()->getCallSign() << "intention = " << k << endl;
1374                         osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1375                         obj_trans->setDataVariance(osg::Object::STATIC);
1376                         FGTaxiSegment *segment  = parent->getGroundNetwork()->findSegment(k);
1377
1378                         double elevationStart = segment->getStart()->getElevation();
1379                         double elevationEnd   = segment->getEnd  ()->getElevation();
1380                         if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
1381                             SGGeod center2 = segment->getStart()->getGeod();
1382                             center2.setElevationM(SG_MAX_ELEVATION_M);
1383                             if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
1384                                 elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
1385                                 //elevation_meters += 0.5;
1386                             }
1387                             else {
1388                                 elevationStart = parent->getElevation();
1389                             }
1390                             segment->getStart()->setElevation(elevationStart);
1391                         }
1392                         if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1393                             SGGeod center2 = segment->getEnd()->getGeod();
1394                             center2.setElevationM(SG_MAX_ELEVATION_M);
1395                             if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1396                                 elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1397                                 //elevation_meters += 0.5;
1398                             }
1399                             else {
1400                                 elevationEnd = parent->getElevation();
1401                             }
1402                             segment->getEnd()->setElevation(elevationEnd);
1403                         }
1404
1405                         double elevationMean  = (elevationStart + elevationEnd) / 2.0;
1406                         double elevDiff       = elevationEnd - elevationStart;
1407                         double length         = segment->getLength();
1408                         double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1409
1410                         //cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
1411
1412
1413                         WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), elevationMean + 0.5 + dx, -(segment->getHeading()), slope );
1414
1415                         //WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), parent->getElevation()+8+dx, -(segment->getHeading()) );
1416
1417                         obj_trans->setMatrix( obj_pos );
1418                         //osg::Vec3 center(0, 0, 0)
1419
1420                         float width = segment->getLength() /2.0;
1421                         osg::Vec3 corner(-width, 0, 0.25f);
1422                         osg::Vec3 widthVec(2*width + 1, 0, 0);
1423                         osg::Vec3 heightVec(0, 1, 0);
1424                         osg::Geometry* geometry;
1425                         geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1426                         simgear::EffectGeode* geode = new simgear::EffectGeode;
1427                         geode->setName("test");
1428                         geode->addDrawable(geometry);
1429                         //osg::Node *custom_obj;
1430                         SGMaterial *mat;
1431                         if (segment->hasBlock()) {
1432                             mat = matlib->find("UnidirectionalTaperRed");
1433                         } else {
1434                             mat = matlib->find("UnidirectionalTaperGreen");
1435                         }
1436                         if (mat)
1437                             geode->setEffect(mat->get_effect());
1438                         obj_trans->addChild(geode);
1439                         // wire as much of the scene graph together as we can
1440                         //->addChild( obj_trans );
1441                         group->addChild( obj_trans );
1442                     } else {
1443                         //cerr << "BIG FAT WARNING: k is here : " << pos << endl;
1444                     }
1445                 }
1446                 dx += 0.2;
1447             }
1448         }
1449         globals->get_scenery()->get_scene_graph()->addChild(group);
1450     }
1451 }
1452
1453 string FGStartupController::getName() {
1454     return string(parent->getId() + "-startup");
1455 }
1456
1457 void FGStartupController::update(double dt)
1458 {
1459
1460 }
1461
1462
1463
1464 /***************************************************************************
1465  * class FGApproachController
1466  *
1467  **************************************************************************/
1468 FGApproachController::FGApproachController(FGAirportDynamics *par):
1469         FGATCController()
1470 {
1471     parent = par;
1472 }
1473
1474 //
1475 void FGApproachController::announcePosition(int id,
1476         FGAIFlightPlan * intendedRoute,
1477         int currentPosition,
1478         double lat, double lon,
1479         double heading, double speed,
1480         double alt, double radius,
1481         int leg, FGAIAircraft * ref)
1482 {
1483     init();
1484     TrafficVectorIterator i = activeTraffic.begin();
1485     // Search whether the current id alread has an entry
1486     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1487     if (activeTraffic.size()) {
1488         //while ((i->getId() != id) && i != activeTraffic.end()) {
1489         while (i != activeTraffic.end()) {
1490             if (i->getId() == id) {
1491                 break;
1492             }
1493             i++;
1494         }
1495     }
1496     // Add a new TrafficRecord if no one exsists for this aircraft.
1497     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1498         FGTrafficRecord rec;
1499         rec.setId(id);
1500
1501         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1502         rec.setRunway(intendedRoute->getRunway());
1503         rec.setLeg(leg);
1504         //rec.setCallSign(callsign);
1505         rec.setAircraft(ref);
1506         activeTraffic.push_back(rec);
1507     } else {
1508         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1509     }
1510 }
1511
1512 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1513         double heading, double speed, double alt,
1514         double dt)
1515 {
1516     TrafficVectorIterator i = activeTraffic.begin();
1517     // Search search if the current id has an entry
1518     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1519     TrafficVectorIterator current, closest;
1520     if (activeTraffic.size()) {
1521         //while ((i->getId() != id) && i != activeTraffic.end()) {
1522         while (i != activeTraffic.end()) {
1523             if (i->getId() == id) {
1524                 break;
1525             }
1526             i++;
1527         }
1528     }
1529 //    // update position of the current aircraft
1530     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1531         SG_LOG(SG_GENERAL, SG_ALERT,
1532                "AI error: updating aircraft without traffic record");
1533     } else {
1534         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1535         current = i;
1536         //cerr << "ApproachController: checking for speed" << endl;
1537         time_t time_diff =
1538             current->getAircraft()->
1539             checkForArrivalTime(string("final001"));
1540         if (time_diff > 15) {
1541             current->setSpeedAdjustment(current->getAircraft()->
1542                                         getPerformance()->vDescent() *
1543                                         1.35);
1544         } else if (time_diff > 5) {
1545             current->setSpeedAdjustment(current->getAircraft()->
1546                                         getPerformance()->vDescent() *
1547                                         1.2);
1548         } else if (time_diff < -15) {
1549             current->setSpeedAdjustment(current->getAircraft()->
1550                                         getPerformance()->vDescent() *
1551                                         0.65);
1552         } else if (time_diff < -5) {
1553             current->setSpeedAdjustment(current->getAircraft()->
1554                                         getPerformance()->vDescent() *
1555                                         0.8);
1556         } else {
1557             current->clearSpeedAdjustment();
1558         }
1559         //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1560     }
1561     setDt(getDt() + dt);
1562 }
1563
1564 void FGApproachController::signOff(int id)
1565 {
1566     TrafficVectorIterator i = activeTraffic.begin();
1567     // Search search if the current id alread has an entry
1568     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1569     if (activeTraffic.size()) {
1570         //while ((i->getId() != id) && i != activeTraffic.end()) {
1571         while (i != activeTraffic.end()) {
1572             if (i->getId() == id) {
1573                 break;
1574             }
1575             i++;
1576         }
1577     }
1578     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1579         SG_LOG(SG_GENERAL, SG_ALERT,
1580                "AI error: Aircraft without traffic record is signing off from approach");
1581     } else {
1582         i = activeTraffic.erase(i);
1583     }
1584 }
1585
1586 void FGApproachController::update(double dt)
1587 {
1588
1589 }
1590
1591
1592
1593 bool FGApproachController::hasInstruction(int id)
1594 {
1595     TrafficVectorIterator i = activeTraffic.begin();
1596     // Search search if the current id has an entry
1597     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1598     if (activeTraffic.size()) {
1599         //while ((i->getId() != id) && i != activeTraffic.end()) {
1600         while (i != activeTraffic.end()) {
1601             if (i->getId() == id) {
1602                 break;
1603             }
1604             i++;
1605         }
1606     }
1607     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1608         SG_LOG(SG_GENERAL, SG_ALERT,
1609                "AI error: checking ATC instruction for aircraft without traffic record");
1610     } else {
1611         return i->hasInstruction();
1612     }
1613     return false;
1614 }
1615
1616
1617 FGATCInstruction FGApproachController::getInstruction(int id)
1618 {
1619     TrafficVectorIterator i = activeTraffic.begin();
1620     // Search search if the current id has an entry
1621     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1622     if (activeTraffic.size()) {
1623         //while ((i->getId() != id) && i != activeTraffic.end()) {
1624         while (i != activeTraffic.end()) {
1625             if (i->getId() == id) {
1626                 break;
1627             }
1628             i++;
1629         }
1630     }
1631     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1632         SG_LOG(SG_GENERAL, SG_ALERT,
1633                "AI error: requesting ATC instruction for aircraft without traffic record");
1634     } else {
1635         return i->getInstruction();
1636     }
1637     return FGATCInstruction();
1638 }
1639
1640
1641 ActiveRunway *FGApproachController::getRunway(string name)
1642 {
1643     ActiveRunwayVecIterator rwy = activeRunways.begin();
1644     if (activeRunways.size()) {
1645         while (rwy != activeRunways.end()) {
1646             if (rwy->getRunwayName() == name) {
1647                 break;
1648             }
1649             rwy++;
1650         }
1651     }
1652     if (rwy == activeRunways.end()) {
1653         ActiveRunway aRwy(name, 0);
1654         activeRunways.push_back(aRwy);
1655         rwy = activeRunways.end() - 1;
1656     }
1657     return &(*rwy);
1658 }
1659
1660 void FGApproachController::render(bool visible) {
1661     //cerr << "FGApproachController::render function not yet implemented" << endl;
1662 }
1663
1664
1665
1666 string FGApproachController::getName() {
1667     return string(parent->getId() + "-approach");
1668 }