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