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