]> git.mxchange.org Git - flightgear.git/blob - src/ATC/trafficcontrol.cxx
MSVC doesn't have a round function
[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
32
33 /***************************************************************************
34  * FGTrafficRecord
35  **************************************************************************/
36 FGTrafficRecord::FGTrafficRecord() :
37   id(0), waitsForId(0),
38   currentPos(0),
39   leg(0),
40   state(0),
41   latitude(0),
42   longitude(0), 
43    heading(0), 
44    speed(0), 
45    altitude(0), 
46    radius(0) {
47 }
48
49 void FGTrafficRecord::setPositionAndIntentions(int pos, FGAIFlightPlan *route)
50 {
51  
52    currentPos = pos;
53    if (intentions.size()) {
54      intVecIterator i = intentions.begin();
55      if ((*i) != pos) {
56        SG_LOG(SG_GENERAL, SG_ALERT, "Error in FGTrafficRecord::setPositionAndIntentions");
57        //cerr << "Pos : " << pos << " Curr " << *(intentions.begin())  << endl;
58        for (intVecIterator i = intentions.begin(); i != intentions.end() ; i++) {
59         //cerr << (*i) << " ";
60        }
61        //cerr << endl;
62      }
63      intentions.erase(i);
64    } else {
65      //int legNr, routeNr;
66      //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
67      int size = route->getNrOfWayPoints();
68      //cerr << "Setting pos" << pos << " ";
69      //cerr << "setting intentions ";
70      for (int i = 0; i < size; i++) {
71        int val = route->getRouteIndex(i);
72        //cerr << val<< " ";
73        if ((val) && (val != pos))
74         {
75           intentions.push_back(val); 
76           //cerr << "[set] ";
77         }
78      }
79      //cerr << endl;
80      //while (route->next(&legNr, &routeNr)) {
81      //intentions.push_back(routeNr);
82      //}
83      //route->rewind(currentPos);
84    }
85    //exit(1);
86 }
87
88 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord &other)
89 {
90    bool result = false;
91    //cerr << "Start check 1" << endl;
92    if (currentPos == other.currentPos) 
93      {
94        //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
95        result = true;
96      }
97   //  else if (other.intentions.size()) 
98  //     {
99  //       cerr << "Start check 2" << endl;
100  //       intVecIterator i = other.intentions.begin(); 
101  //       while (!((i == other.intentions.end()) || ((*i) == currentPos)))
102  //     i++;
103  //       if (i != other.intentions.end()) {
104  //     cerr << "Check Position and intentions: current matches other.intentions" << endl;
105  //     result = true;
106  //       }
107    else if (intentions.size()) {
108      //cerr << "Start check 3" << endl;
109      intVecIterator i = intentions.begin(); 
110      //while (!((i == intentions.end()) || ((*i) == other.currentPos)))
111      while (i != intentions.end()) {
112        if ((*i) == other.currentPos) {
113          break;
114        }
115        i++;
116      }
117      if (i != intentions.end()) {
118        //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
119        result = true;
120      }
121    }
122    //cerr << "Done !!" << endl;
123    return result;
124 }
125
126 void FGTrafficRecord::setPositionAndHeading(double lat, double lon, double hdg, 
127                                             double spd, double alt)
128 {
129   latitude = lat;
130   longitude = lon;
131   heading = hdg;
132   speed = spd;
133   altitude = alt;
134 }
135
136 int FGTrafficRecord::crosses(FGGroundNetwork *net, FGTrafficRecord &other)
137 {
138    if (checkPositionAndIntentions(other) || (other.checkPositionAndIntentions(*this)))
139      return -1;
140    intVecIterator i, j;
141    int currentTargetNode = 0, otherTargetNode = 0;
142    if (currentPos > 0)
143      currentTargetNode = net->findSegment(currentPos      )->getEnd()->getIndex(); // OKAY,... 
144    if (other.currentPos > 0)
145      otherTargetNode   = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,...
146    if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
147      return currentTargetNode;
148    if (intentions.size())
149      {
150        for (i = intentions.begin(); i != intentions.end(); i++)
151         {
152           if ((*i) > 0) {
153             if ((currentTargetNode == net->findSegment(*i)->getEnd()->getIndex()))
154               {
155                 //cerr << "Current crosses at " << currentTargetNode <<endl;
156                 return currentTargetNode;
157               }
158           }
159         }
160      }
161    if (other.intentions.size())
162      {
163        for (i = other.intentions.begin(); i != other.intentions.end(); i++)
164         {
165           if ((*i) > 0) {
166             if (otherTargetNode == net->findSegment(*i)->getEnd()->getIndex())
167               {
168                 //cerr << "Other crosses at " << currentTargetNode <<endl;
169                 return otherTargetNode;
170               }
171           }
172         }
173      }
174    if (intentions.size() && other.intentions.size())
175      {
176        for (i = intentions.begin(); i != intentions.end(); i++) 
177         {
178           for (j = other.intentions.begin(); j != other.intentions.end(); j++)
179             {
180               //cerr << "finding segment " << *i << " and " << *j << endl;
181               if (((*i) > 0) && ((*j) > 0)) {
182                 currentTargetNode = net->findSegment(*i)->getEnd()->getIndex();
183                 otherTargetNode   = net->findSegment(*j)->getEnd()->getIndex();
184                 if (currentTargetNode == otherTargetNode) 
185                   {
186                     //cerr << "Routes will cross at " << currentTargetNode << endl;
187                     return currentTargetNode;
188                   }
189               }
190             }
191         }
192      }
193   return -1;
194 }
195
196 bool FGTrafficRecord::onRoute(FGGroundNetwork *net, FGTrafficRecord &other)
197 {
198   int node = -1, othernode = -1;
199   if (currentPos >0)
200     node = net->findSegment(currentPos)->getEnd()->getIndex();
201   if (other.currentPos > 0)
202     othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
203   if ((node == othernode) && (node != -1))
204     return true;
205   if (other.intentions.size())
206     {
207       for (intVecIterator i = other.intentions.begin(); i != other.intentions.end(); i++)
208         {
209           if (*i > 0) 
210             {
211               othernode = net->findSegment(*i)->getEnd()->getIndex();
212               if ((node == othernode) && (node > -1))
213                 return true;
214             }
215         }
216     }
217   //if (other.currentPos > 0)
218   //  othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
219   //if (intentions.size())
220   //  {
221   //    for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
222   //    {
223   //      if (*i > 0) 
224   //        {
225   //          node = net->findSegment(*i)->getEnd()->getIndex();
226   //          if ((node == othernode) && (node > -1))
227   //            return true;
228   //        }
229   //    }
230   //  }
231   return false;
232 }
233
234
235 bool FGTrafficRecord::isOpposing (FGGroundNetwork *net, FGTrafficRecord &other, int node)
236 {
237    // Check if current segment is the reverse segment for the other aircraft
238    FGTaxiSegment *opp;
239    //cerr << "Current segment " << currentPos << endl;
240    if ((currentPos > 0) && (other.currentPos > 0))
241      {
242        opp = net->findSegment(currentPos)->opposite();
243        if (opp) {
244         if (opp->getIndex() == other.currentPos)
245           return true;
246        }
247       
248        for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
249         {
250           if (opp = net->findSegment(other.currentPos)->opposite())
251             {
252               if ((*i) > 0)
253                 if (opp->getIndex() == net->findSegment(*i)->getIndex())
254                   {
255                     if (net->findSegment(*i)->getStart()->getIndex() == node) {
256                       {
257                         //cerr << "Found the node " << node << endl;
258                         return true;
259                       }
260                     }
261                   }
262             }
263           if (other.intentions.size())
264             {
265               for (intVecIterator j = other.intentions.begin(); j != other.intentions.end(); j++)
266                 {  
267                   // cerr << "Current segment 1 " << (*i) << endl;
268                   if ((*i) > 0) {
269                     if (opp = net->findSegment(*i)->opposite())
270                       {
271                         if (opp->getIndex() == 
272                             net->findSegment(*j)->getIndex())
273                           {
274                             //cerr << "Nodes " << net->findSegment(*i)->getIndex()
275                             //   << " and  " << net->findSegment(*j)->getIndex()
276                             //   << " are opposites " << endl;
277                             if (net->findSegment(*i)->getStart()->getIndex() == node) {
278                               {
279                                 //cerr << "Found the node " << node << endl;
280                                 return true;
281                               }
282                             }
283                           }
284                       }
285                   }
286                 }
287             }
288         }
289      }
290    return false;
291 }
292
293 void FGTrafficRecord::setSpeedAdjustment(double spd) 
294
295   instruction.setChangeSpeed(true); 
296   instruction.setSpeed(spd); 
297 }
298
299 void FGTrafficRecord::setHeadingAdjustment(double heading) 
300
301   instruction.setChangeHeading(true);
302   instruction.setHeading(heading); 
303 }
304
305
306
307 /***************************************************************************
308  * FGATCInstruction
309  *
310  **************************************************************************/
311 FGATCInstruction::FGATCInstruction()
312 {
313   holdPattern    = false; 
314   holdPosition   = false;
315   changeSpeed    = false;
316   changeHeading  = false;
317   changeAltitude = false;
318   resolveCircularWait = false;
319
320   double speed   = 0;
321   double heading = 0;
322   double alt     = 0;
323 }
324
325 bool FGATCInstruction::hasInstruction()
326 {
327   return (holdPattern || holdPosition || changeSpeed || changeHeading || changeAltitude || resolveCircularWait);
328 }
329
330 string FGATCController::getGateName(FGAIAircraft *ref) 
331 {
332     return ref->atGate();
333 }
334
335 void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir msgDir)
336 {
337     string sender, receiver;
338     int commFreq = 0;
339     //double commFreqD;
340     switch (msgDir) {
341          case ATC_AIR_TO_GROUND:
342              sender = rec->getAircraft()->getTrafficRef()->getCallSign();
343              switch (rec->getLeg()) {
344                  case 2:
345                      commFreq =
346                         rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency();
347                      receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
348                      break;
349                  case 3: 
350                      receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
351                      break;
352                  case 4: 
353                      receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
354                      break;
355              }
356              break;
357          case ATC_GROUND_TO_AIR:
358              receiver = rec->getAircraft()->getTrafficRef()->getCallSign();
359              switch (rec->getLeg()) {
360                  case 2:
361                  commFreq =
362                         rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency();
363                      sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
364                      break;
365                  case 3: 
366                      sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
367                      break;
368                  case 4: 
369                      sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
370                      break;
371              }
372              break;
373          }
374     string text;
375     switch (msgId) {
376           case MSG_ANNOUNCE_ENGINE_START:
377                text = sender + ". Ready to Start up";
378                break;
379           case MSG_REQUEST_ENGINE_START:
380                text = receiver + ", This is " + sender + ". Position " +getGateName(rec->getAircraft()) +
381                        ". Information YY." +
382                        rec->getAircraft()->getTrafficRef()->getFlightRules() + " to " + 
383                        rec->getAircraft()->getTrafficRef()->getArrivalAirport()->getName() + ". Request start-up";
384                break;
385           case MSG_PERMIT_ENGINE_START:
386                text = receiver + ". Start-up approved. YY correct, runway ZZ, AAA departure, squawk BBBB. " +
387                       "For push-back and taxi clearance call CCC.CCC. " + sender + " control.";
388                break;
389           case MSG_DENY_ENGINE_START:
390                text = receiver + ". Standby";
391                break;
392          case MSG_ACKNOWLEDGE_ENGINE_START:
393                text = receiver + ". Start-up approved. YY correct, runway ZZ, AAA departure, squawk BBBB. " +
394                       "For push-back and taxi clearance call CCC.CCC. " + sender;
395                break;
396            default:
397                break;
398     }
399     double currFreq = fgGetDouble("/instrumentation/comm/frequencies/selected-mhz");
400     int currFreqI = (int) floor(currFreq * 100 + 0.5);
401     //cerr << "Using " << currFreqI << " and " << commFreq << endl;
402     if (currFreqI == commFreq) {
403         fgSetString("/sim/messages/atc", text.c_str());
404         //cerr << "Printing Message: " << endl;
405     }
406 }
407
408
409 /***************************************************************************
410  * class FGTowerController
411  *
412  **************************************************************************/
413 FGTowerController::FGTowerController() :
414   FGATCController()
415 {
416 }
417
418 // 
419 void FGTowerController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
420                                          double lat, double lon, double heading, 
421                                          double speed, double alt, double radius, int leg,
422                                          FGAIAircraft *ref)
423 {
424   TrafficVectorIterator i = activeTraffic.begin();
425   // Search whether the current id alread has an entry
426   // This might be faster using a map instead of a vector, but let's start by taking a safe route
427   if (activeTraffic.size()) {
428     //while ((i->getId() != id) && i != activeTraffic.end()) {
429     while (i != activeTraffic.end()) {
430       if (i->getId() == id) {
431         break;
432       }
433       i++;
434     }
435   }
436   
437   // Add a new TrafficRecord if no one exsists for this aircraft.
438   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
439     FGTrafficRecord rec;
440     rec.setId(id);
441
442     rec.setPositionAndHeading(lat, lon, heading, speed, alt);
443     rec.setRunway(intendedRoute->getRunway());
444     rec.setLeg(leg);
445     //rec.setCallSign(callsign);
446     rec.setAircraft(ref);
447     activeTraffic.push_back(rec);
448   } else {
449     i->setPositionAndHeading(lat, lon, heading, speed, alt);
450   }
451 }
452
453 void FGTowerController::update(int id, double lat, double lon, double heading, double speed, double alt, 
454                              double dt)
455 {
456     TrafficVectorIterator i = activeTraffic.begin();
457     // Search search if the current id has an entry
458     // This might be faster using a map instead of a vector, but let's start by taking a safe route
459     TrafficVectorIterator current, closest;
460     if (activeTraffic.size()) {
461       //while ((i->getId() != id) && i != activeTraffic.end()) {
462       while (i != activeTraffic.end()) {
463         if (i->getId() == id) {
464           break;
465         }
466         i++;
467       }
468     }
469
470 //    // update position of the current aircraft
471     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
472       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
473     } else {
474       i->setPositionAndHeading(lat, lon, heading, speed, alt);
475       current = i;
476     }
477     setDt(getDt() + dt);
478
479 //    // see if we already have a clearance record for the currently active runway
480     ActiveRunwayVecIterator rwy = activeRunways.begin();
481     // again, a map might be more efficient here
482     if (activeRunways.size()) {
483       //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
484       while (rwy != activeRunways.end()) {
485         if (rwy->getRunwayName() == current->getRunway()) {
486           break;
487         }
488         rwy++;
489       }
490     }
491     if (rwy == activeRunways.end()) {
492       ActiveRunway aRwy(current->getRunway(), id);
493       activeRunways.push_back(aRwy);   // Since there are no clearance records for this runway yet
494       current->setHoldPosition(false); // Clear the current aircraft to continue
495     }
496     else {
497       // Okay, we have a clearance record for this runway, so check
498       // whether the clearence ID matches that of the current aircraft
499       if (id == rwy->getCleared()) {
500         current->setHoldPosition(false);
501       } else {
502         current->setHoldPosition(true);
503       }
504     }
505 }
506
507
508 void FGTowerController::signOff(int id) 
509 {
510   TrafficVectorIterator i = activeTraffic.begin();
511   // Search search if the current id alread has an entry
512   // This might be faster using a map instead of a vector, but let's start by taking a safe route
513     if (activeTraffic.size()) {
514       //while ((i->getId() != id) && i != activeTraffic.end()) {
515       while (i != activeTraffic.end()) {
516         if (i->getId() == id) {
517           break;
518         }
519         i++;
520       }
521     }
522     // If this aircraft has left the runway, we can clear the departure record for this runway
523     ActiveRunwayVecIterator rwy = activeRunways.begin();
524     if (activeRunways.size()) {
525       //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
526       while (rwy != activeRunways.end()) {
527         if (rwy->getRunwayName() == i->getRunway()) {
528           break;
529         }
530         rwy++;
531       }
532       if (rwy != activeRunways.end()) {
533         rwy = activeRunways.erase(rwy);
534       } else {
535         SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
536       }
537     }
538     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
539       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower");
540     } else {
541       i = activeTraffic.erase(i);
542     }
543 }
544
545 // NOTE:
546 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
547 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
548 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
549 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
550 // Note that this function is probably obsolete
551 bool FGTowerController::hasInstruction(int id)
552 {
553     TrafficVectorIterator i = activeTraffic.begin();
554     // Search search if the current id has an entry
555     // This might be faster using a map instead of a vector, but let's start by taking a safe route
556     if (activeTraffic.size()) 
557       {
558         //while ((i->getId() != id) && i != activeTraffic.end()) {
559         while (i != activeTraffic.end()) {
560           if (i->getId() == id) {
561             break;
562           }
563         i++;
564       }
565     }
566     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
567       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
568     } else {
569       return i->hasInstruction();
570     }
571   return false;
572 }
573
574
575 FGATCInstruction FGTowerController::getInstruction(int id)
576 {
577   TrafficVectorIterator i = activeTraffic.begin();
578   // Search search if the current id has an entry
579   // This might be faster using a map instead of a vector, but let's start by taking a safe route
580   if (activeTraffic.size()) {
581     //while ((i->getId() != id) && i != activeTraffic.end()) {
582     while (i != activeTraffic.end()) {
583       if (i->getId() == id) {
584         break;
585       }
586       i++;
587     }
588   }
589   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
590     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
591   } else {
592     return i->getInstruction();
593   }
594   return FGATCInstruction();
595 }
596
597 /***************************************************************************
598  * class FGStartupController
599  *
600  **************************************************************************/
601 FGStartupController::FGStartupController() :
602   FGATCController()
603 {
604     available = true;
605 }
606
607 void FGStartupController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
608                                          double lat, double lon, double heading, 
609                                          double speed, double alt, double radius, int leg,
610                                          FGAIAircraft *ref)
611 {
612   TrafficVectorIterator i = activeTraffic.begin();
613   // Search whether the current id alread has an entry
614   // This might be faster using a map instead of a vector, but let's start by taking a safe route
615   if (activeTraffic.size()) {
616     //while ((i->getId() != id) && i != activeTraffic.end()) {
617     while (i != activeTraffic.end()) {
618       if (i->getId() == id) {
619         break;
620       }
621       i++;
622     }
623   }
624   
625   // Add a new TrafficRecord if no one exsists for this aircraft.
626   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
627     FGTrafficRecord rec;
628     rec.setId(id);
629
630     rec.setPositionAndHeading(lat, lon, heading, speed, alt);
631     rec.setRunway(intendedRoute->getRunway());
632     rec.setLeg(leg);
633     //rec.setCallSign(callsign);
634     rec.setAircraft(ref);
635     rec.setHoldPosition(true);
636     activeTraffic.push_back(rec);
637     } else {
638         i->setPositionAndHeading(lat, lon, heading, speed, alt);
639         
640   }
641 }
642
643 // NOTE:
644 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
645 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
646 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
647 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
648 // Note that this function is probably obsolete
649 bool FGStartupController::hasInstruction(int id)
650 {
651     TrafficVectorIterator i = activeTraffic.begin();
652     // Search search if the current id has an entry
653     // This might be faster using a map instead of a vector, but let's start by taking a safe route
654     if (activeTraffic.size()) 
655       {
656         //while ((i->getId() != id) && i != activeTraffic.end()) {
657         while (i != activeTraffic.end()) {
658           if (i->getId() == id) {
659             break;
660           }
661         i++;
662       }
663     }
664     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
665       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
666     } else {
667       return i->hasInstruction();
668     }
669   return false;
670 }
671
672
673 FGATCInstruction FGStartupController::getInstruction(int id)
674 {
675   TrafficVectorIterator i = activeTraffic.begin();
676   // Search search if the current id has an entry
677   // This might be faster using a map instead of a vector, but let's start by taking a safe route
678   if (activeTraffic.size()) {
679     //while ((i->getId() != id) && i != activeTraffic.end()) {
680     while (i != activeTraffic.end()) {
681       if (i->getId() == id) {
682         break;
683       }
684       i++;
685     }
686   }
687   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
688     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
689   } else {
690     return i->getInstruction();
691   }
692   return FGATCInstruction();
693 }
694
695 void FGStartupController::signOff(int id) 
696 {
697   TrafficVectorIterator i = activeTraffic.begin();
698   // Search search if the current id alread has an entry
699   // This might be faster using a map instead of a vector, but let's start by taking a safe route
700     if (activeTraffic.size()) {
701       //while ((i->getId() != id) && i != activeTraffic.end()) {
702       while (i != activeTraffic.end()) {
703         if (i->getId() == id) {
704           break;
705         }
706         i++;
707       }
708     }
709     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
710       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower");
711     } else {
712       i = activeTraffic.erase(i);
713     }
714 }
715
716 void FGStartupController::update(int id, double lat, double lon, double heading, double speed, double alt, 
717                              double dt)
718 {
719     TrafficVectorIterator i = activeTraffic.begin();
720     // Search search if the current id has an entry
721     // This might be faster using a map instead of a vector, but let's start by taking a safe route
722     TrafficVectorIterator current, closest;
723     if (activeTraffic.size()) {
724       //while ((i->getId() != id) && i != activeTraffic.end()) {
725       while (i != activeTraffic.end()) {
726         if (i->getId() == id) {
727           break;
728         }
729         i++;
730       }
731     }
732
733 //    // update position of the current aircraft
734     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
735       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
736     } else {
737       i->setPositionAndHeading(lat, lon, heading, speed, alt);
738       current = i;
739     }
740     setDt(getDt() + dt);
741
742     int state = i->getState();
743     time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
744     time_t now       = time(NULL) + fgGetLong("/sim/time/warp");
745
746     if ((now - lastTransmission) > 3 + (rand() % 15)) {
747         available = true;
748     }
749
750     if ((state == 0) && available) {
751         if (now > startTime) {
752              transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
753              i->updateState();
754              lastTransmission = now;
755              available = false;
756         }
757     }
758     if ((state == 1) && available) {
759         if (now > startTime+60) {
760             transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
761             i->updateState();
762             lastTransmission = now;
763              available = false;
764         }
765     }
766     if ((state == 2) && available) {
767         if (now > startTime+80) {
768             transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
769             i->updateState();
770             lastTransmission = now;
771              available = false;
772         }
773     }
774     if ((state == 3) && available){
775         if (now > startTime+100) {
776             transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND);
777             i->updateState();
778             lastTransmission = now;
779              available = false;
780         }
781      }
782      // TODO: Switch to APRON control and request pushback Clearance.
783      // Get Push back clearance
784      if ((state == 4) && available){
785           i->setHoldPosition(false);
786      }
787 }