]> git.mxchange.org Git - flightgear.git/blob - src/ATC/trafficcontrol.cxx
7848de10e044d446971aa0e09e85c21cd4636e07
[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 "trafficcontrol.hxx"
28 #include <AIModel/AIAircraft.hxx>
29 #include <AIModel/AIFlightPlan.hxx>
30 #include <Traffic/TrafficMgr.hxx>
31 #include <Airports/groundnetwork.hxx>
32 #include <Airports/dynamics.hxx>
33
34 /***************************************************************************
35  * FGTrafficRecord
36  **************************************************************************/
37 FGTrafficRecord::FGTrafficRecord() :
38   id(0), waitsForId(0),
39   currentPos(0),
40   leg(0),
41   state(0),
42   latitude(0),
43   longitude(0), 
44    heading(0), 
45    speed(0), 
46    altitude(0), 
47    radius(0),
48    frequencyId(0),
49    allowTransmission(true) {
50 }
51
52 void FGTrafficRecord::setPositionAndIntentions(int pos, FGAIFlightPlan *route)
53 {
54  
55    currentPos = pos;
56    if (intentions.size()) {
57      intVecIterator i = intentions.begin();
58      if ((*i) != pos) {
59        SG_LOG(SG_GENERAL, SG_ALERT, "Error in FGTrafficRecord::setPositionAndIntentions");
60        //cerr << "Pos : " << pos << " Curr " << *(intentions.begin())  << endl;
61        for (intVecIterator i = intentions.begin(); i != intentions.end() ; i++) {
62         //cerr << (*i) << " ";
63        }
64        //cerr << endl;
65      }
66      intentions.erase(i);
67    } else {
68      //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
69      int size = route->getNrOfWayPoints();
70      //cerr << "Setting pos" << pos << " ";
71      //cerr << "setting intentions ";
72      for (int i = 0; i < size; i++) {
73        int val = route->getRouteIndex(i);
74        //cerr << val<< " ";
75        if ((val) && (val != pos))
76         {
77           intentions.push_back(val); 
78           //cerr << "[set] ";
79         }
80      }
81      //cerr << endl;
82      //while (route->next(&legNr, &routeNr)) {
83      //intentions.push_back(routeNr);
84      //}
85      //route->rewind(currentPos);
86    }
87    //exit(1);
88 }
89
90 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord &other)
91 {
92    bool result = false;
93    //cerr << "Start check 1" << endl;
94    if (currentPos == other.currentPos) 
95      {
96        //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
97        result = true;
98      }
99   //  else if (other.intentions.size()) 
100  //     {
101  //       cerr << "Start check 2" << endl;
102  //       intVecIterator i = other.intentions.begin(); 
103  //       while (!((i == other.intentions.end()) || ((*i) == currentPos)))
104  //     i++;
105  //       if (i != other.intentions.end()) {
106  //     cerr << "Check Position and intentions: current matches other.intentions" << endl;
107  //     result = true;
108  //       }
109    else if (intentions.size()) {
110      //cerr << "Start check 3" << endl;
111      intVecIterator i = intentions.begin(); 
112      //while (!((i == intentions.end()) || ((*i) == other.currentPos)))
113      while (i != intentions.end()) {
114        if ((*i) == other.currentPos) {
115          break;
116        }
117        i++;
118      }
119      if (i != intentions.end()) {
120        //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
121        result = true;
122      }
123    }
124    //cerr << "Done !!" << endl;
125    return result;
126 }
127
128 void FGTrafficRecord::setPositionAndHeading(double lat, double lon, double hdg, 
129                                             double spd, double alt)
130 {
131   latitude = lat;
132   longitude = lon;
133   heading = hdg;
134   speed = spd;
135   altitude = alt;
136 }
137
138 int FGTrafficRecord::crosses(FGGroundNetwork *net, FGTrafficRecord &other)
139 {
140    if (checkPositionAndIntentions(other) || (other.checkPositionAndIntentions(*this)))
141      return -1;
142    intVecIterator i, j;
143    int currentTargetNode = 0, otherTargetNode = 0;
144    if (currentPos > 0)
145      currentTargetNode = net->findSegment(currentPos      )->getEnd()->getIndex(); // OKAY,... 
146    if (other.currentPos > 0)
147      otherTargetNode   = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,...
148    if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
149      return currentTargetNode;
150    if (intentions.size())
151      {
152        for (i = intentions.begin(); i != intentions.end(); i++)
153         {
154           if ((*i) > 0) {
155             if ((currentTargetNode == net->findSegment(*i)->getEnd()->getIndex()))
156               {
157                 //cerr << "Current crosses at " << currentTargetNode <<endl;
158                 return currentTargetNode;
159               }
160           }
161         }
162      }
163    if (other.intentions.size())
164      {
165        for (i = other.intentions.begin(); i != other.intentions.end(); i++)
166         {
167           if ((*i) > 0) {
168             if (otherTargetNode == net->findSegment(*i)->getEnd()->getIndex())
169               {
170                 //cerr << "Other crosses at " << currentTargetNode <<endl;
171                 return otherTargetNode;
172               }
173           }
174         }
175      }
176    if (intentions.size() && other.intentions.size())
177      {
178        for (i = intentions.begin(); i != intentions.end(); i++) 
179         {
180           for (j = other.intentions.begin(); j != other.intentions.end(); j++)
181             {
182               //cerr << "finding segment " << *i << " and " << *j << endl;
183               if (((*i) > 0) && ((*j) > 0)) {
184                 currentTargetNode = net->findSegment(*i)->getEnd()->getIndex();
185                 otherTargetNode   = net->findSegment(*j)->getEnd()->getIndex();
186                 if (currentTargetNode == otherTargetNode) 
187                   {
188                     //cerr << "Routes will cross at " << currentTargetNode << endl;
189                     return currentTargetNode;
190                   }
191               }
192             }
193         }
194      }
195   return -1;
196 }
197
198 bool FGTrafficRecord::onRoute(FGGroundNetwork *net, FGTrafficRecord &other)
199 {
200   int node = -1, othernode = -1;
201   if (currentPos >0)
202     node = net->findSegment(currentPos)->getEnd()->getIndex();
203   if (other.currentPos > 0)
204     othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
205   if ((node == othernode) && (node != -1))
206     return true;
207   if (other.intentions.size())
208     {
209       for (intVecIterator i = other.intentions.begin(); i != other.intentions.end(); i++)
210         {
211           if (*i > 0) 
212             {
213               othernode = net->findSegment(*i)->getEnd()->getIndex();
214               if ((node == othernode) && (node > -1))
215                 return true;
216             }
217         }
218     }
219   //if (other.currentPos > 0)
220   //  othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
221   //if (intentions.size())
222   //  {
223   //    for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
224   //    {
225   //      if (*i > 0) 
226   //        {
227   //          node = net->findSegment(*i)->getEnd()->getIndex();
228   //          if ((node == othernode) && (node > -1))
229   //            return true;
230   //        }
231   //    }
232   //  }
233   return false;
234 }
235
236
237 bool FGTrafficRecord::isOpposing (FGGroundNetwork *net, FGTrafficRecord &other, int node)
238 {
239    // Check if current segment is the reverse segment for the other aircraft
240    FGTaxiSegment *opp;
241    //cerr << "Current segment " << currentPos << endl;
242    if ((currentPos > 0) && (other.currentPos > 0))
243      {
244        opp = net->findSegment(currentPos)->opposite();
245        if (opp) {
246         if (opp->getIndex() == other.currentPos)
247           return true;
248        }
249       
250        for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
251         {
252          if ((opp = net->findSegment(other.currentPos)->opposite()))
253             {
254               if ((*i) > 0)
255                 if (opp->getIndex() == net->findSegment(*i)->getIndex())
256                   {
257                     if (net->findSegment(*i)->getStart()->getIndex() == node) {
258                       {
259                         //cerr << "Found the node " << node << endl;
260                         return true;
261                       }
262                     }
263                   }
264             }
265           if (other.intentions.size())
266             {
267               for (intVecIterator j = other.intentions.begin(); j != other.intentions.end(); j++)
268                 {
269                   // cerr << "Current segment 1 " << (*i) << endl;
270                   if ((*i) > 0) {
271                     if ((opp = net->findSegment(*i)->opposite()))
272                       {
273                         if (opp->getIndex() == 
274                             net->findSegment(*j)->getIndex())
275                           {
276                             //cerr << "Nodes " << net->findSegment(*i)->getIndex()
277                             //   << " and  " << net->findSegment(*j)->getIndex()
278                             //   << " are opposites " << endl;
279                             if (net->findSegment(*i)->getStart()->getIndex() == node) {
280                               {
281                                 //cerr << "Found the node " << node << endl;
282                                 return true;
283                               }
284                             }
285                           }
286                       }
287                   }
288                 }
289             }
290         }
291      }
292    return false;
293 }
294
295 void FGTrafficRecord::setSpeedAdjustment(double spd) 
296
297   instruction.setChangeSpeed(true); 
298   instruction.setSpeed(spd); 
299 }
300
301 void FGTrafficRecord::setHeadingAdjustment(double heading) 
302
303   instruction.setChangeHeading(true);
304   instruction.setHeading(heading); 
305 }
306
307 bool FGTrafficRecord::pushBackAllowed() {
308       double course, az2,dist;
309        SGGeod curr(SGGeod::fromDegM(getLongitude(),
310                                        getLatitude(), 
311                                        getAltitude()));
312
313       double userLatitude  = fgGetDouble("/position/latitude-deg");
314       double userLongitude = fgGetDouble("/position/longitude-deg");
315       SGGeod user(SGGeod::fromDeg(userLongitude,userLatitude));
316       SGGeodesy::inverse(curr, user, course, az2, dist);
317       //cerr << "Distance to user : " << dist << endl;
318       return (dist > 250);
319
320 }
321
322
323
324 /***************************************************************************
325  * FGATCInstruction
326  *
327  **************************************************************************/
328 FGATCInstruction::FGATCInstruction()
329 {
330   holdPattern    = false; 
331   holdPosition   = false;
332   changeSpeed    = false;
333   changeHeading  = false;
334   changeAltitude = false;
335   resolveCircularWait = false;
336
337   speed   = 0;
338   heading = 0;
339   alt     = 0;
340 }
341
342
343 bool FGATCInstruction::hasInstruction()
344 {
345   return (holdPattern || holdPosition || changeSpeed || changeHeading || changeAltitude || resolveCircularWait);
346 }
347
348 /***************************************************************************
349  * FGATCController
350  *
351  **************************************************************************/
352
353
354
355
356 FGATCController::FGATCController() 
357 {
358      dt_count = 0; 
359      available = true; 
360      lastTransmission = 0;
361 }
362
363 string FGATCController::getGateName(FGAIAircraft *ref) 
364 {
365     return ref->atGate();
366 }
367
368 void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir msgDir)
369 {
370     string sender, receiver;
371     int stationFreq = 0;
372     int taxiFreq    = 0;
373     int freqId      = 0;
374     string atisInformation;
375     string text;
376     string taxiFreqStr;
377     double heading = 0;
378     string activeRunway;
379     string fltType;
380     string rwyClass;
381     string SID;
382     string transponderCode;
383     FGAIFlightPlan *fp;
384     string fltRules;
385
386     //double commFreqD;
387     sender = rec->getAircraft()->getTrafficRef()->getCallSign();
388     //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
389     switch (rec->getLeg()) {
390         case 2:
391         case 3:
392             freqId = rec->getNextFrequency();
393             stationFreq =
394             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(rec->getLeg()+freqId);
395             taxiFreq =
396             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(3);
397             receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
398             atisInformation = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getAtisInformation();
399             break;
400         case 4: 
401             receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
402             break;
403     }
404     // Swap sender and receiver value in case of a ground to air transmission
405     if (msgDir == ATC_GROUND_TO_AIR) {
406        string tmp = sender;
407        sender = receiver;
408        receiver = tmp;
409     }
410     switch (msgId) {
411           case MSG_ANNOUNCE_ENGINE_START:
412                text = sender + ". Ready to Start up";
413                break;
414           case MSG_REQUEST_ENGINE_START:
415                text = receiver + ", This is " + sender + ". Position " +getGateName(rec->getAircraft()) +
416                        ". Information " + atisInformation + ". " +
417                        rec->getAircraft()->getTrafficRef()->getFlightRules() + " to " + 
418                        rec->getAircraft()->getTrafficRef()->getArrivalAirport()->getName() + ". Request start-up";
419                break;
420           // Acknowledge engine startup permission
421           // Assign departure runway
422           // Assign SID, if necessery (TODO)
423           case MSG_PERMIT_ENGINE_START:
424                taxiFreqStr = formatATCFrequency3_2(taxiFreq);
425
426                heading = rec->getAircraft()->getTrafficRef()->getCourse();
427                fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
428                rwyClass= rec->getAircraft()->GetFlightPlan()->getRunwayClassFromTrafficType(fltType);
429
430                rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, heading);
431                rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
432                fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getSID(activeRunway, heading);
433                rec->getAircraft()->GetFlightPlan()->setSID(fp);
434                if (fp) {
435                    SID = fp->getName() + " departure";
436                } else {
437                    SID = "fly runway heading ";
438                }
439                //snprintf(buffer, 7, "%3.2f", heading);
440                fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
441                transponderCode = genTransponderCode(fltRules);
442                rec->getAircraft()->SetTransponderCode(transponderCode);
443                text = receiver + ". Start-up approved. " + atisInformation + " correct, runway " + activeRunway
444                        + ", " + SID + ", squawk " + transponderCode + ". " +
445                       "For push-back and taxi clearance call " + taxiFreqStr + ". " + sender + " control.";
446                break;
447           case MSG_DENY_ENGINE_START:
448                text = receiver + ". Standby";
449                break;
450          case MSG_ACKNOWLEDGE_ENGINE_START:
451                fp = rec->getAircraft()->GetFlightPlan()->getSID();
452                if (fp) {
453                   SID = rec->getAircraft()->GetFlightPlan()->getSID()->getName() + " departure";
454                } else {
455                   SID = "fly runway heading ";
456                }
457                taxiFreqStr = formatATCFrequency3_2(taxiFreq);
458                activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
459                transponderCode = rec->getAircraft()->GetTransponderCode();
460                text = receiver + ". Start-up approved. " + atisInformation + " correct, runway " +
461                       activeRunway + ", " + SID + ", squawk " + transponderCode + ". " +
462                       "For push-back and taxi clearance call " + taxiFreqStr + ". " + sender;
463                break;
464            case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
465                taxiFreqStr = formatATCFrequency3_2(taxiFreq);
466                text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
467                break;
468            case MSG_INITIATE_CONTACT:
469                 text = receiver + ". With you. " + sender;
470                 break;
471            case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
472                 text = receiver + ". Roger. " + sender;
473                 break;
474            case MSG_REQUEST_PUSHBACK_CLEARANCE:
475                text = receiver + ". Request push-back. " + sender;
476                break;
477            case MSG_PERMIT_PUSHBACK_CLEARANCE:
478                text = receiver + ". Push-back approved. " + sender;
479                break;
480            case MSG_HOLD_PUSHBACK_CLEARANCE:
481                 text = receiver + ". Standby. " + sender;
482                 break;
483            case MSG_REQUEST_TAXI_CLEARANCE:
484                 text = receiver + ". Ready to Taxi. " + sender;
485                 break;
486            case MSG_ISSUE_TAXI_CLEARANCE:
487                 text = receiver + ". Cleared to taxi. " + sender;
488                 break;
489            case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
490                 text = receiver + ". Cleared to taxi. " + sender;
491                 break;
492            case MSG_HOLD_POSITION:
493                 text = receiver + ". Hold Position. " + sender;
494                 break;
495            case MSG_ACKNOWLEDGE_HOLD_POSITION:
496                 text = receiver + ". Holding Position. " + sender;
497                 break;
498            case MSG_RESUME_TAXI:
499                 text = receiver + ". Resume Taxiing. " + sender;
500                 break;
501            case MSG_ACKNOWLEDGE_RESUME_TAXI:
502                 text = receiver + ". Continuing Taxi. " + sender;
503                 break;
504            default:
505                  text = text + sender + ". Transmitting unknown Message";
506                   break;
507     }
508     double onBoardRadioFreq0 = fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
509     double onBoardRadioFreq1 = fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
510     int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
511     int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
512     //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << endl;
513
514     // Display ATC message only when one of the radios is tuned
515     // the relevant frequency.
516     // Note that distance attenuation is currently not yet implemented
517     if ((onBoardRadioFreqI0 == stationFreq) || (onBoardRadioFreqI1 == stationFreq)) {
518         if (rec->allowTransmissions()) {
519             fgSetString("/sim/messages/atc", text.c_str());
520         }
521     }
522 }
523
524 string FGATCController::formatATCFrequency3_2(int freq) {
525     char buffer[7];
526     snprintf(buffer, 7, "%3.2f", ( (float) freq / 100.0) );
527     return string(buffer);
528 }
529
530 // TODO: Set transponder codes according to real-world routes.
531 // The current version just returns a random string of four octal numbers. 
532 string FGATCController::genTransponderCode(string fltRules) {
533     if (fltRules == "VFR") {
534         return string("1200");
535     } else {
536         char buffer[5];
537         snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8,rand() % 8, rand() % 8);
538         return string(buffer);
539     }
540 }
541
542 /***************************************************************************
543  * class FGTowerController
544  *
545  **************************************************************************/
546 FGTowerController::FGTowerController() :
547   FGATCController()
548 {
549 }
550
551 // 
552 void FGTowerController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
553                                          double lat, double lon, double heading, 
554                                          double speed, double alt, double radius, int leg,
555                                          FGAIAircraft *ref)
556 {
557   TrafficVectorIterator i = activeTraffic.begin();
558   // Search whether the current id alread has an entry
559   // This might be faster using a map instead of a vector, but let's start by taking a safe route
560   if (activeTraffic.size()) {
561     //while ((i->getId() != id) && i != activeTraffic.end()) {
562     while (i != activeTraffic.end()) {
563       if (i->getId() == id) {
564         break;
565       }
566       i++;
567     }
568   }
569   
570   // Add a new TrafficRecord if no one exsists for this aircraft.
571   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
572     FGTrafficRecord rec;
573     rec.setId(id);
574
575     rec.setPositionAndHeading(lat, lon, heading, speed, alt);
576     rec.setRunway(intendedRoute->getRunway());
577     rec.setLeg(leg);
578     //rec.setCallSign(callsign);
579     rec.setAircraft(ref);
580     activeTraffic.push_back(rec);
581   } else {
582     i->setPositionAndHeading(lat, lon, heading, speed, alt);
583   }
584 }
585
586 void FGTowerController::update(int id, double lat, double lon, double heading, double speed, double alt, 
587                              double dt)
588 {
589     TrafficVectorIterator i = activeTraffic.begin();
590     // Search search if the current id has an entry
591     // This might be faster using a map instead of a vector, but let's start by taking a safe route
592     TrafficVectorIterator current, closest;
593     if (activeTraffic.size()) {
594       //while ((i->getId() != id) && i != activeTraffic.end()) {
595       while (i != activeTraffic.end()) {
596         if (i->getId() == id) {
597           break;
598         }
599         i++;
600       }
601     }
602
603 //    // update position of the current aircraft
604     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
605       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
606     } else {
607       i->setPositionAndHeading(lat, lon, heading, speed, alt);
608       current = i;
609     }
610     setDt(getDt() + dt);
611
612 //    // see if we already have a clearance record for the currently active runway
613     ActiveRunwayVecIterator rwy = activeRunways.begin();
614     // again, a map might be more efficient here
615     if (activeRunways.size()) {
616       //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
617       while (rwy != activeRunways.end()) {
618         if (rwy->getRunwayName() == current->getRunway()) {
619           break;
620         }
621         rwy++;
622       }
623     }
624     if (rwy == activeRunways.end()) {
625       ActiveRunway aRwy(current->getRunway(), id);
626       activeRunways.push_back(aRwy);   // Since there are no clearance records for this runway yet
627       current->setHoldPosition(false); // Clear the current aircraft to continue
628     }
629     else {
630       // Okay, we have a clearance record for this runway, so check
631       // whether the clearence ID matches that of the current aircraft
632       if (id == rwy->getCleared()) {
633         current->setHoldPosition(false);
634       } else {
635         current->setHoldPosition(true);
636       }
637     }
638 }
639
640
641 void FGTowerController::signOff(int id) 
642 {
643   TrafficVectorIterator i = activeTraffic.begin();
644   // Search search if the current id alread has an entry
645   // This might be faster using a map instead of a vector, but let's start by taking a safe route
646     if (activeTraffic.size()) {
647       //while ((i->getId() != id) && i != activeTraffic.end()) {
648       while (i != activeTraffic.end()) {
649         if (i->getId() == id) {
650           break;
651         }
652         i++;
653       }
654     }
655     // If this aircraft has left the runway, we can clear the departure record for this runway
656     ActiveRunwayVecIterator rwy = activeRunways.begin();
657     if (activeRunways.size()) {
658       //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
659       while (rwy != activeRunways.end()) {
660         if (rwy->getRunwayName() == i->getRunway()) {
661           break;
662         }
663         rwy++;
664       }
665       if (rwy != activeRunways.end()) {
666         rwy = activeRunways.erase(rwy);
667       } else {
668         SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
669       }
670     }
671     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
672       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower");
673     } else {
674       i = activeTraffic.erase(i);
675     }
676 }
677
678 // NOTE:
679 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
680 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
681 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
682 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
683 // Note that this function is probably obsolete
684 bool FGTowerController::hasInstruction(int id)
685 {
686     TrafficVectorIterator i = activeTraffic.begin();
687     // Search search if the current id has an entry
688     // This might be faster using a map instead of a vector, but let's start by taking a safe route
689     if (activeTraffic.size()) 
690       {
691         //while ((i->getId() != id) && i != activeTraffic.end()) {
692         while (i != activeTraffic.end()) {
693           if (i->getId() == id) {
694             break;
695           }
696         i++;
697       }
698     }
699     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
700       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
701     } else {
702       return i->hasInstruction();
703     }
704   return false;
705 }
706
707
708 FGATCInstruction FGTowerController::getInstruction(int id)
709 {
710   TrafficVectorIterator i = activeTraffic.begin();
711   // Search search if the current id has an entry
712   // This might be faster using a map instead of a vector, but let's start by taking a safe route
713   if (activeTraffic.size()) {
714     //while ((i->getId() != id) && i != activeTraffic.end()) {
715     while (i != activeTraffic.end()) {
716       if (i->getId() == id) {
717         break;
718       }
719       i++;
720     }
721   }
722   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
723     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
724   } else {
725     return i->getInstruction();
726   }
727   return FGATCInstruction();
728 }
729
730 /***************************************************************************
731  * class FGStartupController
732  *
733  **************************************************************************/
734 FGStartupController::FGStartupController() :
735   FGATCController()
736 {
737 }
738
739 void FGStartupController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
740                                          double lat, double lon, double heading, 
741                                          double speed, double alt, double radius, int leg,
742                                          FGAIAircraft *ref)
743 {
744   TrafficVectorIterator i = activeTraffic.begin();
745   // Search whether the current id alread has an entry
746   // This might be faster using a map instead of a vector, but let's start by taking a safe route
747   if (activeTraffic.size()) {
748     //while ((i->getId() != id) && i != activeTraffic.end()) {
749     while (i != activeTraffic.end()) {
750       if (i->getId() == id) {
751         break;
752       }
753       i++;
754     }
755   }
756   
757   // Add a new TrafficRecord if no one exsists for this aircraft.
758   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
759     FGTrafficRecord rec;
760     rec.setId(id);
761
762     rec.setPositionAndHeading(lat, lon, heading, speed, alt);
763     rec.setRunway(intendedRoute->getRunway());
764     rec.setLeg(leg);
765     //rec.setCallSign(callsign);
766     rec.setAircraft(ref);
767     rec.setHoldPosition(true);
768     activeTraffic.push_back(rec);
769     } else {
770         i->setPositionAndHeading(lat, lon, heading, speed, alt);
771         
772   }
773 }
774
775 // NOTE:
776 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
777 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
778 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
779 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
780 // Note that this function is probably obsolete
781 bool FGStartupController::hasInstruction(int id)
782 {
783     TrafficVectorIterator i = activeTraffic.begin();
784     // Search search if the current id has an entry
785     // This might be faster using a map instead of a vector, but let's start by taking a safe route
786     if (activeTraffic.size()) 
787       {
788         //while ((i->getId() != id) && i != activeTraffic.end()) {
789         while (i != activeTraffic.end()) {
790           if (i->getId() == id) {
791             break;
792           }
793         i++;
794       }
795     }
796     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
797       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
798     } else {
799       return i->hasInstruction();
800     }
801   return false;
802 }
803
804
805 FGATCInstruction FGStartupController::getInstruction(int id)
806 {
807   TrafficVectorIterator i = activeTraffic.begin();
808   // Search search if the current id has an entry
809   // This might be faster using a map instead of a vector, but let's start by taking a safe route
810   if (activeTraffic.size()) {
811     //while ((i->getId() != id) && i != activeTraffic.end()) {
812     while (i != activeTraffic.end()) {
813       if (i->getId() == id) {
814         break;
815       }
816       i++;
817     }
818   }
819   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
820     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
821   } else {
822     return i->getInstruction();
823   }
824   return FGATCInstruction();
825 }
826
827 void FGStartupController::signOff(int id) 
828 {
829   TrafficVectorIterator i = activeTraffic.begin();
830   // Search search if the current id alread has an entry
831   // This might be faster using a map instead of a vector, but let's start by taking a safe route
832     if (activeTraffic.size()) {
833       //while ((i->getId() != id) && i != activeTraffic.end()) {
834       while (i != activeTraffic.end()) {
835         if (i->getId() == id) {
836           break;
837         }
838         i++;
839       }
840     }
841     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
842       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower");
843     } else {
844       i = activeTraffic.erase(i);
845     }
846 }
847
848 void FGStartupController::update(int id, double lat, double lon, double heading, double speed, double alt, 
849                              double dt)
850 {
851     TrafficVectorIterator i = activeTraffic.begin();
852     // Search search if the current id has an entry
853     // This might be faster using a map instead of a vector, but let's start by taking a safe route
854     TrafficVectorIterator current, closest;
855     if (activeTraffic.size()) {
856       //while ((i->getId() != id) && i != activeTraffic.end()) {
857       while (i != activeTraffic.end()) {
858         if (i->getId() == id) {
859           break;
860         }
861         i++;
862       }
863     }
864
865 //    // update position of the current aircraft
866     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
867       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
868     } else {
869       i->setPositionAndHeading(lat, lon, heading, speed, alt);
870       current = i;
871     }
872     setDt(getDt() + dt);
873
874     int state = i->getState();
875     time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
876     time_t now       = time(NULL) + fgGetLong("/sim/time/warp");
877     //cerr << i->getAircraft()->getTrafficRef()->getCallSign() 
878     //     << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
879     //     << " at parking " << getGateName(i->getAircraft()) << endl;
880
881     if ((now - lastTransmission) > 3 + (rand() % 15)) {
882         available = true;
883     }
884
885     if ((state == 0) && available) {
886         if (now > startTime) {
887              //cerr << "Transmitting startup msg" << endl;
888              transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
889              i->updateState();
890              lastTransmission = now;
891              available = false;
892         }
893     }
894     if ((state == 1) && available) {
895         if (now > startTime+60) {
896             transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
897             i->updateState();
898             lastTransmission = now;
899              available = false;
900         }
901     }
902     if ((state == 2) && available) {
903         if (now > startTime+80) {
904             transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
905             i->updateState();
906             lastTransmission = now;
907              available = false;
908         }
909     }
910     if ((state == 3) && available){
911         if (now > startTime+100) {
912             transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND);
913             i->updateState();
914             lastTransmission = now;
915              available = false;
916         }
917      }
918      // Note: The next four stages are only necessesary when Startup control is
919      //  on a different frequency, compared to ground control
920      if ((state == 4) && available){
921         if (now > startTime+130) {
922             transmit(&(*i), MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, ATC_AIR_TO_GROUND);
923             i->updateState();
924             i->nextFrequency();
925             lastTransmission = now;
926              available = false;
927         }
928      }
929      if ((state == 5) && available){
930         if (now > startTime+140) {
931             transmit(&(*i), MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND);
932             i->updateState();
933             lastTransmission = now;
934              available = false;
935         }
936      }
937      if ((state == 6) && available){
938         if (now > startTime+150) {
939             transmit(&(*i), MSG_ACKNOWLEDGE_INITIATE_CONTACT, ATC_GROUND_TO_AIR);
940             i->updateState();
941             lastTransmission = now;
942              available = false;
943         }
944      }
945
946
947      // TODO: Switch to APRON control and request pushback Clearance.
948      // Get Push back clearance
949      if ((state == 7) && available){
950         if (now > startTime+180) {
951             transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND);
952             i->updateState();
953             lastTransmission = now;
954              available = false;
955         }
956      }
957      if ((state == 8) && available){
958         if (now > startTime+200) {
959             if (i->pushBackAllowed()) {
960                  i->allowRepeatedTransmissions();
961                  transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE, ATC_GROUND_TO_AIR);
962                  i->updateState();
963             } else {
964                  transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE, ATC_GROUND_TO_AIR);
965                  i->suppressRepeatedTransmissions();
966             }
967             lastTransmission = now;
968              available = false;
969         }
970      }
971      if ((state == 9) && available){
972           i->setHoldPosition(false);
973      }
974 }