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