]> git.mxchange.org Git - flightgear.git/blob - src/ATC/trafficcontrol.cxx
fix trx and rx heights and improve calculations
[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 transceiver_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         //cerr << "ITM:: Distance: " << distance_m << endl;
822         
823         double max_points = distance_m / point_distance;
824         deque<double> _elevations;
825         
826         //SGGeod probe_pilot = SGGeod::fromGeoc(center.advanceRadM( course, 0 ));
827         SGGeod probe_pilot = max_own_pos;
828         double elevation_under_pilot = 0.0;
829         if (scenery->get_elevation_m( probe_pilot, elevation_under_pilot, NULL )) {
830                 receiver_height = own_alt - elevation_under_pilot + 3; //assume antenna located 3 meters above ground
831         }
832         
833         
834         //SGGeod probe_sender = SGGeod::fromGeoc(center.advanceRadM( course, distance_m ));
835         SGGeod probe_sender = max_sender_pos;
836         double elevation_under_sender = 0.0;
837         if (scenery->get_elevation_m( probe_sender, elevation_under_sender, NULL )) {
838                 transmitter_height = sender_alt - elevation_under_sender;
839         }
840         if(ground_to_air) 
841                 transmitter_height += ATC_HAAT;
842         else
843                 transmitter_height += Aircraft_HAAT;
844         
845         cerr << "ITM:: RCVhgt: " << receiver_height << ", TRXhgt: " << transmitter_height << endl;
846         // If distance larger than this value (400 km), assume reception imposssible
847         // technically 400 km is no problem if LOS conditions exist,
848         // but we do this to spare resources
849         if (distance_m > 400000)
850                 return -1.0;
851         
852         unsigned int e_size = (deque<unsigned>::size_type)max_points;
853         
854         while (_elevations.size() <= e_size) {
855                 probe_distance += point_distance;
856                 SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, probe_distance ));
857                 
858                 double elevation_m = 0.0;
859         
860                 if (scenery->get_elevation_m( probe, elevation_m, NULL )) {
861                          _elevations.push_front(elevation_m);
862                          //cerr << "ITM:: Probe elev: " << elevation_m << endl;
863                 }
864         }
865         _elevations.push_back(elevation_under_pilot);
866         _elevations.push_front(elevation_under_sender);
867         double max_alt_between=0.0;
868         for( deque<double>::size_type i = 0; i < _elevations.size(); i++ ) {
869                 if (_elevations[i] > max_alt_between) {
870                         max_alt_between = _elevations[i];
871                 }
872         }
873         
874         double num_points= (double)_elevations.size();
875         //cerr << "ITM:: Max alt between: " << max_alt_between << ", num points:" << num_points << endl;
876         _elevations.push_front(point_distance);
877         _elevations.push_front(num_points -1);
878         int size = _elevations.size();
879         double itm_elev[size];
880         for(int i=0;i<size;i++) {
881                 itm_elev[i]=_elevations[i];
882                 //cerr << "ITM:: itm_elev: " << _elevations[i] << endl;
883         }
884         
885         ////////////// ITM default parameters //////////////
886         // later perhaps take them from tile materials?
887         double eps_dielect=15.0;
888         double sgm_conductivity = 0.005;
889         double eno = 301.0;
890         double frq_mhz = 125.0;         // middle of bandplan
891         int radio_climate = 5;          // continental temperate
892         int pol=1;      // assuming vertical polarization although this is more complex in reality
893         double conf = 0.90;     // my own tests in Radiomobile have worked best with these values
894         double rel = 0.80;      // ^^
895         double dbloss;
896         char strmode[150];
897         int errnum;
898         
899         /////////// radio parameters ///////////
900         double receiver_sensitivity = -110.0;   // typical AM receiver sensitivity seems to be 0.8 microVolt at 12dB SINAD
901         // AM transmitter power in dBm.
902         // Note this value is calculated from the typical final transistor stage output
903         // !!! small aircraft have portable transmitters which operate at 36 dBm output (4 Watts)
904         // later store this value in aircraft description
905         // ATC comms usually operate high power equipment, thus making the link asymetrical; this is ignored for now
906         double transmitter_power = 43.0;
907         double antenna_gain = 2.0;      //real-life gain for conventional monopole/dipole antenna
908         if(ground_to_air)
909                 transmitter_power = 49.0;
910         else
911                 transmitter_power = 43.0;
912         if(ground_to_air)
913                 antenna_gain = 5.0; //pilot plane's antenna gain + ground station antenna gain
914         else
915                 antenna_gain = 2.0; //pilot plane's antenna gain + AI aircraft antenna gain
916         double link_budget = transmitter_power - receiver_sensitivity + antenna_gain;   
917         
918         
919         // first Fresnel zone radius
920         // frequency in the middle of the bandplan, more accuracy is not necessary
921         double fz_clr= 8.657 * sqrt(distance_m / 0.125);
922         
923         // TODO: If we clear the first Fresnel zone, we are into line of sight teritory
924
925         // else we need to calculate point to point link loss
926
927         point_to_point(itm_elev, transmitter_height, receiver_height,
928                 eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate,
929                 pol, conf, rel, dbloss, strmode, errnum);
930
931         cerr << "ITM:: Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum << endl;
932         
933         //if (errnum !=0 && errnum !=1)
934         //      return -1;
935         double snr = link_budget - dbloss;
936         return snr;
937
938 }
939
940 string FGATCController::formatATCFrequency3_2(int freq)
941 {
942     char buffer[7];
943     snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
944     return string(buffer);
945 }
946
947 // TODO: Set transponder codes according to real-world routes.
948 // The current version just returns a random string of four octal numbers. 
949 string FGATCController::genTransponderCode(string fltRules)
950 {
951     if (fltRules == "VFR") {
952         return string("1200");
953     } else {
954         char buffer[5];
955         snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
956                  rand() % 8);
957         return string(buffer);
958     }
959 }
960
961 void FGATCController::init() 
962 {
963    if (!initialized) {
964        FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
965        mgr->addController(this);
966        initialized = true;
967     }
968 }
969
970 /***************************************************************************
971  * class FGTowerController
972  *
973  **************************************************************************/
974 FGTowerController::FGTowerController(FGAirportDynamics *par) :
975 FGATCController()
976 {
977     parent = par;
978 }
979
980 // 
981 void FGTowerController::announcePosition(int id,
982                                          FGAIFlightPlan * intendedRoute,
983                                          int currentPosition, double lat,
984                                          double lon, double heading,
985                                          double speed, double alt,
986                                          double radius, int leg,
987                                          FGAIAircraft * ref)
988 {
989     init();
990     TrafficVectorIterator i = activeTraffic.begin();
991     // Search whether the current id alread has an entry
992     // This might be faster using a map instead of a vector, but let's start by taking a safe route
993     if (activeTraffic.size()) {
994         //while ((i->getId() != id) && i != activeTraffic.end()) {
995         while (i != activeTraffic.end()) {
996             if (i->getId() == id) {
997                 break;
998             }
999             i++;
1000         }
1001     }
1002     // Add a new TrafficRecord if no one exsists for this aircraft.
1003     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1004         FGTrafficRecord rec;
1005         rec.setId(id);
1006
1007         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1008         rec.setRunway(intendedRoute->getRunway());
1009         rec.setLeg(leg);
1010         //rec.setCallSign(callsign);
1011         rec.setRadius(radius);
1012         rec.setAircraft(ref);
1013         activeTraffic.push_back(rec);
1014         // Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway. 
1015         ActiveRunwayVecIterator rwy = activeRunways.begin();
1016         if (activeRunways.size()) {
1017             while (rwy != activeRunways.end()) {
1018                 if (rwy->getRunwayName() == intendedRoute->getRunway()) {
1019                     break;
1020                 }
1021                 rwy++;
1022             }
1023         }
1024         if (rwy == activeRunways.end()) {
1025             ActiveRunway aRwy(intendedRoute->getRunway(), id);
1026             aRwy.addToDepartureCue(ref);
1027             activeRunways.push_back(aRwy);
1028             rwy = (activeRunways.end()-1);
1029         } else {
1030             rwy->addToDepartureCue(ref);
1031         }
1032
1033         //cerr << ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getDepartureCueSize() <<  " for takeoff " << endl;
1034     } else {
1035         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1036     }
1037 }
1038
1039 void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
1040                                                   double heading, double speed, double alt,
1041                                                   double dt)
1042 {
1043     TrafficVectorIterator i = activeTraffic.begin();
1044     // Search whether the current id has an entry
1045     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1046     TrafficVectorIterator current, closest;
1047     if (activeTraffic.size()) {
1048         //while ((i->getId() != id) && i != activeTraffic.end()) {
1049         while (i != activeTraffic.end()) {
1050             if (i->getId() == id) {
1051                 break;
1052             }
1053             i++;
1054         }
1055     }
1056 //    // update position of the current aircraft
1057     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1058         SG_LOG(SG_GENERAL, SG_ALERT,
1059                "AI error: updating aircraft without traffic record");
1060     } else {
1061         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1062         current = i;
1063     }
1064     setDt(getDt() + dt);
1065
1066     // see if we already have a clearance record for the currently active runway
1067     // NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is
1068     // already exists here. So, we can simplify the current code. 
1069     ActiveRunwayVecIterator rwy = activeRunways.begin();
1070     while (rwy != activeRunways.end()) {
1071         if (rwy->getRunwayName() == current->getRunway()) {
1072             break;
1073         }
1074         rwy++;
1075     }
1076
1077     // only bother running the following code if the current aircraft is the
1078     // first in line for depature
1079     /* if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
1080         if (rwy->getCleared()) {
1081             if (id == rwy->getCleared()) {
1082                 current->setHoldPosition(false);
1083             } else {
1084                 current->setHoldPosition(true);
1085             }
1086         } else {
1087             // For now. At later stages, this will probably be the place to check for inbound traffc.
1088             rwy->setCleared(id);
1089         }
1090     } */
1091     // only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control
1092     if (current->getAircraft()->getTakeOffStatus() == 2) {
1093         current->setHoldPosition(true);
1094         int clearanceId = rwy->getCleared();
1095         if (clearanceId) {
1096             if (id == clearanceId) {
1097                 current->setHoldPosition(false);
1098             }
1099         } else {
1100             if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
1101                 rwy->setCleared(id);
1102             }
1103         }
1104     }
1105 }
1106
1107
1108 void FGTowerController::signOff(int id)
1109 {
1110     TrafficVectorIterator i = activeTraffic.begin();
1111     // Search search if the current id alread has an entry
1112     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1113     if (activeTraffic.size()) {
1114         //while ((i->getId() != id) && i != activeTraffic.end()) {
1115         while (i != activeTraffic.end()) {
1116             if (i->getId() == id) {
1117                 break;
1118             }
1119             i++;
1120         }
1121     }
1122     // If this aircraft has left the runway, we can clear the departure record for this runway
1123     ActiveRunwayVecIterator rwy = activeRunways.begin();
1124     if (activeRunways.size()) {
1125         //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
1126         while (rwy != activeRunways.end()) {
1127             if (rwy->getRunwayName() == i->getRunway()) {
1128                 break;
1129             }
1130             rwy++;
1131         }
1132         if (rwy != activeRunways.end()) {
1133             rwy->setCleared(0);
1134             rwy->updateDepartureCue();
1135         } else {
1136             SG_LOG(SG_GENERAL, SG_ALERT,
1137                    "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
1138         }
1139     }
1140     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1141         SG_LOG(SG_GENERAL, SG_ALERT,
1142                "AI error: Aircraft without traffic record is signing off from tower");
1143     } else {
1144         i->getAircraft()->resetTakeOffStatus();
1145         i = activeTraffic.erase(i);
1146         //cerr << "Signing off from tower controller" << endl;
1147     }
1148 }
1149
1150 // NOTE:
1151 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
1152 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
1153 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1154 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1155 // Note that this function is probably obsolete
1156 bool FGTowerController::hasInstruction(int id)
1157 {
1158     TrafficVectorIterator i = activeTraffic.begin();
1159     // Search search if the current id has an entry
1160     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1161     if (activeTraffic.size()) {
1162         //while ((i->getId() != id) && i != activeTraffic.end()) {
1163         while (i != activeTraffic.end()) {
1164             if (i->getId() == id) {
1165                 break;
1166             }
1167             i++;
1168         }
1169     }
1170     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1171         SG_LOG(SG_GENERAL, SG_ALERT,
1172                "AI error: checking ATC instruction for aircraft without traffic record");
1173     } else {
1174         return i->hasInstruction();
1175     }
1176     return false;
1177 }
1178
1179
1180 FGATCInstruction FGTowerController::getInstruction(int id)
1181 {
1182     TrafficVectorIterator i = activeTraffic.begin();
1183     // Search search if the current id has an entry
1184     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1185     if (activeTraffic.size()) {
1186         //while ((i->getId() != id) && i != activeTraffic.end()) {
1187         while (i != activeTraffic.end()) {
1188             if (i->getId() == id) {
1189                 break;
1190             }
1191             i++;
1192         }
1193     }
1194     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1195         SG_LOG(SG_GENERAL, SG_ALERT,
1196                "AI error: requesting ATC instruction for aircraft without traffic record");
1197     } else {
1198         return i->getInstruction();
1199     }
1200     return FGATCInstruction();
1201 }
1202
1203 void FGTowerController::render(bool visible) {
1204     //cerr << "FGTowerController::render function not yet implemented" << endl;
1205 }
1206
1207 string FGTowerController::getName() {
1208     return string(parent->getId() + "-tower");
1209 }
1210
1211
1212
1213 /***************************************************************************
1214  * class FGStartupController
1215  *
1216  **************************************************************************/
1217 FGStartupController::FGStartupController(FGAirportDynamics *par):
1218     FGATCController()
1219 {
1220     parent = par;
1221 }
1222
1223 void FGStartupController::announcePosition(int id,
1224                                            FGAIFlightPlan * intendedRoute,
1225                                            int currentPosition, double lat,
1226                                            double lon, double heading,
1227                                            double speed, double alt,
1228                                            double radius, int leg,
1229                                            FGAIAircraft * ref)
1230 {
1231     init();
1232     TrafficVectorIterator i = activeTraffic.begin();
1233     // Search whether the current id alread has an entry
1234     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1235     if (activeTraffic.size()) {
1236         //while ((i->getId() != id) && i != activeTraffic.end()) {
1237         while (i != activeTraffic.end()) {
1238             if (i->getId() == id) {
1239                 break;
1240             }
1241             i++;
1242         }
1243     }
1244     // Add a new TrafficRecord if no one exsists for this aircraft.
1245     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1246         FGTrafficRecord rec;
1247         rec.setId(id);
1248
1249         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1250         rec.setRunway(intendedRoute->getRunway());
1251         rec.setLeg(leg);
1252         rec.setPositionAndIntentions(currentPosition, intendedRoute);
1253         //rec.setCallSign(callsign);
1254         rec.setAircraft(ref);
1255         rec.setHoldPosition(true);
1256         activeTraffic.push_back(rec);
1257     } else {
1258         i->setPositionAndIntentions(currentPosition, intendedRoute);
1259         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1260
1261     }
1262 }
1263
1264 // NOTE:
1265 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
1266 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
1267 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1268 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1269 // Note that this function is probably obsolete
1270 bool FGStartupController::hasInstruction(int id)
1271 {
1272     TrafficVectorIterator i = activeTraffic.begin();
1273     // Search search if the current id has an entry
1274     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1275     if (activeTraffic.size()) {
1276         //while ((i->getId() != id) && i != activeTraffic.end()) {
1277         while (i != activeTraffic.end()) {
1278             if (i->getId() == id) {
1279                 break;
1280             }
1281             i++;
1282         }
1283     }
1284     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1285         SG_LOG(SG_GENERAL, SG_ALERT,
1286                "AI error: checking ATC instruction for aircraft without traffic record");
1287     } else {
1288         return i->hasInstruction();
1289     }
1290     return false;
1291 }
1292
1293
1294 FGATCInstruction FGStartupController::getInstruction(int id)
1295 {
1296     TrafficVectorIterator i = activeTraffic.begin();
1297     // Search search if the current id has an entry
1298     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1299     if (activeTraffic.size()) {
1300         //while ((i->getId() != id) && i != activeTraffic.end()) {
1301         while (i != activeTraffic.end()) {
1302             if (i->getId() == id) {
1303                 break;
1304             }
1305             i++;
1306         }
1307     }
1308     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1309         SG_LOG(SG_GENERAL, SG_ALERT,
1310                "AI error: requesting ATC instruction for aircraft without traffic record");
1311     } else {
1312         return i->getInstruction();
1313     }
1314     return FGATCInstruction();
1315 }
1316
1317 void FGStartupController::signOff(int id)
1318 {
1319     TrafficVectorIterator i = activeTraffic.begin();
1320     // Search search if the current id alread has an entry
1321     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1322     if (activeTraffic.size()) {
1323         //while ((i->getId() != id) && i != activeTraffic.end()) {
1324         while (i != activeTraffic.end()) {
1325             if (i->getId() == id) {
1326                 break;
1327             }
1328             i++;
1329         }
1330     }
1331     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1332         SG_LOG(SG_GENERAL, SG_ALERT,
1333                "AI error: Aircraft without traffic record is signing off from tower");
1334     } else {
1335         //cerr << i->getAircraft()->getCallSign() << " signing off from startupcontroller" << endl;
1336         i = activeTraffic.erase(i);
1337     }
1338 }
1339
1340 bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId,
1341                                AtcMsgDir msgDir)
1342 {
1343     int state = i->getState();
1344     if ((state == st) && available) {
1345         if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
1346             
1347             //cerr << "Checking state " << st << " for " << i->getAircraft()->getCallSign() << endl;
1348             static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
1349             int n = trans_num->getIntValue();
1350             if (n == 0) {
1351                 trans_num->setIntValue(-1);
1352                  // PopupCallback(n);
1353                  //cerr << "Selected transmission message " << n << endl;
1354                  FGATCManager *atc = (FGATCManager*) globals->get_subsystem("atc");
1355                  atc->getATCDialog()->removeEntry(1);
1356             } else {
1357                 //cerr << "creading message for " << i->getAircraft()->getCallSign() << endl;
1358                 transmit(&(*i), &(*parent), msgId, msgDir, false);
1359                 return false;
1360             }
1361         }
1362         if (now > startTime) {
1363             //cerr << "Transmitting startup msg" << endl;
1364             transmit(&(*i), &(*parent), msgId, msgDir, true);
1365             i->updateState();
1366             lastTransmission = now;
1367             available = false;
1368             return true;
1369         }
1370     }
1371     return false;
1372 }
1373
1374 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
1375                                                     double heading, double speed, double alt,
1376                                                     double dt)
1377 {
1378     TrafficVectorIterator i = activeTraffic.begin();
1379     // Search search if the current id has an entry
1380     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1381     TrafficVectorIterator current, closest;
1382     if (activeTraffic.size()) {
1383         //while ((i->getId() != id) && i != activeTraffic.end()) {
1384         while (i != activeTraffic.end()) {
1385             if (i->getId() == id) {
1386                 break;
1387             }
1388             i++;
1389         }
1390     }
1391 //    // update position of the current aircraft
1392
1393     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1394         SG_LOG(SG_GENERAL, SG_ALERT,
1395                "AI error: updating aircraft without traffic record");
1396     } else {
1397         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1398         current = i;
1399     }
1400     setDt(getDt() + dt);
1401
1402     int state = i->getState();
1403
1404     // The user controlled aircraft should have crased here, because it doesn't have a traffic reference. 
1405     // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
1406     time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
1407     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1408     //cerr << i->getAircraft()->getTrafficRef()->getCallSign() 
1409     //     << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1410     //     << " at parking " << getGateName(i->getAircraft()) << endl;
1411
1412     if ((now - lastTransmission) > 3 + (rand() % 15)) {
1413         available = true;
1414     }
1415
1416     checkTransmissionState(0, now, (startTime + 0  ), i, MSG_ANNOUNCE_ENGINE_START,                     ATC_AIR_TO_GROUND);
1417     checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START,                      ATC_AIR_TO_GROUND);
1418     checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START,                       ATC_GROUND_TO_AIR);
1419     checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START,                  ATC_AIR_TO_GROUND);
1420     if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,       ATC_AIR_TO_GROUND)) {
1421         i->nextFrequency();
1422     }
1423     checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT,                          ATC_AIR_TO_GROUND);
1424     checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT,              ATC_GROUND_TO_AIR);
1425     checkTransmissionState(7, now, (startTime + 180), i, MSG_REQUEST_PUSHBACK_CLEARANCE,                ATC_AIR_TO_GROUND);
1426
1427
1428    
1429     if ((state == 8) && available) {
1430         if (now > startTime + 200) {
1431             if (i->pushBackAllowed()) {
1432                 i->allowRepeatedTransmissions();
1433                 transmit(&(*i), &(*parent), MSG_PERMIT_PUSHBACK_CLEARANCE,
1434                          ATC_GROUND_TO_AIR, true);
1435                 i->updateState();
1436             } else {
1437                 transmit(&(*i), &(*parent), MSG_HOLD_PUSHBACK_CLEARANCE,
1438                          ATC_GROUND_TO_AIR, true);
1439                 i->suppressRepeatedTransmissions();
1440             }
1441             lastTransmission = now;
1442             available = false;
1443         }
1444     }
1445     if ((state == 9) && available) {
1446         i->setHoldPosition(false);
1447     }
1448 }
1449
1450 // Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
1451 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
1452                             double lon, double elev, double hdg, double slope)
1453 {
1454     SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
1455     obj_pos = geod.makeZUpFrame();
1456     // hdg is not a compass heading, but a counter-clockwise rotation
1457     // around the Z axis
1458     obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
1459                                         0.0, 0.0, 1.0));
1460     obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
1461                                         0.0, 1.0, 0.0));
1462 }
1463
1464
1465 void FGStartupController::render(bool visible)
1466 {
1467
1468     SGMaterialLib *matlib = globals->get_matlib();
1469     if (group) {
1470         //int nr = ;
1471         globals->get_scenery()->get_scene_graph()->removeChild(group);
1472         //while (group->getNumChildren()) {
1473         //  cerr << "Number of children: " << group->getNumChildren() << endl;
1474         //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
1475           //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
1476            //geode->releaseGLObjects();
1477            //group->removeChild(geode);
1478            //delete geode;
1479         group = 0;
1480     }
1481     if (visible) {
1482         group = new osg::Group;
1483         FGScenery * local_scenery = globals->get_scenery();
1484         double elevation_meters = 0.0;
1485         double elevation_feet = 0.0;
1486
1487
1488         //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
1489         double dx = 0;
1490         for   (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1491             // Handle start point
1492             int pos = i->getCurrentPosition();
1493             //cerr << "rendering for " << i->getAircraft()->getCallSign() << "pos = " << pos << endl;
1494             if (pos > 0) {
1495                 FGTaxiSegment *segment  = parent->getGroundNetwork()->findSegment(pos);
1496                 SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1497                 SGGeod end  (SGGeod::fromDeg(segment->getEnd()->getLongitude(), segment->getEnd()->getLatitude()));
1498
1499                 double length = SGGeodesy::distanceM(start, end);
1500                 //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
1501
1502                 double az2, heading; //, distanceM;
1503                 SGGeodesy::inverse(start, end, heading, az2, length);
1504                 double coveredDistance = length * 0.5;
1505                 SGGeod center;
1506                 SGGeodesy::direct(start, heading, coveredDistance, center, az2);
1507                 //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
1508                 ///////////////////////////////////////////////////////////////////////////////
1509                 // Make a helper function out of this
1510                 osg::Matrix obj_pos;
1511                 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1512                 obj_trans->setDataVariance(osg::Object::STATIC);
1513                 // Experimental: Calculate slope here, based on length, and the individual elevations
1514                 double elevationStart;
1515                 if (isUserAircraft((i)->getAircraft())) {
1516                     elevationStart = fgGetDouble("/position/ground-elev-m");
1517                 } else {
1518                     elevationStart = ((i)->getAircraft()->_getAltitude()); 
1519                 }
1520                 double elevationEnd   = segment->getEnd()->getElevation();
1521                 if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1522                     SGGeod center2 = end;
1523                     center2.setElevationM(SG_MAX_ELEVATION_M);
1524                     if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1525                         elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1526                             //elevation_meters += 0.5;
1527                     }
1528                     else { 
1529                         elevationEnd = parent->getElevation();
1530                     }
1531                     segment->getEnd()->setElevation(elevationEnd);
1532                 }
1533
1534                 double elevationMean  = (elevationStart + elevationEnd) / 2.0;
1535                 double elevDiff       = elevationEnd - elevationStart;
1536                
1537                double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1538                 
1539                //cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
1540
1541                 WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean + 0.5, -(heading), slope );
1542 ;
1543
1544                 obj_trans->setMatrix( obj_pos );
1545                 //osg::Vec3 center(0, 0, 0)
1546
1547                 float width = length /2.0;
1548                 osg::Vec3 corner(-width, 0, 0.25f);
1549                 osg::Vec3 widthVec(2*width + 1, 0, 0);
1550                 osg::Vec3 heightVec(0, 1, 0);
1551                 osg::Geometry* geometry;
1552                 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1553                 simgear::EffectGeode* geode = new simgear::EffectGeode;
1554                 geode->setName("test");
1555                 geode->addDrawable(geometry);
1556                 //osg::Node *custom_obj;
1557                 SGMaterial *mat = matlib->find("UnidirectionalTaper");
1558                 if (mat)
1559                     geode->setEffect(mat->get_effect());
1560                 obj_trans->addChild(geode);
1561                 // wire as much of the scene graph together as we can
1562                 //->addChild( obj_trans );
1563                 group->addChild( obj_trans );
1564                 /////////////////////////////////////////////////////////////////////
1565             } else {
1566                 //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
1567             }
1568             for(intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
1569                 osg::Matrix obj_pos;
1570                 int k = (*j);
1571                 if (k > 0) {
1572                     //cerr << "rendering for " << i->getAircraft()->getCallSign() << "intention = " << k << endl;
1573                     osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1574                     obj_trans->setDataVariance(osg::Object::STATIC);
1575                     FGTaxiSegment *segment  = parent->getGroundNetwork()->findSegment(k);
1576
1577                     double elevationStart = segment->getStart()->getElevation();
1578                     double elevationEnd   = segment->getEnd  ()->getElevation();
1579                     if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
1580                         SGGeod center2 = segment->getStart()->getGeod();
1581                         center2.setElevationM(SG_MAX_ELEVATION_M);
1582                         if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
1583                             elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
1584                             //elevation_meters += 0.5;
1585                         }
1586                         else { 
1587                             elevationStart = parent->getElevation();
1588                         }
1589                         segment->getStart()->setElevation(elevationStart);
1590                     }
1591                     if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1592                         SGGeod center2 = segment->getEnd()->getGeod();
1593                         center2.setElevationM(SG_MAX_ELEVATION_M);
1594                         if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1595                             elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1596                             //elevation_meters += 0.5;
1597                         }
1598                         else { 
1599                             elevationEnd = parent->getElevation();
1600                         }
1601                         segment->getEnd()->setElevation(elevationEnd);
1602                     }
1603  
1604                     double elevationMean  = (elevationStart + elevationEnd) / 2.0;
1605                     double elevDiff       = elevationEnd - elevationStart;
1606                     double length         = segment->getLength();
1607                     double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1608                 
1609                     //cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
1610
1611
1612                     WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), elevationMean + 0.5, -(segment->getHeading()), slope );
1613
1614                     //WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), parent->getElevation()+8+dx, -(segment->getHeading()) );
1615
1616                     obj_trans->setMatrix( obj_pos );
1617                     //osg::Vec3 center(0, 0, 0)
1618
1619                     float width = segment->getLength() /2.0;
1620                     osg::Vec3 corner(-width, 0, 0.25f);
1621                     osg::Vec3 widthVec(2*width + 1, 0, 0);
1622                     osg::Vec3 heightVec(0, 1, 0);
1623                     osg::Geometry* geometry;
1624                     geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1625                     simgear::EffectGeode* geode = new simgear::EffectGeode;
1626                     geode->setName("test");
1627                     geode->addDrawable(geometry);
1628                     //osg::Node *custom_obj;
1629                     SGMaterial *mat = matlib->find("UnidirectionalTaper");
1630                     if (mat)
1631                         geode->setEffect(mat->get_effect());
1632                     obj_trans->addChild(geode);
1633                     // wire as much of the scene graph together as we can
1634                     //->addChild( obj_trans );
1635                     group->addChild( obj_trans );
1636                 } else {
1637                     //cerr << "BIG FAT WARNING: k is here : " << pos << endl;
1638                 }
1639             }
1640             //dx += 0.1;
1641         }
1642         globals->get_scenery()->get_scene_graph()->addChild(group);
1643     }
1644 }
1645
1646 string FGStartupController::getName() {
1647     return string(parent->getId() + "-startup");
1648 }
1649
1650
1651 /***************************************************************************
1652  * class FGApproachController
1653  *
1654  **************************************************************************/
1655 FGApproachController::FGApproachController(FGAirportDynamics *par):
1656 FGATCController()
1657 {
1658     parent = par;
1659 }
1660
1661 // 
1662 void FGApproachController::announcePosition(int id,
1663                                             FGAIFlightPlan * intendedRoute,
1664                                             int currentPosition,
1665                                             double lat, double lon,
1666                                             double heading, double speed,
1667                                             double alt, double radius,
1668                                             int leg, FGAIAircraft * ref)
1669 {
1670     init();
1671     TrafficVectorIterator i = activeTraffic.begin();
1672     // Search whether the current id alread has an entry
1673     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1674     if (activeTraffic.size()) {
1675         //while ((i->getId() != id) && i != activeTraffic.end()) {
1676         while (i != activeTraffic.end()) {
1677             if (i->getId() == id) {
1678                 break;
1679             }
1680             i++;
1681         }
1682     }
1683     // Add a new TrafficRecord if no one exsists for this aircraft.
1684     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1685         FGTrafficRecord rec;
1686         rec.setId(id);
1687
1688         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1689         rec.setRunway(intendedRoute->getRunway());
1690         rec.setLeg(leg);
1691         //rec.setCallSign(callsign);
1692         rec.setAircraft(ref);
1693         activeTraffic.push_back(rec);
1694     } else {
1695         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1696     }
1697 }
1698
1699 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1700                                                      double heading, double speed, double alt,
1701                                                      double dt)
1702 {
1703     TrafficVectorIterator i = activeTraffic.begin();
1704     // Search search if the current id has an entry
1705     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1706     TrafficVectorIterator current, closest;
1707     if (activeTraffic.size()) {
1708         //while ((i->getId() != id) && i != activeTraffic.end()) {
1709         while (i != activeTraffic.end()) {
1710             if (i->getId() == id) {
1711                 break;
1712             }
1713             i++;
1714         }
1715     }
1716 //    // update position of the current aircraft
1717     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1718         SG_LOG(SG_GENERAL, SG_ALERT,
1719                "AI error: updating aircraft without traffic record");
1720     } else {
1721         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1722         current = i;
1723         //cerr << "ApproachController: checking for speed" << endl;
1724         time_t time_diff =
1725             current->getAircraft()->
1726             checkForArrivalTime(string("final001"));
1727         if (time_diff > 15) {
1728             current->setSpeedAdjustment(current->getAircraft()->
1729                                         getPerformance()->vDescent() *
1730                                         1.35);
1731         } else if (time_diff > 5) {
1732             current->setSpeedAdjustment(current->getAircraft()->
1733                                         getPerformance()->vDescent() *
1734                                         1.2);
1735         } else if (time_diff < -15) {
1736             current->setSpeedAdjustment(current->getAircraft()->
1737                                         getPerformance()->vDescent() *
1738                                         0.65);
1739         } else if (time_diff < -5) {
1740             current->setSpeedAdjustment(current->getAircraft()->
1741                                         getPerformance()->vDescent() *
1742                                         0.8);
1743         } else {
1744             current->clearSpeedAdjustment();
1745         }
1746         //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1747     }
1748     setDt(getDt() + dt);
1749 }
1750
1751 void FGApproachController::signOff(int id)
1752 {
1753     TrafficVectorIterator i = activeTraffic.begin();
1754     // Search search if the current id alread has an entry
1755     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1756     if (activeTraffic.size()) {
1757         //while ((i->getId() != id) && i != activeTraffic.end()) {
1758         while (i != activeTraffic.end()) {
1759             if (i->getId() == id) {
1760                 break;
1761             }
1762             i++;
1763         }
1764     }
1765     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1766         SG_LOG(SG_GENERAL, SG_ALERT,
1767                "AI error: Aircraft without traffic record is signing off from approach");
1768     } else {
1769         i = activeTraffic.erase(i);
1770     }
1771 }
1772
1773
1774
1775
1776 bool FGApproachController::hasInstruction(int id)
1777 {
1778     TrafficVectorIterator i = activeTraffic.begin();
1779     // Search search if the current id has an entry
1780     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1781     if (activeTraffic.size()) {
1782         //while ((i->getId() != id) && i != activeTraffic.end()) {
1783         while (i != activeTraffic.end()) {
1784             if (i->getId() == id) {
1785                 break;
1786             }
1787             i++;
1788         }
1789     }
1790     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1791         SG_LOG(SG_GENERAL, SG_ALERT,
1792                "AI error: checking ATC instruction for aircraft without traffic record");
1793     } else {
1794         return i->hasInstruction();
1795     }
1796     return false;
1797 }
1798
1799
1800 FGATCInstruction FGApproachController::getInstruction(int id)
1801 {
1802     TrafficVectorIterator i = activeTraffic.begin();
1803     // Search search if the current id has an entry
1804     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1805     if (activeTraffic.size()) {
1806         //while ((i->getId() != id) && i != activeTraffic.end()) {
1807         while (i != activeTraffic.end()) {
1808             if (i->getId() == id) {
1809                 break;
1810             }
1811             i++;
1812         }
1813     }
1814     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1815         SG_LOG(SG_GENERAL, SG_ALERT,
1816                "AI error: requesting ATC instruction for aircraft without traffic record");
1817     } else {
1818         return i->getInstruction();
1819     }
1820     return FGATCInstruction();
1821 }
1822
1823
1824 ActiveRunway *FGApproachController::getRunway(string name)
1825 {
1826     ActiveRunwayVecIterator rwy = activeRunways.begin();
1827     if (activeRunways.size()) {
1828         while (rwy != activeRunways.end()) {
1829             if (rwy->getRunwayName() == name) {
1830                 break;
1831             }
1832             rwy++;
1833         }
1834     }
1835     if (rwy == activeRunways.end()) {
1836         ActiveRunway aRwy(name, 0);
1837         activeRunways.push_back(aRwy);
1838         rwy = activeRunways.end() - 1;
1839     }
1840     return &(*rwy);
1841 }
1842
1843 void FGApproachController::render(bool visible) {
1844     //cerr << "FGApproachController::render function not yet implemented" << endl;
1845 }
1846
1847
1848
1849 string FGApproachController::getName() {
1850     return string(parent->getId() + "-approach");
1851 }