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