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