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