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