]> git.mxchange.org Git - flightgear.git/blob - src/ATC/trafficcontrol.cxx
e24444d1317b2b49f94e9133fc9b96ed3c8e41ca
[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 bool FGATCController::isUserAircraft(FGAIAircraft* ac) 
469
470     return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false; 
471 };
472
473 void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId,
474                                AtcMsgDir msgDir)
475 {
476     string sender, receiver;
477     int stationFreq = 0;
478     int taxiFreq = 0;
479     int freqId = 0;
480     string atisInformation;
481     string text;
482     string taxiFreqStr;
483     double heading = 0;
484     string activeRunway;
485     string fltType;
486     string rwyClass;
487     string SID;
488     string transponderCode;
489     FGAIFlightPlan *fp;
490     string fltRules;
491
492     //double commFreqD;
493     sender = rec->getAircraft()->getTrafficRef()->getCallSign();
494     //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
495     switch (rec->getLeg()) {
496     case 2:
497     case 3:
498         freqId = rec->getNextFrequency();
499         stationFreq =
500             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
501             getDynamics()->getGroundFrequency(rec->getLeg() + freqId);
502         taxiFreq =
503             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
504             getDynamics()->getGroundFrequency(3);
505         receiver =
506             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
507             getName() + "-Ground";
508         atisInformation =
509             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
510             getDynamics()->getAtisInformation();
511         break;
512     case 4:
513         receiver =
514             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
515             getName() + "-Tower";
516         break;
517     }
518     // Swap sender and receiver value in case of a ground to air transmission
519     if (msgDir == ATC_GROUND_TO_AIR) {
520         string tmp = sender;
521         sender = receiver;
522         receiver = tmp;
523     }
524     switch (msgId) {
525     case MSG_ANNOUNCE_ENGINE_START:
526         text = sender + ". Ready to Start up";
527         break;
528     case MSG_REQUEST_ENGINE_START:
529         text =
530             receiver + ", This is " + sender + ". Position " +
531             getGateName(rec->getAircraft()) + ". Information " +
532             atisInformation + ". " +
533             rec->getAircraft()->getTrafficRef()->getFlightRules() +
534             " to " +
535             rec->getAircraft()->getTrafficRef()->getArrivalAirport()->
536             getName() + ". Request start-up";
537         break;
538         // Acknowledge engine startup permission
539         // Assign departure runway
540         // Assign SID, if necessery (TODO)
541     case MSG_PERMIT_ENGINE_START:
542         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
543
544         heading = rec->getAircraft()->getTrafficRef()->getCourse();
545         fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
546         rwyClass =
547             rec->getAircraft()->GetFlightPlan()->
548             getRunwayClassFromTrafficType(fltType);
549
550         rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
551             getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
552                                            heading);
553         rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
554         fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
555             getDynamics()->getSID(activeRunway, heading);
556         rec->getAircraft()->GetFlightPlan()->setSID(fp);
557         if (fp) {
558             SID = fp->getName() + " departure";
559         } else {
560             SID = "fly runway heading ";
561         }
562         //snprintf(buffer, 7, "%3.2f", heading);
563         fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
564         transponderCode = genTransponderCode(fltRules);
565         rec->getAircraft()->SetTransponderCode(transponderCode);
566         text =
567             receiver + ". Start-up approved. " + atisInformation +
568             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
569             transponderCode + ". " +
570             "For push-back and taxi clearance call " + taxiFreqStr + ". " +
571             sender + " control.";
572         break;
573     case MSG_DENY_ENGINE_START:
574         text = receiver + ". Standby";
575         break;
576     case MSG_ACKNOWLEDGE_ENGINE_START:
577         fp = rec->getAircraft()->GetFlightPlan()->getSID();
578         if (fp) {
579             SID =
580                 rec->getAircraft()->GetFlightPlan()->getSID()->getName() +
581                 " departure";
582         } else {
583             SID = "fly runway heading ";
584         }
585         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
586         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
587         transponderCode = rec->getAircraft()->GetTransponderCode();
588         text =
589             receiver + ". Start-up approved. " + atisInformation +
590             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
591             transponderCode + ". " +
592             "For push-back and taxi clearance call " + taxiFreqStr + ". " +
593             sender;
594         break;
595     case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
596         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
597         text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
598         break;
599     case MSG_INITIATE_CONTACT:
600         text = receiver + ". With you. " + sender;
601         break;
602     case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
603         text = receiver + ". Roger. " + sender;
604         break;
605     case MSG_REQUEST_PUSHBACK_CLEARANCE:
606         text = receiver + ". Request push-back. " + sender;
607         break;
608     case MSG_PERMIT_PUSHBACK_CLEARANCE:
609         text = receiver + ". Push-back approved. " + sender;
610         break;
611     case MSG_HOLD_PUSHBACK_CLEARANCE:
612         text = receiver + ". Standby. " + sender;
613         break;
614     case MSG_REQUEST_TAXI_CLEARANCE:
615         text = receiver + ". Ready to Taxi. " + sender;
616         break;
617     case MSG_ISSUE_TAXI_CLEARANCE:
618         text = receiver + ". Cleared to taxi. " + sender;
619         break;
620     case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
621         text = receiver + ". Cleared to taxi. " + sender;
622         break;
623     case MSG_HOLD_POSITION:
624         text = receiver + ". Hold Position. " + sender;
625         break;
626     case MSG_ACKNOWLEDGE_HOLD_POSITION:
627         text = receiver + ". Holding Position. " + sender;
628         break;
629     case MSG_RESUME_TAXI:
630         text = receiver + ". Resume Taxiing. " + sender;
631         break;
632     case MSG_ACKNOWLEDGE_RESUME_TAXI:
633         text = receiver + ". Continuing Taxi. " + sender;
634         break;
635     default:
636         text = text + sender + ". Transmitting unknown Message";
637         break;
638     }
639     double onBoardRadioFreq0 =
640         fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
641     double onBoardRadioFreq1 =
642         fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
643     int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
644     int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
645     //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl;
646
647     // Display ATC message only when one of the radios is tuned
648     // the relevant frequency.
649     // Note that distance attenuation is currently not yet implemented
650     if ((onBoardRadioFreqI0 == stationFreq)
651         || (onBoardRadioFreqI1 == stationFreq)) {
652         if (rec->allowTransmissions()) {
653             fgSetString("/sim/messages/atc", text.c_str());
654         }
655     }
656 }
657
658 string FGATCController::formatATCFrequency3_2(int freq)
659 {
660     char buffer[7];
661     snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
662     return string(buffer);
663 }
664
665 // TODO: Set transponder codes according to real-world routes.
666 // The current version just returns a random string of four octal numbers. 
667 string FGATCController::genTransponderCode(string fltRules)
668 {
669     if (fltRules == "VFR") {
670         return string("1200");
671     } else {
672         char buffer[5];
673         snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
674                  rand() % 8);
675         return string(buffer);
676     }
677 }
678
679 void FGATCController::init() 
680 {
681    if (!initialized) {
682        FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
683        mgr->addController(this);
684        initialized = true;
685     }
686 }
687
688 /***************************************************************************
689  * class FGTowerController
690  *
691  **************************************************************************/
692 FGTowerController::FGTowerController():
693 FGATCController()
694 {
695 }
696
697 // 
698 void FGTowerController::announcePosition(int id,
699                                          FGAIFlightPlan * intendedRoute,
700                                          int currentPosition, double lat,
701                                          double lon, double heading,
702                                          double speed, double alt,
703                                          double radius, int leg,
704                                          FGAIAircraft * ref)
705 {
706     init();
707     TrafficVectorIterator i = activeTraffic.begin();
708     // Search whether the current id alread has an entry
709     // This might be faster using a map instead of a vector, but let's start by taking a safe route
710     if (activeTraffic.size()) {
711         //while ((i->getId() != id) && i != activeTraffic.end()) {
712         while (i != activeTraffic.end()) {
713             if (i->getId() == id) {
714                 break;
715             }
716             i++;
717         }
718     }
719     // Add a new TrafficRecord if no one exsists for this aircraft.
720     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
721         FGTrafficRecord rec;
722         rec.setId(id);
723
724         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
725         rec.setRunway(intendedRoute->getRunway());
726         rec.setLeg(leg);
727         //rec.setCallSign(callsign);
728         rec.setAircraft(ref);
729         activeTraffic.push_back(rec);
730     } else {
731         i->setPositionAndHeading(lat, lon, heading, speed, alt);
732     }
733 }
734
735 void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
736                                                   double heading, double speed, double alt,
737                                                   double dt)
738 {
739     TrafficVectorIterator i = activeTraffic.begin();
740     // Search whether the current id has an entry
741     // This might be faster using a map instead of a vector, but let's start by taking a safe route
742     TrafficVectorIterator current, closest;
743     if (activeTraffic.size()) {
744         //while ((i->getId() != id) && i != activeTraffic.end()) {
745         while (i != activeTraffic.end()) {
746             if (i->getId() == id) {
747                 break;
748             }
749             i++;
750         }
751     }
752 //    // update position of the current aircraft
753     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
754         SG_LOG(SG_GENERAL, SG_ALERT,
755                "AI error: updating aircraft without traffic record");
756     } else {
757         i->setPositionAndHeading(lat, lon, heading, speed, alt);
758         current = i;
759     }
760     setDt(getDt() + dt);
761
762 //    // see if we already have a clearance record for the currently active runway
763     ActiveRunwayVecIterator rwy = activeRunways.begin();
764     // again, a map might be more efficient here
765     if (activeRunways.size()) {
766         //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
767         while (rwy != activeRunways.end()) {
768             if (rwy->getRunwayName() == current->getRunway()) {
769                 break;
770             }
771             rwy++;
772         }
773     }
774     if (rwy == activeRunways.end()) {
775         ActiveRunway aRwy(current->getRunway(), id);
776         activeRunways.push_back(aRwy);  // Since there are no clearance records for this runway yet
777         current->setHoldPosition(false);        // Clear the current aircraft to continue
778     } else {
779         // Okay, we have a clearance record for this runway, so check
780         // whether the clearence ID matches that of the current aircraft
781         if (id == rwy->getCleared()) {
782             current->setHoldPosition(false);
783         } else {
784             current->setHoldPosition(true);
785         }
786     }
787 }
788
789
790 void FGTowerController::signOff(int id)
791 {
792     TrafficVectorIterator i = activeTraffic.begin();
793     // Search search if the current id alread has an entry
794     // This might be faster using a map instead of a vector, but let's start by taking a safe route
795     if (activeTraffic.size()) {
796         //while ((i->getId() != id) && i != activeTraffic.end()) {
797         while (i != activeTraffic.end()) {
798             if (i->getId() == id) {
799                 break;
800             }
801             i++;
802         }
803     }
804     // If this aircraft has left the runway, we can clear the departure record for this runway
805     ActiveRunwayVecIterator rwy = activeRunways.begin();
806     if (activeRunways.size()) {
807         //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
808         while (rwy != activeRunways.end()) {
809             if (rwy->getRunwayName() == i->getRunway()) {
810                 break;
811             }
812             rwy++;
813         }
814         if (rwy != activeRunways.end()) {
815             rwy = activeRunways.erase(rwy);
816         } else {
817             SG_LOG(SG_GENERAL, SG_ALERT,
818                    "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
819         }
820     }
821     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
822         SG_LOG(SG_GENERAL, SG_ALERT,
823                "AI error: Aircraft without traffic record is signing off from tower");
824     } else {
825         i = activeTraffic.erase(i);
826     }
827 }
828
829 // NOTE:
830 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
831 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
832 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
833 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
834 // Note that this function is probably obsolete
835 bool FGTowerController::hasInstruction(int id)
836 {
837     TrafficVectorIterator i = activeTraffic.begin();
838     // Search search if the current id has an entry
839     // This might be faster using a map instead of a vector, but let's start by taking a safe route
840     if (activeTraffic.size()) {
841         //while ((i->getId() != id) && i != activeTraffic.end()) {
842         while (i != activeTraffic.end()) {
843             if (i->getId() == id) {
844                 break;
845             }
846             i++;
847         }
848     }
849     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
850         SG_LOG(SG_GENERAL, SG_ALERT,
851                "AI error: checking ATC instruction for aircraft without traffic record");
852     } else {
853         return i->hasInstruction();
854     }
855     return false;
856 }
857
858
859 FGATCInstruction FGTowerController::getInstruction(int id)
860 {
861     TrafficVectorIterator i = activeTraffic.begin();
862     // Search search if the current id has an entry
863     // This might be faster using a map instead of a vector, but let's start by taking a safe route
864     if (activeTraffic.size()) {
865         //while ((i->getId() != id) && i != activeTraffic.end()) {
866         while (i != activeTraffic.end()) {
867             if (i->getId() == id) {
868                 break;
869             }
870             i++;
871         }
872     }
873     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
874         SG_LOG(SG_GENERAL, SG_ALERT,
875                "AI error: requesting ATC instruction for aircraft without traffic record");
876     } else {
877         return i->getInstruction();
878     }
879     return FGATCInstruction();
880 }
881
882 /***************************************************************************
883  * class FGStartupController
884  *
885  **************************************************************************/
886 FGStartupController::FGStartupController():
887 FGATCController()
888 {
889 }
890
891 void FGStartupController::announcePosition(int id,
892                                            FGAIFlightPlan * intendedRoute,
893                                            int currentPosition, double lat,
894                                            double lon, double heading,
895                                            double speed, double alt,
896                                            double radius, int leg,
897                                            FGAIAircraft * ref)
898 {
899     init();
900     TrafficVectorIterator i = activeTraffic.begin();
901     // Search whether the current id alread has an entry
902     // This might be faster using a map instead of a vector, but let's start by taking a safe route
903     if (activeTraffic.size()) {
904         //while ((i->getId() != id) && i != activeTraffic.end()) {
905         while (i != activeTraffic.end()) {
906             if (i->getId() == id) {
907                 break;
908             }
909             i++;
910         }
911     }
912     // Add a new TrafficRecord if no one exsists for this aircraft.
913     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
914         FGTrafficRecord rec;
915         rec.setId(id);
916
917         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
918         rec.setRunway(intendedRoute->getRunway());
919         rec.setLeg(leg);
920         //rec.setCallSign(callsign);
921         rec.setAircraft(ref);
922         rec.setHoldPosition(true);
923         activeTraffic.push_back(rec);
924     } else {
925         i->setPositionAndHeading(lat, lon, heading, speed, alt);
926
927     }
928 }
929
930 // NOTE:
931 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
932 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
933 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
934 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
935 // Note that this function is probably obsolete
936 bool FGStartupController::hasInstruction(int id)
937 {
938     TrafficVectorIterator i = activeTraffic.begin();
939     // Search search if the current id has an entry
940     // This might be faster using a map instead of a vector, but let's start by taking a safe route
941     if (activeTraffic.size()) {
942         //while ((i->getId() != id) && i != activeTraffic.end()) {
943         while (i != activeTraffic.end()) {
944             if (i->getId() == id) {
945                 break;
946             }
947             i++;
948         }
949     }
950     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
951         SG_LOG(SG_GENERAL, SG_ALERT,
952                "AI error: checking ATC instruction for aircraft without traffic record");
953     } else {
954         return i->hasInstruction();
955     }
956     return false;
957 }
958
959
960 FGATCInstruction FGStartupController::getInstruction(int id)
961 {
962     TrafficVectorIterator i = activeTraffic.begin();
963     // Search search if the current id has an entry
964     // This might be faster using a map instead of a vector, but let's start by taking a safe route
965     if (activeTraffic.size()) {
966         //while ((i->getId() != id) && i != activeTraffic.end()) {
967         while (i != activeTraffic.end()) {
968             if (i->getId() == id) {
969                 break;
970             }
971             i++;
972         }
973     }
974     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
975         SG_LOG(SG_GENERAL, SG_ALERT,
976                "AI error: requesting ATC instruction for aircraft without traffic record");
977     } else {
978         return i->getInstruction();
979     }
980     return FGATCInstruction();
981 }
982
983 void FGStartupController::signOff(int id)
984 {
985     TrafficVectorIterator i = activeTraffic.begin();
986     // Search search if the current id alread has an entry
987     // This might be faster using a map instead of a vector, but let's start by taking a safe route
988     if (activeTraffic.size()) {
989         //while ((i->getId() != id) && i != activeTraffic.end()) {
990         while (i != activeTraffic.end()) {
991             if (i->getId() == id) {
992                 break;
993             }
994             i++;
995         }
996     }
997     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
998         SG_LOG(SG_GENERAL, SG_ALERT,
999                "AI error: Aircraft without traffic record is signing off from tower");
1000     } else {
1001         i = activeTraffic.erase(i);
1002     }
1003 }
1004
1005 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
1006                                                     double heading, double speed, double alt,
1007                                                     double dt)
1008 {
1009     TrafficVectorIterator i = activeTraffic.begin();
1010     // Search search if the current id has an entry
1011     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1012     TrafficVectorIterator current, closest;
1013     if (activeTraffic.size()) {
1014         //while ((i->getId() != id) && i != activeTraffic.end()) {
1015         while (i != activeTraffic.end()) {
1016             if (i->getId() == id) {
1017                 break;
1018             }
1019             i++;
1020         }
1021     }
1022 //    // update position of the current aircraft
1023
1024     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1025         SG_LOG(SG_GENERAL, SG_ALERT,
1026                "AI error: updating aircraft without traffic record");
1027     } else {
1028         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1029         current = i;
1030     }
1031     setDt(getDt() + dt);
1032
1033     int state = i->getState();
1034
1035     // The user controlled aircraft should have crased here, because it doesn't have a traffic reference. 
1036     // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
1037     time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
1038     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1039     //cerr << i->getAircraft()->getTrafficRef()->getCallSign() 
1040     //     << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1041     //     << " at parking " << getGateName(i->getAircraft()) << endl;
1042
1043     if ((now - lastTransmission) > 3 + (rand() % 15)) {
1044         available = true;
1045     }
1046
1047     if ((state == 0) && available) {
1048         if (now > startTime) {
1049             //cerr << "Transmitting startup msg" << endl;
1050             transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
1051             i->updateState();
1052             lastTransmission = now;
1053             available = false;
1054         }
1055     }
1056     if ((state == 1) && available) {
1057         if (now > startTime + 60) {
1058             transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
1059             i->updateState();
1060             lastTransmission = now;
1061             available = false;
1062         }
1063     }
1064     if ((state == 2) && available) {
1065         if (now > startTime + 80) {
1066             transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
1067             i->updateState();
1068             lastTransmission = now;
1069             available = false;
1070         }
1071     }
1072     if ((state == 3) && available) {
1073         if (now > startTime + 100) {
1074             transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START,
1075                      ATC_AIR_TO_GROUND);
1076             i->updateState();
1077             lastTransmission = now;
1078             available = false;
1079         }
1080     }
1081     // Note: The next four stages are only necessesary when Startup control is
1082     //  on a different frequency, compared to ground control
1083     if ((state == 4) && available) {
1084         if (now > startTime + 130) {
1085             transmit(&(*i), MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,
1086                      ATC_AIR_TO_GROUND);
1087             i->updateState();
1088             i->nextFrequency();
1089             lastTransmission = now;
1090             available = false;
1091         }
1092     }
1093     if ((state == 5) && available) {
1094         if (now > startTime + 140) {
1095             transmit(&(*i), MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND);
1096             i->updateState();
1097             lastTransmission = now;
1098             available = false;
1099         }
1100     }
1101     if ((state == 6) && available) {
1102         if (now > startTime + 150) {
1103             transmit(&(*i), MSG_ACKNOWLEDGE_INITIATE_CONTACT,
1104                      ATC_GROUND_TO_AIR);
1105             i->updateState();
1106             lastTransmission = now;
1107             available = false;
1108         }
1109     }
1110
1111     // TODO: Switch to APRON control and request pushback Clearance.
1112     // Get Push back clearance
1113     if ((state == 7) && available) {
1114         if (now > startTime + 180) {
1115             transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE,
1116                      ATC_AIR_TO_GROUND);
1117             i->updateState();
1118             lastTransmission = now;
1119             available = false;
1120         }
1121     }
1122     if ((state == 8) && available) {
1123         if (now > startTime + 200) {
1124             if (i->pushBackAllowed()) {
1125                 i->allowRepeatedTransmissions();
1126                 transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE,
1127                          ATC_GROUND_TO_AIR);
1128                 i->updateState();
1129             } else {
1130                 transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE,
1131                          ATC_GROUND_TO_AIR);
1132                 i->suppressRepeatedTransmissions();
1133             }
1134             lastTransmission = now;
1135             available = false;
1136         }
1137     }
1138     if ((state == 9) && available) {
1139         i->setHoldPosition(false);
1140     }
1141 }
1142
1143
1144 /***************************************************************************
1145  * class FGApproachController
1146  *
1147  **************************************************************************/
1148 FGApproachController::FGApproachController():
1149 FGATCController()
1150 {
1151 }
1152
1153 // 
1154 void FGApproachController::announcePosition(int id,
1155                                             FGAIFlightPlan * intendedRoute,
1156                                             int currentPosition,
1157                                             double lat, double lon,
1158                                             double heading, double speed,
1159                                             double alt, double radius,
1160                                             int leg, FGAIAircraft * ref)
1161 {
1162     init();
1163     TrafficVectorIterator i = activeTraffic.begin();
1164     // Search whether the current id alread has an entry
1165     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1166     if (activeTraffic.size()) {
1167         //while ((i->getId() != id) && i != activeTraffic.end()) {
1168         while (i != activeTraffic.end()) {
1169             if (i->getId() == id) {
1170                 break;
1171             }
1172             i++;
1173         }
1174     }
1175     // Add a new TrafficRecord if no one exsists for this aircraft.
1176     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1177         FGTrafficRecord rec;
1178         rec.setId(id);
1179
1180         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1181         rec.setRunway(intendedRoute->getRunway());
1182         rec.setLeg(leg);
1183         //rec.setCallSign(callsign);
1184         rec.setAircraft(ref);
1185         activeTraffic.push_back(rec);
1186     } else {
1187         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1188     }
1189 }
1190
1191 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1192                                                      double heading, double speed, double alt,
1193                                                      double dt)
1194 {
1195     TrafficVectorIterator i = activeTraffic.begin();
1196     // Search search if the current id has an entry
1197     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1198     TrafficVectorIterator current, closest;
1199     if (activeTraffic.size()) {
1200         //while ((i->getId() != id) && i != activeTraffic.end()) {
1201         while (i != activeTraffic.end()) {
1202             if (i->getId() == id) {
1203                 break;
1204             }
1205             i++;
1206         }
1207     }
1208 //    // update position of the current aircraft
1209     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1210         SG_LOG(SG_GENERAL, SG_ALERT,
1211                "AI error: updating aircraft without traffic record");
1212     } else {
1213         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1214         current = i;
1215         //cerr << "ApproachController: checking for speed" << endl;
1216         time_t time_diff =
1217             current->getAircraft()->
1218             checkForArrivalTime(string("final001"));
1219         if (time_diff > 15) {
1220             current->setSpeedAdjustment(current->getAircraft()->
1221                                         getPerformance()->vDescent() *
1222                                         1.35);
1223         } else if (time_diff > 5) {
1224             current->setSpeedAdjustment(current->getAircraft()->
1225                                         getPerformance()->vDescent() *
1226                                         1.2);
1227         } else if (time_diff < -15) {
1228             current->setSpeedAdjustment(current->getAircraft()->
1229                                         getPerformance()->vDescent() *
1230                                         0.65);
1231         } else if (time_diff < -5) {
1232             current->setSpeedAdjustment(current->getAircraft()->
1233                                         getPerformance()->vDescent() *
1234                                         0.8);
1235         } else {
1236             current->clearSpeedAdjustment();
1237         }
1238         //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1239     }
1240     setDt(getDt() + dt);
1241 }
1242
1243 void FGApproachController::signOff(int id)
1244 {
1245     TrafficVectorIterator i = activeTraffic.begin();
1246     // Search search if the current id alread has an entry
1247     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1248     if (activeTraffic.size()) {
1249         //while ((i->getId() != id) && i != activeTraffic.end()) {
1250         while (i != activeTraffic.end()) {
1251             if (i->getId() == id) {
1252                 break;
1253             }
1254             i++;
1255         }
1256     }
1257     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1258         SG_LOG(SG_GENERAL, SG_ALERT,
1259                "AI error: Aircraft without traffic record is signing off from approach");
1260     } else {
1261         i = activeTraffic.erase(i);
1262     }
1263 }
1264
1265
1266
1267
1268 bool FGApproachController::hasInstruction(int id)
1269 {
1270     TrafficVectorIterator i = activeTraffic.begin();
1271     // Search search if the current id has an entry
1272     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1273     if (activeTraffic.size()) {
1274         //while ((i->getId() != id) && i != activeTraffic.end()) {
1275         while (i != activeTraffic.end()) {
1276             if (i->getId() == id) {
1277                 break;
1278             }
1279             i++;
1280         }
1281     }
1282     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1283         SG_LOG(SG_GENERAL, SG_ALERT,
1284                "AI error: checking ATC instruction for aircraft without traffic record");
1285     } else {
1286         return i->hasInstruction();
1287     }
1288     return false;
1289 }
1290
1291
1292 FGATCInstruction FGApproachController::getInstruction(int id)
1293 {
1294     TrafficVectorIterator i = activeTraffic.begin();
1295     // Search search if the current id has an entry
1296     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1297     if (activeTraffic.size()) {
1298         //while ((i->getId() != id) && i != activeTraffic.end()) {
1299         while (i != activeTraffic.end()) {
1300             if (i->getId() == id) {
1301                 break;
1302             }
1303             i++;
1304         }
1305     }
1306     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1307         SG_LOG(SG_GENERAL, SG_ALERT,
1308                "AI error: requesting ATC instruction for aircraft without traffic record");
1309     } else {
1310         return i->getInstruction();
1311     }
1312     return FGATCInstruction();
1313 }
1314
1315
1316 ActiveRunway *FGApproachController::getRunway(string name)
1317 {
1318     ActiveRunwayVecIterator rwy = activeRunways.begin();
1319     if (activeRunways.size()) {
1320         while (rwy != activeRunways.end()) {
1321             if (rwy->getRunwayName() == name) {
1322                 break;
1323             }
1324             rwy++;
1325         }
1326     }
1327     if (rwy == activeRunways.end()) {
1328         ActiveRunway aRwy(name, 0);
1329         activeRunways.push_back(aRwy);
1330         rwy = activeRunways.end() - 1;
1331     }
1332     return &(*rwy);
1333 }