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