]> git.mxchange.org Git - flightgear.git/blob - src/ATC/trafficcontrol.cxx
5dad818ae45224729ed2bb4d1ae045373de64fc8
[flightgear.git] / src / ATC / trafficcontrol.cxx
1 // trafficrecord.cxx - Implementation of AIModels ATC code.
2 //
3 // Written by Durk Talsma, started September 2006.
4 //
5 // Copyright (C) 2006 Durk Talsma.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #include <algorithm>
28
29 #include "trafficcontrol.hxx"
30 #include "atc_mgr.hxx"
31 #include <AIModel/AIAircraft.hxx>
32 #include <AIModel/AIFlightPlan.hxx>
33 #include <AIModel/performancedata.hxx>
34 #include <AIModel/performancedb.hxx>
35 #include <Traffic/TrafficMgr.hxx>
36 #include <Airports/groundnetwork.hxx>
37 #include <Airports/dynamics.hxx>
38 #include <Airports/simple.hxx>
39
40 using std::sort;
41
42 /***************************************************************************
43  * ActiveRunway
44  **************************************************************************/
45 time_t ActiveRunway::requestTimeSlot(time_t eta)
46 {
47     time_t newEta;
48     time_t separation = 90;
49     bool found = false;
50     if (estimatedArrivalTimes.size() == 0) {
51         estimatedArrivalTimes.push_back(eta);
52         return eta;
53     } else {
54         TimeVectorIterator i = estimatedArrivalTimes.begin();
55         //cerr << "Checking eta slots " << eta << ": " << endl;
56         for (i = estimatedArrivalTimes.begin();
57              i != estimatedArrivalTimes.end(); i++) {
58             //cerr << "Stored time : " << (*i) << endl;
59         }
60         i = estimatedArrivalTimes.begin();
61         if ((eta + separation) < (*i)) {
62             newEta = eta;
63             found = true;
64             //cerr << "Storing at beginning" << endl;
65         }
66         while ((i != estimatedArrivalTimes.end()) && (!found)) {
67             TimeVectorIterator j = i + 1;
68             if (j == estimatedArrivalTimes.end()) {
69                 if (((*i) + separation) < eta) {
70                     //cerr << "Storing at end" << endl;
71                     newEta = eta;
72                 } else {
73                     newEta = (*i) + separation;
74                     //cerr << "Storing at end + separation" << endl;
75                 }
76             } else {
77                 if ((((*j) - (*i)) > (separation * 2))) {       // found a potential slot
78                     // now check whether this slow is usable:
79                     // 1) eta should fall between the two points
80                     //    i.e. eta > i AND eta < j
81                     //
82                     //cerr << "Found potential slot after " << (*i) << endl;
83                     if (eta > (*i) && (eta < (*j))) {
84                         found = true;
85                         if (eta < ((*i) + separation)) {
86                             newEta = (*i) + separation;
87                             //cerr << "Using  original" << (*i) << " + separation " << endl;
88                         } else {
89                             newEta = eta;
90                             //cerr << "Using original after " << (*i) << endl;
91                         }
92                     } else if (eta < (*i)) {
93                         found = true;
94                         newEta = (*i) + separation;
95                         //cerr << "Using delayed slot after " << (*i) << endl;
96                     }
97                     /*
98                        if (((*j) - separation) < eta) {
99                        found = true;
100                        if (((*i) + separation) < eta) {
101                        newEta = eta;
102                        cerr << "Using original after " << (*i) << endl;
103                        } else {
104                        newEta = (*i) + separation;
105                        cerr << "Using  " << (*i) << " + separation " << endl;
106                        }
107                        } */
108                 }
109             }
110             i++;
111         }
112     }
113     //cerr << ". done. New ETA : " << newEta << endl;
114
115     estimatedArrivalTimes.push_back(newEta);
116     sort(estimatedArrivalTimes.begin(), estimatedArrivalTimes.end());
117     // do some housekeeping : remove any timestamps that are past
118     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
119     TimeVectorIterator i = estimatedArrivalTimes.begin();
120     while (i != estimatedArrivalTimes.end()) {
121         if ((*i) < now) {
122             //cerr << "Deleting timestamp " << (*i) << " (now = " << now << "). " << endl;
123             estimatedArrivalTimes.erase(i);
124             i = estimatedArrivalTimes.begin();
125         } else {
126             i++;
127         }
128     }
129     return newEta;
130 }
131
132 /***************************************************************************
133  * FGTrafficRecord
134  **************************************************************************/
135 FGTrafficRecord::FGTrafficRecord():
136 id(0), waitsForId(0),
137 currentPos(0),
138 leg(0),
139 frequencyId(0),
140 state(0),
141 allowTransmission(true),
142 latitude(0), longitude(0), heading(0), speed(0), altitude(0), radius(0)
143 {
144 }
145
146 void FGTrafficRecord::setPositionAndIntentions(int pos,
147                                                FGAIFlightPlan * route)
148 {
149
150     currentPos = pos;
151     if (intentions.size()) {
152         intVecIterator i = intentions.begin();
153         if ((*i) != pos) {
154             SG_LOG(SG_GENERAL, SG_ALERT,
155                    "Error in FGTrafficRecord::setPositionAndIntentions");
156             //cerr << "Pos : " << pos << " Curr " << *(intentions.begin())  << endl;
157             for (intVecIterator i = intentions.begin();
158                  i != intentions.end(); i++) {
159                 //cerr << (*i) << " ";
160             }
161             //cerr << endl;
162         }
163         intentions.erase(i);
164     } else {
165         //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
166         int size = route->getNrOfWayPoints();
167         //cerr << "Setting pos" << pos << " ";
168         //cerr << "setting intentions ";
169         for (int i = 0; i < size; i++) {
170             int val = route->getRouteIndex(i);
171             //cerr << val<< " ";
172             if ((val) && (val != pos)) {
173                 intentions.push_back(val);
174                 //cerr << "[set] ";
175             }
176         }
177         //cerr << endl;
178         //while (route->next(&legNr, &routeNr)) {
179         //intentions.push_back(routeNr);
180         //}
181         //route->rewind(currentPos);
182     }
183     //exit(1);
184 }
185
186 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other)
187 {
188     bool result = false;
189     //cerr << "Start check 1" << endl;
190     if (currentPos == other.currentPos) {
191         //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
192         result = true;
193     }
194     //  else if (other.intentions.size()) 
195     //     {
196     //       cerr << "Start check 2" << endl;
197     //       intVecIterator i = other.intentions.begin(); 
198     //       while (!((i == other.intentions.end()) || ((*i) == currentPos)))
199     //     i++;
200     //       if (i != other.intentions.end()) {
201     //     cerr << "Check Position and intentions: current matches other.intentions" << endl;
202     //     result = true;
203     //       }
204     else if (intentions.size()) {
205         //cerr << "Start check 3" << endl;
206         intVecIterator i = intentions.begin();
207         //while (!((i == intentions.end()) || ((*i) == other.currentPos)))
208         while (i != intentions.end()) {
209             if ((*i) == other.currentPos) {
210                 break;
211             }
212             i++;
213         }
214         if (i != intentions.end()) {
215             //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
216             result = true;
217         }
218     }
219     //cerr << "Done !!" << endl;
220     return result;
221 }
222
223 void FGTrafficRecord::setPositionAndHeading(double lat, double lon,
224                                             double hdg, double spd,
225                                             double alt)
226 {
227     latitude = lat;
228     longitude = lon;
229     heading = hdg;
230     speed = spd;
231     altitude = alt;
232 }
233
234 int FGTrafficRecord::crosses(FGGroundNetwork * net,
235                              FGTrafficRecord & other)
236 {
237     if (checkPositionAndIntentions(other)
238         || (other.checkPositionAndIntentions(*this)))
239         return -1;
240     intVecIterator i, j;
241     int currentTargetNode = 0, otherTargetNode = 0;
242     if (currentPos > 0)
243         currentTargetNode = net->findSegment(currentPos)->getEnd()->getIndex(); // OKAY,... 
244     if (other.currentPos > 0)
245         otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex();     // OKAY,...
246     if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
247         return currentTargetNode;
248     if (intentions.size()) {
249         for (i = intentions.begin(); i != intentions.end(); i++) {
250             if ((*i) > 0) {
251                 if ((currentTargetNode ==
252                      net->findSegment(*i)->getEnd()->getIndex())) {
253                     //cerr << "Current crosses at " << currentTargetNode <<endl;
254                     return currentTargetNode;
255                 }
256             }
257         }
258     }
259     if (other.intentions.size()) {
260         for (i = other.intentions.begin(); i != other.intentions.end();
261              i++) {
262             if ((*i) > 0) {
263                 if (otherTargetNode ==
264                     net->findSegment(*i)->getEnd()->getIndex()) {
265                     //cerr << "Other crosses at " << currentTargetNode <<endl;
266                     return otherTargetNode;
267                 }
268             }
269         }
270     }
271     if (intentions.size() && other.intentions.size()) {
272         for (i = intentions.begin(); i != intentions.end(); i++) {
273             for (j = other.intentions.begin(); j != other.intentions.end();
274                  j++) {
275                 //cerr << "finding segment " << *i << " and " << *j << endl;
276                 if (((*i) > 0) && ((*j) > 0)) {
277                     currentTargetNode =
278                         net->findSegment(*i)->getEnd()->getIndex();
279                     otherTargetNode =
280                         net->findSegment(*j)->getEnd()->getIndex();
281                     if (currentTargetNode == otherTargetNode) {
282                         //cerr << "Routes will cross at " << currentTargetNode << endl;
283                         return currentTargetNode;
284                     }
285                 }
286             }
287         }
288     }
289     return -1;
290 }
291
292 bool FGTrafficRecord::onRoute(FGGroundNetwork * net,
293                               FGTrafficRecord & other)
294 {
295     int node = -1, othernode = -1;
296     if (currentPos > 0)
297         node = net->findSegment(currentPos)->getEnd()->getIndex();
298     if (other.currentPos > 0)
299         othernode =
300             net->findSegment(other.currentPos)->getEnd()->getIndex();
301     if ((node == othernode) && (node != -1))
302         return true;
303     if (other.intentions.size()) {
304         for (intVecIterator i = other.intentions.begin();
305              i != other.intentions.end(); i++) {
306             if (*i > 0) {
307                 othernode = net->findSegment(*i)->getEnd()->getIndex();
308                 if ((node == othernode) && (node > -1))
309                     return true;
310             }
311         }
312     }
313     //if (other.currentPos > 0)
314     //  othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
315     //if (intentions.size())
316     //  {
317     //    for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
318     //    {
319     //      if (*i > 0) 
320     //        {
321     //          node = net->findSegment(*i)->getEnd()->getIndex();
322     //          if ((node == othernode) && (node > -1))
323     //            return true;
324     //        }
325     //    }
326     //  }
327     return false;
328 }
329
330
331 bool FGTrafficRecord::isOpposing(FGGroundNetwork * net,
332                                  FGTrafficRecord & other, int node)
333 {
334     // Check if current segment is the reverse segment for the other aircraft
335     FGTaxiSegment *opp;
336     //cerr << "Current segment " << currentPos << endl;
337     if ((currentPos > 0) && (other.currentPos > 0)) {
338         opp = net->findSegment(currentPos)->opposite();
339         if (opp) {
340             if (opp->getIndex() == other.currentPos)
341                 return true;
342         }
343
344         for (intVecIterator i = intentions.begin(); i != intentions.end();
345              i++) {
346             if ((opp = net->findSegment(other.currentPos)->opposite())) {
347                 if ((*i) > 0)
348                     if (opp->getIndex() ==
349                         net->findSegment(*i)->getIndex()) {
350                         if (net->findSegment(*i)->getStart()->getIndex() ==
351                             node) {
352                             {
353                                 //cerr << "Found the node " << node << endl;
354                                 return true;
355                             }
356                         }
357                     }
358             }
359             if (other.intentions.size()) {
360                 for (intVecIterator j = other.intentions.begin();
361                      j != other.intentions.end(); j++) {
362                     // cerr << "Current segment 1 " << (*i) << endl;
363                     if ((*i) > 0) {
364                         if ((opp = net->findSegment(*i)->opposite())) {
365                             if (opp->getIndex() ==
366                                 net->findSegment(*j)->getIndex()) {
367                                 //cerr << "Nodes " << net->findSegment(*i)->getIndex()
368                                 //   << " and  " << net->findSegment(*j)->getIndex()
369                                 //   << " are opposites " << endl;
370                                 if (net->findSegment(*i)->getStart()->
371                                     getIndex() == node) {
372                                     {
373                                         //cerr << "Found the node " << node << endl;
374                                         return true;
375                                     }
376                                 }
377                             }
378                         }
379                     }
380                 }
381             }
382         }
383     }
384     return false;
385 }
386
387 void FGTrafficRecord::setSpeedAdjustment(double spd)
388 {
389     instruction.setChangeSpeed(true);
390     instruction.setSpeed(spd);
391 }
392
393 void FGTrafficRecord::setHeadingAdjustment(double heading)
394 {
395     instruction.setChangeHeading(true);
396     instruction.setHeading(heading);
397 }
398
399 bool FGTrafficRecord::pushBackAllowed()
400 {
401     double course, az2, dist;
402     SGGeod curr(SGGeod::fromDegM(getLongitude(),
403                                  getLatitude(), getAltitude()));
404
405     double userLatitude = fgGetDouble("/position/latitude-deg");
406     double userLongitude = fgGetDouble("/position/longitude-deg");
407     SGGeod user(SGGeod::fromDeg(userLongitude, userLatitude));
408     SGGeodesy::inverse(curr, user, course, az2, dist);
409     //cerr << "Distance to user : " << dist << endl;
410     return (dist > 250);
411
412 }
413
414
415
416 /***************************************************************************
417  * FGATCInstruction
418  *
419  **************************************************************************/
420 FGATCInstruction::FGATCInstruction()
421 {
422     holdPattern = false;
423     holdPosition = false;
424     changeSpeed = false;
425     changeHeading = false;
426     changeAltitude = false;
427     resolveCircularWait = false;
428
429     speed = 0;
430     heading = 0;
431     alt = 0;
432 }
433
434
435 bool FGATCInstruction::hasInstruction()
436 {
437     return (holdPattern || holdPosition || changeSpeed || changeHeading
438             || changeAltitude || resolveCircularWait);
439 }
440
441 /***************************************************************************
442  * FGATCController
443  *
444  **************************************************************************/
445
446
447
448
449 FGATCController::FGATCController()
450 {
451     cerr << "running FGATController constructor" << endl;
452     dt_count = 0;
453     available = true;
454     lastTransmission = 0;
455     initialized = false;
456 }
457
458 FGATCController::~FGATCController()
459 {
460      cerr << "running FGATController destructor" << endl;
461 }
462
463 string FGATCController::getGateName(FGAIAircraft * ref)
464 {
465     return ref->atGate();
466 }
467
468 bool FGATCController::isUserAircraft(FGAIAircraft* ac) 
469
470     return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false; 
471 };
472
473 void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId,
474                                AtcMsgDir msgDir)
475 {
476     string sender, receiver;
477     int stationFreq = 0;
478     int taxiFreq = 0;
479     int freqId = 0;
480     string atisInformation;
481     string text;
482     string taxiFreqStr;
483     double heading = 0;
484     string activeRunway;
485     string fltType;
486     string rwyClass;
487     string SID;
488     string transponderCode;
489     FGAIFlightPlan *fp;
490     string fltRules;
491
492     //double commFreqD;
493     sender = rec->getAircraft()->getTrafficRef()->getCallSign();
494     //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
495     switch (rec->getLeg()) {
496     case 2:
497     case 3:
498         freqId = rec->getNextFrequency();
499         stationFreq =
500             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
501             getDynamics()->getGroundFrequency(rec->getLeg() + freqId);
502         taxiFreq =
503             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
504             getDynamics()->getGroundFrequency(3);
505         receiver =
506             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
507             getName() + "-Ground";
508         atisInformation =
509             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
510             getDynamics()->getAtisInformation();
511         break;
512     case 4:
513         receiver =
514             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
515             getName() + "-Tower";
516         break;
517     }
518     // Swap sender and receiver value in case of a ground to air transmission
519     if (msgDir == ATC_GROUND_TO_AIR) {
520         string tmp = sender;
521         sender = receiver;
522         receiver = tmp;
523     }
524     switch (msgId) {
525     case MSG_ANNOUNCE_ENGINE_START:
526         text = sender + ". Ready to Start up";
527         break;
528     case MSG_REQUEST_ENGINE_START:
529         text =
530             receiver + ", This is " + sender + ". Position " +
531             getGateName(rec->getAircraft()) + ". Information " +
532             atisInformation + ". " +
533             rec->getAircraft()->getTrafficRef()->getFlightRules() +
534             " to " +
535             rec->getAircraft()->getTrafficRef()->getArrivalAirport()->
536             getName() + ". Request start-up";
537         break;
538         // Acknowledge engine startup permission
539         // Assign departure runway
540         // Assign SID, if necessery (TODO)
541     case MSG_PERMIT_ENGINE_START:
542         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
543
544         heading = rec->getAircraft()->getTrafficRef()->getCourse();
545         fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
546         rwyClass =
547             rec->getAircraft()->GetFlightPlan()->
548             getRunwayClassFromTrafficType(fltType);
549
550         rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
551             getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
552                                            heading);
553         rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
554         fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
555             getDynamics()->getSID(activeRunway, heading);
556         rec->getAircraft()->GetFlightPlan()->setSID(fp);
557         if (fp) {
558             SID = fp->getName() + " departure";
559         } else {
560             SID = "fly runway heading ";
561         }
562         //snprintf(buffer, 7, "%3.2f", heading);
563         fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
564         transponderCode = genTransponderCode(fltRules);
565         rec->getAircraft()->SetTransponderCode(transponderCode);
566         text =
567             receiver + ". Start-up approved. " + atisInformation +
568             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
569             transponderCode + ". " +
570             "For push-back and taxi clearance call " + taxiFreqStr + ". " +
571             sender + " control.";
572         break;
573     case MSG_DENY_ENGINE_START:
574         text = receiver + ". Standby";
575         break;
576     case MSG_ACKNOWLEDGE_ENGINE_START:
577         fp = rec->getAircraft()->GetFlightPlan()->getSID();
578         if (fp) {
579             SID =
580                 rec->getAircraft()->GetFlightPlan()->getSID()->getName() +
581                 " departure";
582         } else {
583             SID = "fly runway heading ";
584         }
585         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
586         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
587         transponderCode = rec->getAircraft()->GetTransponderCode();
588         text =
589             receiver + ". Start-up approved. " + atisInformation +
590             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
591             transponderCode + ". " +
592             "For push-back and taxi clearance call " + taxiFreqStr + ". " +
593             sender;
594         break;
595     case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
596         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
597         text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
598         break;
599     case MSG_INITIATE_CONTACT:
600         text = receiver + ". With you. " + sender;
601         break;
602     case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
603         text = receiver + ". Roger. " + sender;
604         break;
605     case MSG_REQUEST_PUSHBACK_CLEARANCE:
606         text = receiver + ". Request push-back. " + sender;
607         break;
608     case MSG_PERMIT_PUSHBACK_CLEARANCE:
609         text = receiver + ". Push-back approved. " + sender;
610         break;
611     case MSG_HOLD_PUSHBACK_CLEARANCE:
612         text = receiver + ". Standby. " + sender;
613         break;
614     case MSG_REQUEST_TAXI_CLEARANCE:
615         text = receiver + ". Ready to Taxi. " + sender;
616         break;
617     case MSG_ISSUE_TAXI_CLEARANCE:
618         text = receiver + ". Cleared to taxi. " + sender;
619         break;
620     case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
621         text = receiver + ". Cleared to taxi. " + sender;
622         break;
623     case MSG_HOLD_POSITION:
624         text = receiver + ". Hold Position. " + sender;
625         break;
626     case MSG_ACKNOWLEDGE_HOLD_POSITION:
627         text = receiver + ". Holding Position. " + sender;
628         break;
629     case MSG_RESUME_TAXI:
630         text = receiver + ". Resume Taxiing. " + sender;
631         break;
632     case MSG_ACKNOWLEDGE_RESUME_TAXI:
633         text = receiver + ". Continuing Taxi. " + sender;
634         break;
635     default:
636         text = text + sender + ". Transmitting unknown Message";
637         break;
638     }
639     double onBoardRadioFreq0 =
640         fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
641     double onBoardRadioFreq1 =
642         fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
643     int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
644     int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
645     //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl;
646
647     // Display ATC message only when one of the radios is tuned
648     // the relevant frequency.
649     // Note that distance attenuation is currently not yet implemented
650     if ((onBoardRadioFreqI0 == stationFreq)
651         || (onBoardRadioFreqI1 == stationFreq)) {
652         if (rec->allowTransmissions()) {
653             fgSetString("/sim/messages/atc", text.c_str());
654         }
655     }
656 }
657
658 string FGATCController::formatATCFrequency3_2(int freq)
659 {
660     char buffer[7];
661     snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
662     return string(buffer);
663 }
664
665 // TODO: Set transponder codes according to real-world routes.
666 // The current version just returns a random string of four octal numbers. 
667 string FGATCController::genTransponderCode(string fltRules)
668 {
669     if (fltRules == "VFR") {
670         return string("1200");
671     } else {
672         char buffer[5];
673         snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
674                  rand() % 8);
675         return string(buffer);
676     }
677 }
678
679 void FGATCController::init() 
680 {
681    if (!initialized) {
682        FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
683        mgr->addController(this);
684        initialized = true;
685     }
686 }
687
688 /***************************************************************************
689  * class FGTowerController
690  *
691  **************************************************************************/
692 FGTowerController::FGTowerController():
693 FGATCController()
694 {
695 }
696
697 // 
698 void FGTowerController::announcePosition(int id,
699                                          FGAIFlightPlan * intendedRoute,
700                                          int currentPosition, double lat,
701                                          double lon, double heading,
702                                          double speed, double alt,
703                                          double radius, int leg,
704                                          FGAIAircraft * ref)
705 {
706     init();
707     TrafficVectorIterator i = activeTraffic.begin();
708     // Search whether the current id alread has an entry
709     // This might be faster using a map instead of a vector, but let's start by taking a safe route
710     if (activeTraffic.size()) {
711         //while ((i->getId() != id) && i != activeTraffic.end()) {
712         while (i != activeTraffic.end()) {
713             if (i->getId() == id) {
714                 break;
715             }
716             i++;
717         }
718     }
719     // Add a new TrafficRecord if no one exsists for this aircraft.
720     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
721         FGTrafficRecord rec;
722         rec.setId(id);
723
724         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
725         rec.setRunway(intendedRoute->getRunway());
726         rec.setLeg(leg);
727         //rec.setCallSign(callsign);
728         rec.setAircraft(ref);
729         activeTraffic.push_back(rec);
730     } else {
731         i->setPositionAndHeading(lat, lon, heading, speed, alt);
732     }
733 }
734
735 void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
736                                                   double heading, double speed, double alt,
737                                                   double dt)
738 {
739     TrafficVectorIterator i = activeTraffic.begin();
740     // Search whether the current id has an entry
741     // This might be faster using a map instead of a vector, but let's start by taking a safe route
742     TrafficVectorIterator current, closest;
743     if (activeTraffic.size()) {
744         //while ((i->getId() != id) && i != activeTraffic.end()) {
745         while (i != activeTraffic.end()) {
746             if (i->getId() == id) {
747                 break;
748             }
749             i++;
750         }
751     }
752 //    // update position of the current aircraft
753     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
754         SG_LOG(SG_GENERAL, SG_ALERT,
755                "AI error: updating aircraft without traffic record");
756     } else {
757         i->setPositionAndHeading(lat, lon, heading, speed, alt);
758         current = i;
759     }
760     setDt(getDt() + dt);
761
762 //    // see if we already have a clearance record for the currently active runway
763     ActiveRunwayVecIterator rwy = activeRunways.begin();
764     // again, a map might be more efficient here
765     if (activeRunways.size()) {
766         //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
767         while (rwy != activeRunways.end()) {
768             if (rwy->getRunwayName() == current->getRunway()) {
769                 break;
770             }
771             rwy++;
772         }
773     }
774     if (rwy == activeRunways.end()) {
775         ActiveRunway aRwy(current->getRunway(), id);
776         activeRunways.push_back(aRwy);  // Since there are no clearance records for this runway yet
777         current->setHoldPosition(false);        // Clear the current aircraft to continue
778     } else {
779         // Okay, we have a clearance record for this runway, so check
780         // whether the clearence ID matches that of the current aircraft
781         if (id == rwy->getCleared()) {
782             current->setHoldPosition(false);
783         } else {
784             current->setHoldPosition(true);
785         }
786     }
787 }
788
789
790 void FGTowerController::signOff(int id)
791 {
792     TrafficVectorIterator i = activeTraffic.begin();
793     // Search search if the current id alread has an entry
794     // This might be faster using a map instead of a vector, but let's start by taking a safe route
795     if (activeTraffic.size()) {
796         //while ((i->getId() != id) && i != activeTraffic.end()) {
797         while (i != activeTraffic.end()) {
798             if (i->getId() == id) {
799                 break;
800             }
801             i++;
802         }
803     }
804     // If this aircraft has left the runway, we can clear the departure record for this runway
805     ActiveRunwayVecIterator rwy = activeRunways.begin();
806     if (activeRunways.size()) {
807         //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
808         while (rwy != activeRunways.end()) {
809             if (rwy->getRunwayName() == i->getRunway()) {
810                 break;
811             }
812             rwy++;
813         }
814         if (rwy != activeRunways.end()) {
815             rwy = activeRunways.erase(rwy);
816         } else {
817             SG_LOG(SG_GENERAL, SG_ALERT,
818                    "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
819         }
820     }
821     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
822         SG_LOG(SG_GENERAL, SG_ALERT,
823                "AI error: Aircraft without traffic record is signing off from tower");
824     } else {
825         i = activeTraffic.erase(i);
826     }
827 }
828
829 // NOTE:
830 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
831 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
832 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
833 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
834 // Note that this function is probably obsolete
835 bool FGTowerController::hasInstruction(int id)
836 {
837     TrafficVectorIterator i = activeTraffic.begin();
838     // Search search if the current id has an entry
839     // This might be faster using a map instead of a vector, but let's start by taking a safe route
840     if (activeTraffic.size()) {
841         //while ((i->getId() != id) && i != activeTraffic.end()) {
842         while (i != activeTraffic.end()) {
843             if (i->getId() == id) {
844                 break;
845             }
846             i++;
847         }
848     }
849     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
850         SG_LOG(SG_GENERAL, SG_ALERT,
851                "AI error: checking ATC instruction for aircraft without traffic record");
852     } else {
853         return i->hasInstruction();
854     }
855     return false;
856 }
857
858
859 FGATCInstruction FGTowerController::getInstruction(int id)
860 {
861     TrafficVectorIterator i = activeTraffic.begin();
862     // Search search if the current id has an entry
863     // This might be faster using a map instead of a vector, but let's start by taking a safe route
864     if (activeTraffic.size()) {
865         //while ((i->getId() != id) && i != activeTraffic.end()) {
866         while (i != activeTraffic.end()) {
867             if (i->getId() == id) {
868                 break;
869             }
870             i++;
871         }
872     }
873     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
874         SG_LOG(SG_GENERAL, SG_ALERT,
875                "AI error: requesting ATC instruction for aircraft without traffic record");
876     } else {
877         return i->getInstruction();
878     }
879     return FGATCInstruction();
880 }
881
882 /***************************************************************************
883  * class FGStartupController
884  *
885  **************************************************************************/
886 FGStartupController::FGStartupController():
887 FGATCController()
888 {
889 }
890
891 void FGStartupController::announcePosition(int id,
892                                            FGAIFlightPlan * intendedRoute,
893                                            int currentPosition, double lat,
894                                            double lon, double heading,
895                                            double speed, double alt,
896                                            double radius, int leg,
897                                            FGAIAircraft * ref)
898 {
899     init();
900     TrafficVectorIterator i = activeTraffic.begin();
901     // Search whether the current id alread has an entry
902     // This might be faster using a map instead of a vector, but let's start by taking a safe route
903     if (activeTraffic.size()) {
904         //while ((i->getId() != id) && i != activeTraffic.end()) {
905         while (i != activeTraffic.end()) {
906             if (i->getId() == id) {
907                 break;
908             }
909             i++;
910         }
911     }
912     // Add a new TrafficRecord if no one exsists for this aircraft.
913     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
914         FGTrafficRecord rec;
915         rec.setId(id);
916
917         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
918         rec.setRunway(intendedRoute->getRunway());
919         rec.setLeg(leg);
920         //rec.setCallSign(callsign);
921         rec.setAircraft(ref);
922         rec.setHoldPosition(true);
923         activeTraffic.push_back(rec);
924     } else {
925         i->setPositionAndHeading(lat, lon, heading, speed, alt);
926
927     }
928 }
929
930 // NOTE:
931 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
932 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
933 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
934 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
935 // Note that this function is probably obsolete
936 bool FGStartupController::hasInstruction(int id)
937 {
938     TrafficVectorIterator i = activeTraffic.begin();
939     // Search search if the current id has an entry
940     // This might be faster using a map instead of a vector, but let's start by taking a safe route
941     if (activeTraffic.size()) {
942         //while ((i->getId() != id) && i != activeTraffic.end()) {
943         while (i != activeTraffic.end()) {
944             if (i->getId() == id) {
945                 break;
946             }
947             i++;
948         }
949     }
950     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
951         SG_LOG(SG_GENERAL, SG_ALERT,
952                "AI error: checking ATC instruction for aircraft without traffic record");
953     } else {
954         return i->hasInstruction();
955     }
956     return false;
957 }
958
959
960 FGATCInstruction FGStartupController::getInstruction(int id)
961 {
962     TrafficVectorIterator i = activeTraffic.begin();
963     // Search search if the current id has an entry
964     // This might be faster using a map instead of a vector, but let's start by taking a safe route
965     if (activeTraffic.size()) {
966         //while ((i->getId() != id) && i != activeTraffic.end()) {
967         while (i != activeTraffic.end()) {
968             if (i->getId() == id) {
969                 break;
970             }
971             i++;
972         }
973     }
974     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
975         SG_LOG(SG_GENERAL, SG_ALERT,
976                "AI error: requesting ATC instruction for aircraft without traffic record");
977     } else {
978         return i->getInstruction();
979     }
980     return FGATCInstruction();
981 }
982
983 void FGStartupController::signOff(int id)
984 {
985     TrafficVectorIterator i = activeTraffic.begin();
986     // Search search if the current id alread has an entry
987     // This might be faster using a map instead of a vector, but let's start by taking a safe route
988     if (activeTraffic.size()) {
989         //while ((i->getId() != id) && i != activeTraffic.end()) {
990         while (i != activeTraffic.end()) {
991             if (i->getId() == id) {
992                 break;
993             }
994             i++;
995         }
996     }
997     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
998         SG_LOG(SG_GENERAL, SG_ALERT,
999                "AI error: Aircraft without traffic record is signing off from tower");
1000     } else {
1001         i = activeTraffic.erase(i);
1002     }
1003 }
1004
1005 bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId,
1006                                AtcMsgDir msgDir)
1007 {
1008     int state = i->getState();
1009     if ((state == st) && available) {
1010         if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
1011             cerr << "Checking state " << st << " for " << i->getAircraft()->getCallSign() << endl;
1012             static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
1013             int n = trans_num->getIntValue();
1014             if (n >= 0) {
1015                 trans_num->setIntValue(-1);
1016                  // PopupCallback(n);
1017                  cerr << "Selected transmission message" << n << endl;
1018             } else {
1019                 return false;
1020             }
1021         }
1022         if (now > startTime) {
1023             //cerr << "Transmitting startup msg" << endl;
1024             transmit(&(*i), msgId, msgDir);
1025             i->updateState();
1026             lastTransmission = now;
1027             available = false;
1028             return true;
1029         }
1030     }
1031     return false;
1032 }
1033
1034 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
1035                                                     double heading, double speed, double alt,
1036                                                     double dt)
1037 {
1038     TrafficVectorIterator i = activeTraffic.begin();
1039     // Search search if the current id has an entry
1040     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1041     TrafficVectorIterator current, closest;
1042     if (activeTraffic.size()) {
1043         //while ((i->getId() != id) && i != activeTraffic.end()) {
1044         while (i != activeTraffic.end()) {
1045             if (i->getId() == id) {
1046                 break;
1047             }
1048             i++;
1049         }
1050     }
1051 //    // update position of the current aircraft
1052
1053     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1054         SG_LOG(SG_GENERAL, SG_ALERT,
1055                "AI error: updating aircraft without traffic record");
1056     } else {
1057         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1058         current = i;
1059     }
1060     setDt(getDt() + dt);
1061
1062     int state = i->getState();
1063
1064     // The user controlled aircraft should have crased here, because it doesn't have a traffic reference. 
1065     // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
1066     time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
1067     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1068     //cerr << i->getAircraft()->getTrafficRef()->getCallSign() 
1069     //     << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1070     //     << " at parking " << getGateName(i->getAircraft()) << endl;
1071
1072     if ((now - lastTransmission) > 3 + (rand() % 15)) {
1073         available = true;
1074     }
1075
1076     checkTransmissionState(0, now, (startTime + 0  ), i, MSG_ANNOUNCE_ENGINE_START,                     ATC_AIR_TO_GROUND);
1077     checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START,                      ATC_AIR_TO_GROUND);
1078     checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START,                       ATC_GROUND_TO_AIR);
1079     checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START,                  ATC_AIR_TO_GROUND);
1080     if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,       ATC_AIR_TO_GROUND)) {
1081         i->nextFrequency();
1082     }
1083     checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT,                          ATC_AIR_TO_GROUND);
1084     checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT,              ATC_GROUND_TO_AIR);
1085
1086
1087     /*
1088     if ((state == 0) && available) {
1089         if (now > startTime) {
1090             //cerr << "Transmitting startup msg" << endl;
1091             transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
1092             i->updateState();
1093             lastTransmission = now;
1094             available = false;
1095         }
1096     }
1097     if ((state == 1) && available) {
1098         if (now > startTime + 60) {
1099             transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
1100             i->updateState();
1101             lastTransmission = now;
1102             available = false;
1103         }
1104     }
1105     if ((state == 2) && available) {
1106         if (now > startTime + 80) {
1107             transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
1108             i->updateState();
1109             lastTransmission = now;
1110             available = false;
1111         }
1112     }
1113     if ((state == 3) && available) {
1114         if (now > startTime + 100) {
1115             transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START,
1116                      ATC_AIR_TO_GROUND);
1117             i->updateState();
1118             lastTransmission = now;
1119             available = false;
1120         }
1121     }
1122     // Note: The next four stages are only necessesary when Startup control is
1123     //  on a different frequency, compared to ground control
1124     if ((state == 4) && available) {
1125         if (now > startTime + 130) {
1126             transmit(&(*i), MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,
1127                      ATC_AIR_TO_GROUND);
1128             i->updateState();
1129             i->nextFrequency();
1130             lastTransmission = now;
1131             available = false;
1132         }
1133     }
1134     if ((state == 5) && available) {
1135         if (now > startTime + 140) {
1136             transmit(&(*i), MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND);
1137             i->updateState();
1138             lastTransmission = now;
1139             available = false;
1140         }
1141     }
1142     if ((state == 6) && available) {
1143         if (now > startTime + 150) {
1144             transmit(&(*i), MSG_ACKNOWLEDGE_INITIATE_CONTACT,
1145                      ATC_GROUND_TO_AIR);
1146             i->updateState();
1147             lastTransmission = now;
1148             available = false;
1149         }
1150     }
1151
1152     // TODO: Switch to APRON control and request pushback Clearance.
1153     // Get Push back clearance
1154     if ((state == 7) && available) {
1155         if (now > startTime + 180) {
1156             transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE,
1157                      ATC_AIR_TO_GROUND);
1158             i->updateState();
1159             lastTransmission = now;
1160             available = false;
1161         }
1162     } */
1163     if ((state == 8) && available) {
1164         if (now > startTime + 200) {
1165             if (i->pushBackAllowed()) {
1166                 i->allowRepeatedTransmissions();
1167                 transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE,
1168                          ATC_GROUND_TO_AIR);
1169                 i->updateState();
1170             } else {
1171                 transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE,
1172                          ATC_GROUND_TO_AIR);
1173                 i->suppressRepeatedTransmissions();
1174             }
1175             lastTransmission = now;
1176             available = false;
1177         }
1178     }
1179     if ((state == 9) && available) {
1180         i->setHoldPosition(false);
1181     }
1182 }
1183
1184
1185 /***************************************************************************
1186  * class FGApproachController
1187  *
1188  **************************************************************************/
1189 FGApproachController::FGApproachController():
1190 FGATCController()
1191 {
1192 }
1193
1194 // 
1195 void FGApproachController::announcePosition(int id,
1196                                             FGAIFlightPlan * intendedRoute,
1197                                             int currentPosition,
1198                                             double lat, double lon,
1199                                             double heading, double speed,
1200                                             double alt, double radius,
1201                                             int leg, FGAIAircraft * ref)
1202 {
1203     init();
1204     TrafficVectorIterator i = activeTraffic.begin();
1205     // Search whether the current id alread has an entry
1206     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1207     if (activeTraffic.size()) {
1208         //while ((i->getId() != id) && i != activeTraffic.end()) {
1209         while (i != activeTraffic.end()) {
1210             if (i->getId() == id) {
1211                 break;
1212             }
1213             i++;
1214         }
1215     }
1216     // Add a new TrafficRecord if no one exsists for this aircraft.
1217     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1218         FGTrafficRecord rec;
1219         rec.setId(id);
1220
1221         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1222         rec.setRunway(intendedRoute->getRunway());
1223         rec.setLeg(leg);
1224         //rec.setCallSign(callsign);
1225         rec.setAircraft(ref);
1226         activeTraffic.push_back(rec);
1227     } else {
1228         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1229     }
1230 }
1231
1232 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1233                                                      double heading, double speed, double alt,
1234                                                      double dt)
1235 {
1236     TrafficVectorIterator i = activeTraffic.begin();
1237     // Search search if the current id has an entry
1238     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1239     TrafficVectorIterator current, closest;
1240     if (activeTraffic.size()) {
1241         //while ((i->getId() != id) && i != activeTraffic.end()) {
1242         while (i != activeTraffic.end()) {
1243             if (i->getId() == id) {
1244                 break;
1245             }
1246             i++;
1247         }
1248     }
1249 //    // update position of the current aircraft
1250     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1251         SG_LOG(SG_GENERAL, SG_ALERT,
1252                "AI error: updating aircraft without traffic record");
1253     } else {
1254         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1255         current = i;
1256         //cerr << "ApproachController: checking for speed" << endl;
1257         time_t time_diff =
1258             current->getAircraft()->
1259             checkForArrivalTime(string("final001"));
1260         if (time_diff > 15) {
1261             current->setSpeedAdjustment(current->getAircraft()->
1262                                         getPerformance()->vDescent() *
1263                                         1.35);
1264         } else if (time_diff > 5) {
1265             current->setSpeedAdjustment(current->getAircraft()->
1266                                         getPerformance()->vDescent() *
1267                                         1.2);
1268         } else if (time_diff < -15) {
1269             current->setSpeedAdjustment(current->getAircraft()->
1270                                         getPerformance()->vDescent() *
1271                                         0.65);
1272         } else if (time_diff < -5) {
1273             current->setSpeedAdjustment(current->getAircraft()->
1274                                         getPerformance()->vDescent() *
1275                                         0.8);
1276         } else {
1277             current->clearSpeedAdjustment();
1278         }
1279         //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1280     }
1281     setDt(getDt() + dt);
1282 }
1283
1284 void FGApproachController::signOff(int id)
1285 {
1286     TrafficVectorIterator i = activeTraffic.begin();
1287     // Search search if the current id alread has an entry
1288     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1289     if (activeTraffic.size()) {
1290         //while ((i->getId() != id) && i != activeTraffic.end()) {
1291         while (i != activeTraffic.end()) {
1292             if (i->getId() == id) {
1293                 break;
1294             }
1295             i++;
1296         }
1297     }
1298     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1299         SG_LOG(SG_GENERAL, SG_ALERT,
1300                "AI error: Aircraft without traffic record is signing off from approach");
1301     } else {
1302         i = activeTraffic.erase(i);
1303     }
1304 }
1305
1306
1307
1308
1309 bool FGApproachController::hasInstruction(int id)
1310 {
1311     TrafficVectorIterator i = activeTraffic.begin();
1312     // Search search if the current id has an entry
1313     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1314     if (activeTraffic.size()) {
1315         //while ((i->getId() != id) && i != activeTraffic.end()) {
1316         while (i != activeTraffic.end()) {
1317             if (i->getId() == id) {
1318                 break;
1319             }
1320             i++;
1321         }
1322     }
1323     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1324         SG_LOG(SG_GENERAL, SG_ALERT,
1325                "AI error: checking ATC instruction for aircraft without traffic record");
1326     } else {
1327         return i->hasInstruction();
1328     }
1329     return false;
1330 }
1331
1332
1333 FGATCInstruction FGApproachController::getInstruction(int id)
1334 {
1335     TrafficVectorIterator i = activeTraffic.begin();
1336     // Search search if the current id has an entry
1337     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1338     if (activeTraffic.size()) {
1339         //while ((i->getId() != id) && i != activeTraffic.end()) {
1340         while (i != activeTraffic.end()) {
1341             if (i->getId() == id) {
1342                 break;
1343             }
1344             i++;
1345         }
1346     }
1347     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1348         SG_LOG(SG_GENERAL, SG_ALERT,
1349                "AI error: requesting ATC instruction for aircraft without traffic record");
1350     } else {
1351         return i->getInstruction();
1352     }
1353     return FGATCInstruction();
1354 }
1355
1356
1357 ActiveRunway *FGApproachController::getRunway(string name)
1358 {
1359     ActiveRunwayVecIterator rwy = activeRunways.begin();
1360     if (activeRunways.size()) {
1361         while (rwy != activeRunways.end()) {
1362             if (rwy->getRunwayName() == name) {
1363                 break;
1364             }
1365             rwy++;
1366         }
1367     }
1368     if (rwy == activeRunways.end()) {
1369         ActiveRunway aRwy(name, 0);
1370         activeRunways.push_back(aRwy);
1371         rwy = activeRunways.end() - 1;
1372     }
1373     return &(*rwy);
1374 }