]> git.mxchange.org Git - flightgear.git/blob - src/ATC/trafficcontrol.cxx
AI traffic refused to take-off, because the TrafficRecord destructor would unassign...
[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 #include <cstdio>
29
30 #include <osg/Geode>
31 #include <osg/Geometry>
32 #include <osg/MatrixTransform>
33 #include <osg/Shape>
34
35 #include <simgear/scene/material/EffectGeode.hxx>
36 #include <simgear/scene/material/matlib.hxx>
37 #include <simgear/scene/material/mat.hxx>
38 #include <simgear/scene/util/OsgMath.hxx>
39 #include <simgear/timing/sg_time.hxx>
40
41 #include <Scenery/scenery.hxx>
42
43 #include "trafficcontrol.hxx"
44 #include "atc_mgr.hxx"
45 #include <AIModel/AIAircraft.hxx>
46 #include <AIModel/AIFlightPlan.hxx>
47 #include <AIModel/performancedata.hxx>
48 #include <ATC/atc_mgr.hxx>
49 #include <Traffic/TrafficMgr.hxx>
50 #include <Airports/groundnetwork.hxx>
51 #include <Airports/dynamics.hxx>
52 #include <Airports/airport.hxx>
53 #include <Radio/radio.hxx>
54 #include <signal.h>
55
56 using std::sort;
57 using std::string;
58 using std::cout;
59 using std::endl;
60
61 /***************************************************************************
62  * ActiveRunway
63  **************************************************************************/
64 time_t ActiveRunway::requestTimeSlot(time_t eta)
65 {
66     time_t newEta;
67     time_t separation = 90;
68     bool found = false;
69     if (estimatedArrivalTimes.empty()) {
70         estimatedArrivalTimes.push_back(eta);
71         return eta;
72     } else {
73         TimeVectorIterator i = estimatedArrivalTimes.begin();
74         //cerr << "Checking eta slots " << eta << ": " << endl;
75         for (i = estimatedArrivalTimes.begin();
76                 i != estimatedArrivalTimes.end(); i++) {
77             //cerr << "Stored time : " << (*i) << endl;
78         }
79         i = estimatedArrivalTimes.begin();
80         if ((eta + separation) < (*i)) {
81             newEta = eta;
82             found = true;
83             //cerr << "Storing at beginning" << endl;
84         }
85         while ((i != estimatedArrivalTimes.end()) && (!found)) {
86             TimeVectorIterator j = i + 1;
87             if (j == estimatedArrivalTimes.end()) {
88                 if (((*i) + separation) < eta) {
89                     //cerr << "Storing at end" << endl;
90                     newEta = eta;
91                 } else {
92                     newEta = (*i) + separation;
93                     //cerr << "Storing at end + separation" << endl;
94                 }
95             } else {
96                 if ((((*j) - (*i)) > (separation * 2))) {       // found a potential slot
97                     // now check whether this slot is usable:
98                     // 1) eta should fall between the two points
99                     //    i.e. eta > i AND eta < j
100                     //
101                     //cerr << "Found potential slot after " << (*i) << endl;
102                     if (eta > (*i) && (eta < (*j))) {
103                         found = true;
104                         if (eta < ((*i) + separation)) {
105                             newEta = (*i) + separation;
106                             //cerr << "Using  original" << (*i) << " + separation " << endl;
107                         } else {
108                             newEta = eta;
109                             //cerr << "Using original after " << (*i) << endl;
110                         }
111                     } else if (eta < (*i)) {
112                         found = true;
113                         newEta = (*i) + separation;
114                         //cerr << "Using delayed slot after " << (*i) << endl;
115                     }
116                     /*
117                        if (((*j) - separation) < eta) {
118                        found = true;
119                        if (((*i) + separation) < eta) {
120                        newEta = eta;
121                        cerr << "Using original after " << (*i) << endl;
122                        } else {
123                        newEta = (*i) + separation;
124                        cerr << "Using  " << (*i) << " + separation " << endl;
125                        }
126                        } */
127                 }
128             }
129             i++;
130         }
131     }
132     //cerr << ". done. New ETA : " << newEta << endl;
133
134     estimatedArrivalTimes.push_back(newEta);
135     sort(estimatedArrivalTimes.begin(), estimatedArrivalTimes.end());
136     // do some housekeeping : remove any timestamps that are past
137
138     time_t now = globals->get_time_params()->get_cur_time();
139
140     TimeVectorIterator i = estimatedArrivalTimes.begin();
141     while (i != estimatedArrivalTimes.end()) {
142         if ((*i) < now) {
143             //cerr << "Deleting timestamp " << (*i) << " (now = " << now << "). " << endl;
144             estimatedArrivalTimes.erase(i);
145             i = estimatedArrivalTimes.begin();
146         } else {
147             i++;
148         }
149     }
150     return newEta;
151 }
152
153 void ActiveRunway::printDepartureCue()
154 {
155     cout << "Departure cue for " << rwy << ": " << endl;
156     for (AircraftVecIterator atc = departureCue.begin(); atc != departureCue.end(); atc++) {
157         cout << "     " << (*atc)->getCallSign() << " "  << (*atc)->getTakeOffStatus();
158         cout << " " << (*atc)->_getLatitude() << " " << (*atc)->_getLongitude() << (*atc)-> getSpeed() << " " << (*atc)->getAltitude() << endl;
159     }
160     
161 }
162
163 FGAIAircraft* ActiveRunway::getFirstOfStatus(int stat)
164 {
165     for (AircraftVecIterator atc =departureCue.begin(); atc != departureCue.end(); atc++) {
166         if ((*atc)->getTakeOffStatus() == stat)
167             return (*atc);
168     }
169     return 0;
170 }
171
172
173
174 /***************************************************************************
175  * FGTrafficRecord
176  **************************************************************************/
177 FGTrafficRecord::FGTrafficRecord():
178         id(0), waitsForId(0),
179         currentPos(0),
180         leg(0),
181         frequencyId(0),
182         state(0),
183         allowTransmission(true),
184         allowPushback(true),
185         priority(0),
186         timer(0),
187         latitude(0), longitude(0), heading(0), speed(0), altitude(0), radius(0)
188 {
189 }
190
191 FGTrafficRecord::~FGTrafficRecord()
192 {
193     //if (aircraft) {
194     //    aircraft->clearATCController();
195     //}
196 }
197
198 void FGTrafficRecord::setPositionAndIntentions(int pos,
199         FGAIFlightPlan * route)
200 {
201
202     currentPos = pos;
203     if (! intentions.empty()) {
204         intVecIterator i = intentions.begin();
205         if ((*i) != pos) {
206             SG_LOG(SG_ATC, SG_ALERT,
207                    "Error in FGTrafficRecord::setPositionAndIntentions at " << SG_ORIGIN);
208         }
209         intentions.erase(i);
210     } else {
211         //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
212         int size = route->getNrOfWayPoints();
213         //cerr << "Setting pos" << pos << " ";
214         //cerr << "setting intentions ";
215         for (int i = 2; i < size; i++) {
216             int val = route->getRouteIndex(i);
217             intentions.push_back(val);
218         }
219     }
220 }
221
222 void FGTrafficRecord::setAircraft(FGAIAircraft *ref)
223 {
224     aircraft = ref;
225 }
226
227 FGAIAircraft* FGTrafficRecord::getAircraft() const
228 {
229     return aircraft.ptr();
230 }
231
232 /**
233  * Check if another aircraft is ahead of the current one, and on the same
234  * return true / false is the is/isn't the case.
235  *
236  ****************************************************************************/
237
238 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other)
239 {
240     bool result = false;
241     //cerr << "Start check 1" << endl;
242     if (currentPos == other.currentPos) {
243         //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
244         result = true;
245     }
246     //  else if (! other.intentions.empty())
247     //     {
248     //       cerr << "Start check 2" << endl;
249     //       intVecIterator i = other.intentions.begin();
250     //       while (!((i == other.intentions.end()) || ((*i) == currentPos)))
251     //     i++;
252     //       if (i != other.intentions.end()) {
253     //     cerr << "Check Position and intentions: current matches other.intentions" << endl;
254     //     result = true;
255     //       }
256     else if (! intentions.empty()) {
257         //cerr << "Start check 3" << endl;
258         intVecIterator i = intentions.begin();
259         //while (!((i == intentions.end()) || ((*i) == other.currentPos)))
260         while (i != intentions.end()) {
261             if ((*i) == other.currentPos) {
262                 break;
263             }
264             i++;
265         }
266         if (i != intentions.end()) {
267             //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
268             result = true;
269         }
270     }
271     //cerr << "Done !!" << endl;
272     return result;
273 }
274
275 void FGTrafficRecord::setPositionAndHeading(double lat, double lon,
276         double hdg, double spd,
277         double alt)
278 {
279     latitude = lat;
280     longitude = lon;
281     heading = hdg;
282     speed = spd;
283     altitude = alt;
284 }
285
286 int FGTrafficRecord::crosses(FGGroundNetwork * net,
287                              FGTrafficRecord & other)
288 {
289     if (checkPositionAndIntentions(other)
290             || (other.checkPositionAndIntentions(*this)))
291         return -1;
292     intVecIterator i, j;
293     int currentTargetNode = 0, otherTargetNode = 0;
294     if (currentPos > 0)
295         currentTargetNode = net->findSegment(currentPos)->getEnd()->getIndex(); // OKAY,...
296     if (other.currentPos > 0)
297         otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex();     // OKAY,...
298     if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
299         return currentTargetNode;
300     if (! intentions.empty()) {
301         for (i = intentions.begin(); i != intentions.end(); i++) {
302             if ((*i) > 0) {
303                 if (currentTargetNode ==
304                         net->findSegment(*i)->getEnd()->getIndex()) {
305                     //cerr << "Current crosses at " << currentTargetNode <<endl;
306                     return currentTargetNode;
307                 }
308             }
309         }
310     }
311     if (! other.intentions.empty()) {
312         for (i = other.intentions.begin(); i != other.intentions.end();
313                 i++) {
314             if ((*i) > 0) {
315                 if (otherTargetNode ==
316                         net->findSegment(*i)->getEnd()->getIndex()) {
317                     //cerr << "Other crosses at " << currentTargetNode <<endl;
318                     return otherTargetNode;
319                 }
320             }
321         }
322     }
323     if (! intentions.empty() && ! other.intentions.empty()) {
324         for (i = intentions.begin(); i != intentions.end(); i++) {
325             for (j = other.intentions.begin(); j != other.intentions.end();
326                     j++) {
327                 //cerr << "finding segment " << *i << " and " << *j << endl;
328                 if (((*i) > 0) && ((*j) > 0)) {
329                     currentTargetNode =
330                         net->findSegment(*i)->getEnd()->getIndex();
331                     otherTargetNode =
332                         net->findSegment(*j)->getEnd()->getIndex();
333                     if (currentTargetNode == otherTargetNode) {
334                         //cerr << "Routes will cross at " << currentTargetNode << endl;
335                         return currentTargetNode;
336                     }
337                 }
338             }
339         }
340     }
341     return -1;
342 }
343
344 bool FGTrafficRecord::onRoute(FGGroundNetwork * net,
345                               FGTrafficRecord & other)
346 {
347     int node = -1, othernode = -1;
348     if (currentPos > 0)
349         node = net->findSegment(currentPos)->getEnd()->getIndex();
350     if (other.currentPos > 0)
351         othernode =
352             net->findSegment(other.currentPos)->getEnd()->getIndex();
353     if ((node == othernode) && (node != -1))
354         return true;
355     if (! other.intentions.empty()) {
356         for (intVecIterator i = other.intentions.begin();
357                 i != other.intentions.end(); i++) {
358             if (*i > 0) {
359                 othernode = net->findSegment(*i)->getEnd()->getIndex();
360                 if ((node == othernode) && (node > -1))
361                     return true;
362             }
363         }
364     }
365     //if (other.currentPos > 0)
366     //  othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
367     //if (! intentions.empty())
368     //  {
369     //    for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
370     //    {
371     //      if (*i > 0)
372     //        {
373     //          node = net->findSegment(*i)->getEnd()->getIndex();
374     //          if ((node == othernode) && (node > -1))
375     //            return true;
376     //        }
377     //    }
378     //  }
379     return false;
380 }
381
382
383 bool FGTrafficRecord::isOpposing(FGGroundNetwork * net,
384                                  FGTrafficRecord & other, int node)
385 {
386     // Check if current segment is the reverse segment for the other aircraft
387     FGTaxiSegment *opp;
388     //cerr << "Current segment " << currentPos << endl;
389     if ((currentPos > 0) && (other.currentPos > 0)) {
390         opp = net->findSegment(currentPos)->opposite();
391         if (opp) {
392             if (opp->getIndex() == other.currentPos)
393                 return true;
394         }
395
396         for (intVecIterator i = intentions.begin(); i != intentions.end();
397                 i++) {
398             if ((opp = net->findSegment(other.currentPos)->opposite())) {
399                 if ((*i) > 0)
400                     if (opp->getIndex() ==
401                             net->findSegment(*i)->getIndex()) {
402                         if (net->findSegment(*i)->getStart()->getIndex() ==
403                                 node) {
404                             {
405                                 //cerr << "Found the node " << node << endl;
406                                 return true;
407                             }
408                         }
409                     }
410             }
411             if (! other.intentions.empty()) {
412                 for (intVecIterator j = other.intentions.begin();
413                         j != other.intentions.end(); j++) {
414                     // cerr << "Current segment 1 " << (*i) << endl;
415                     if ((*i) > 0) {
416                         if ((opp = net->findSegment(*i)->opposite())) {
417                             if (opp->getIndex() ==
418                                     net->findSegment(*j)->getIndex()) {
419                                 //cerr << "Nodes " << net->findSegment(*i)->getIndex()
420                                 //   << " and  " << net->findSegment(*j)->getIndex()
421                                 //   << " are opposites " << endl;
422                                 if (net->findSegment(*i)->getStart()->
423                                         getIndex() == node) {
424                                     {
425                                         //cerr << "Found the node " << node << endl;
426                                         return true;
427                                     }
428                                 }
429                             }
430                         }
431                     }
432                 }
433             }
434         }
435     }
436     return false;
437 }
438
439 bool FGTrafficRecord::isActive(int margin) const
440 {
441     time_t now = globals->get_time_params()->get_cur_time();
442     time_t deptime = aircraft->getTrafficRef()->getDepartureTime();
443     return ((now + margin) > deptime);
444 }
445
446
447 void FGTrafficRecord::setSpeedAdjustment(double spd)
448 {
449     instruction.setChangeSpeed(true);
450     instruction.setSpeed(spd);
451 }
452
453 void FGTrafficRecord::setHeadingAdjustment(double heading)
454 {
455     instruction.setChangeHeading(true);
456     instruction.setHeading(heading);
457 }
458
459 bool FGTrafficRecord::pushBackAllowed() const
460 {
461     return allowPushback;
462 }
463
464
465
466
467 /***************************************************************************
468  * FGATCInstruction
469  *
470  **************************************************************************/
471 FGATCInstruction::FGATCInstruction()
472 {
473     holdPattern = false;
474     holdPosition = false;
475     changeSpeed = false;
476     changeHeading = false;
477     changeAltitude = false;
478     resolveCircularWait = false;
479
480     speed = 0;
481     heading = 0;
482     alt = 0;
483 }
484
485
486 bool FGATCInstruction::hasInstruction() const
487 {
488     return (holdPattern || holdPosition || changeSpeed || changeHeading
489             || changeAltitude || resolveCircularWait);
490 }
491
492 /***************************************************************************
493  * FGATCController
494  *
495  **************************************************************************/
496
497
498
499
500 FGATCController::FGATCController()
501 {
502     //cerr << "running FGATController constructor" << endl;
503     dt_count = 0;
504     available = true;
505     lastTransmission = 0;
506     initialized = false;
507     lastTransmissionDirection = ATC_AIR_TO_GROUND;
508     group = NULL;
509 }
510
511 FGATCController::~FGATCController()
512 {
513     FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
514     mgr->removeController(this);
515 }
516
517 string FGATCController::getGateName(FGAIAircraft * ref)
518 {
519     return ref->atGate();
520 }
521
522 bool FGATCController::isUserAircraft(FGAIAircraft* ac)
523 {
524     return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false;
525 };
526
527 void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, AtcMsgId msgId,
528                                AtcMsgDir msgDir, bool audible)
529 {
530     string sender, receiver;
531     int stationFreq = 0;
532     int taxiFreq = 0;
533     int towerFreq = 0;
534     int freqId = 0;
535     string atisInformation;
536     string text;
537     string taxiFreqStr;
538     string towerFreqStr;
539     double heading = 0;
540     string activeRunway;
541     string fltType;
542     string rwyClass;
543     string SID;
544     string transponderCode;
545     FGAIFlightPlan *fp;
546     string fltRules;
547     string instructionText;
548     int ground_to_air=0;
549
550     //double commFreqD;
551     sender = rec->getAircraft()->getTrafficRef()->getCallSign();
552     if (rec->getAircraft()->getTaxiClearanceRequest()) {
553         instructionText = "push-back and taxi";
554     } else {
555         instructionText = "taxi";
556     }
557     //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
558     switch (rec->getLeg()) {
559     case 1:
560     case 2:
561         freqId = rec->getNextFrequency();
562         stationFreq =
563             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
564             getDynamics()->getGroundFrequency(rec->getLeg() + freqId);
565         taxiFreq =
566             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
567             getDynamics()->getGroundFrequency(2);
568         towerFreq =
569             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
570             getDynamics()->getTowerFrequency(2);
571         receiver =
572             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
573             getName() + "-Ground";
574         atisInformation =
575             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
576             getDynamics()->getAtisSequence();
577         break;
578     case 3:
579         receiver =
580             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
581             getName() + "-Tower";
582         break;
583     }
584     // Swap sender and receiver value in case of a ground to air transmission
585     if (msgDir == ATC_GROUND_TO_AIR) {
586         string tmp = sender;
587         sender = receiver;
588         receiver = tmp;
589         ground_to_air=1;
590     }
591     switch (msgId) {
592     case MSG_ANNOUNCE_ENGINE_START:
593         text = sender + ". Ready to Start up";
594         break;
595     case MSG_REQUEST_ENGINE_START:
596         text =
597             receiver + ", This is " + sender + ". Position " +
598             getGateName(rec->getAircraft()) + ". Information " +
599             atisInformation + ". " +
600             rec->getAircraft()->getTrafficRef()->getFlightRules() +
601             " to " +
602             rec->getAircraft()->getTrafficRef()->getArrivalAirport()->
603             getName() + ". Request start-up";
604         break;
605         // Acknowledge engine startup permission
606         // Assign departure runway
607         // Assign SID, if necessery (TODO)
608     case MSG_PERMIT_ENGINE_START:
609         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
610
611         heading = rec->getAircraft()->getTrafficRef()->getCourse();
612         fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
613         rwyClass =
614             rec->getAircraft()->GetFlightPlan()->
615             getRunwayClassFromTrafficType(fltType);
616
617         rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
618         getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
619                                        heading);
620         rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
621         fp = NULL;
622         rec->getAircraft()->GetFlightPlan()->setSID(fp);
623         if (fp) {
624             SID = fp->getName() + " departure";
625         } else {
626             SID = "fly runway heading ";
627         }
628         //snprintf(buffer, 7, "%3.2f", heading);
629         fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
630         transponderCode = genTransponderCode(fltRules);
631         rec->getAircraft()->SetTransponderCode(transponderCode);
632         text =
633             receiver + ". Start-up approved. " + atisInformation +
634             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
635             transponderCode + ". " +
636             "For "+ instructionText + " clearance call " + taxiFreqStr + ". " +
637             sender + " control.";
638         break;
639     case MSG_DENY_ENGINE_START:
640         text = receiver + ". Standby";
641         break;
642     case MSG_ACKNOWLEDGE_ENGINE_START:
643         fp = rec->getAircraft()->GetFlightPlan()->getSID();
644         if (fp) {
645             SID =
646                 rec->getAircraft()->GetFlightPlan()->getSID()->getName() +
647                 " departure";
648         } else {
649             SID = "fly runway heading ";
650         }
651         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
652         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
653         transponderCode = rec->getAircraft()->GetTransponderCode();
654
655         text =
656             receiver + ". Start-up approved. " + atisInformation +
657             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
658             transponderCode + ". " +
659             "For " + instructionText + " clearance call " + taxiFreqStr + ". " +
660             sender;
661         break;
662     case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
663         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
664         text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
665         break;
666     case MSG_INITIATE_CONTACT:
667         text = receiver + ". With you. " + sender;
668         break;
669     case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
670         text = receiver + ". Roger. " + sender;
671         break;
672     case MSG_REQUEST_PUSHBACK_CLEARANCE:
673         if (rec->getAircraft()->getTaxiClearanceRequest()) {
674             text = receiver + ". Request push-back. " + sender;
675         } else {
676             text = receiver + ". Request Taxi clearance. " + sender;
677         }
678         break;
679     case MSG_PERMIT_PUSHBACK_CLEARANCE:
680         if (rec->getAircraft()->getTaxiClearanceRequest()) {
681             text = receiver + ". Push-back approved. " + sender;
682         } else {
683             text = receiver + ". Cleared to Taxi." + sender;
684         }
685         break;
686     case MSG_HOLD_PUSHBACK_CLEARANCE:
687         text = receiver + ". Standby. " + sender;
688         break;
689     case MSG_REQUEST_TAXI_CLEARANCE:
690         text = receiver + ". Ready to Taxi. " + sender;
691         break;
692     case MSG_ISSUE_TAXI_CLEARANCE:
693         text = receiver + ". Cleared to taxi. " + sender;
694         break;
695     case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
696         text = receiver + ". Cleared to taxi. " + sender;
697         break;
698     case MSG_HOLD_POSITION:
699         text = receiver + ". Hold Position. " + sender;
700         break;
701     case MSG_ACKNOWLEDGE_HOLD_POSITION:
702         text = receiver + ". Holding Position. " + sender;
703         break;
704     case MSG_RESUME_TAXI:
705         text = receiver + ". Resume Taxiing. " + sender;
706         break;
707     case MSG_ACKNOWLEDGE_RESUME_TAXI:
708         text = receiver + ". Continuing Taxi. " + sender;
709         break;
710     case MSG_REPORT_RUNWAY_HOLD_SHORT:
711         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
712         //activeRunway = "test";
713         text = receiver + ". Holding short runway "
714                + activeRunway
715                + ". " + sender;
716         //text = "test1";
717         //cerr << "1 Currently at leg " << rec->getLeg() << endl;
718         break;
719     case MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT:
720         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
721         text = receiver + "Roger. Holding short runway "
722                //                + activeRunway
723                + ". " + sender;
724         //text = "test2";
725         //cerr << "2 Currently at leg " << rec->getLeg() << endl;
726         break;
727     case MSG_SWITCH_TOWER_FREQUENCY:
728         towerFreqStr = formatATCFrequency3_2(towerFreq);
729         text = receiver + "Contact Tower at " + towerFreqStr + ". " + sender;
730         //text = "test3";
731         //cerr << "3 Currently at leg " << rec->getLeg() << endl;
732         break;
733     case MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY:
734         towerFreqStr = formatATCFrequency3_2(towerFreq);
735         text = receiver + "Roger, switching to tower at " + towerFreqStr + ". " + sender;
736         //text = "test4";
737         //cerr << "4 Currently at leg " << rec->getLeg() << endl;
738         break;
739     default:
740         //text = "test3";
741         text = text + sender + ". Transmitting unknown Message";
742         break;
743     }
744     if (audible) {
745         double onBoardRadioFreq0 =
746             fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
747         double onBoardRadioFreq1 =
748             fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
749         int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
750         int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
751         //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl;
752
753         // Display ATC message only when one of the radios is tuned
754         // the relevant frequency.
755         // Note that distance attenuation is currently not yet implemented
756                 
757         if ((stationFreq > 0)&&
758             ((onBoardRadioFreqI0 == stationFreq)||
759              (onBoardRadioFreqI1 == stationFreq))) {
760             if (rec->allowTransmissions()) {
761                 
762                 if( fgGetBool( "/sim/radio/use-itm-attenuation", false ) ) {
763                         //cerr << "Using ITM radio propagation" << endl;
764                         FGRadioTransmission* radio = new FGRadioTransmission();
765                         SGGeod sender_pos;
766                         double sender_alt_ft, sender_alt;
767                         if(ground_to_air) {
768                   sender_pos = parent->parent()->geod();
769                                  }
770                                 else {
771                                       sender_alt_ft = rec->getAltitude();
772                                       sender_alt = sender_alt_ft * SG_FEET_TO_METER;
773                                       sender_pos= SGGeod::fromDegM( rec->getLongitude(),
774                                              rec->getLatitude(), sender_alt );
775                                 }
776                                 double frequency = ((double)stationFreq) / 100;
777                         radio->receiveATC(sender_pos, frequency, text, ground_to_air);
778                         delete radio;
779                 }
780                 else {
781                         fgSetString("/sim/messages/atc", text.c_str());
782                 }
783             }
784         }
785     } else {
786         FGATCDialogNew::instance()->addEntry(1, text);
787     }
788 }
789
790
791 string FGATCController::formatATCFrequency3_2(int freq)
792 {
793     char buffer[7];
794     snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
795     return string(buffer);
796 }
797
798 // TODO: Set transponder codes according to real-world routes.
799 // The current version just returns a random string of four octal numbers.
800 string FGATCController::genTransponderCode(const string& fltRules)
801 {
802     if (fltRules == "VFR") {
803         return string("1200");
804     } else {
805         char buffer[5];
806         snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
807                  rand() % 8);
808         return string(buffer);
809     }
810 }
811
812 void FGATCController::init()
813 {
814     if (!initialized) {
815         FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
816         mgr->addController(this);
817         initialized = true;
818     }
819 }
820
821 /***************************************************************************
822  * class FGTowerController
823  *
824  **************************************************************************/
825 FGTowerController::FGTowerController(FGAirportDynamics *par) :
826         FGATCController()
827 {
828     parent = par;
829 }
830
831 //
832 void FGTowerController::announcePosition(int id,
833         FGAIFlightPlan * intendedRoute,
834         int currentPosition, double lat,
835         double lon, double heading,
836         double speed, double alt,
837         double radius, int leg,
838         FGAIAircraft * ref)
839 {
840     init();
841     TrafficVectorIterator i = activeTraffic.begin();
842     // Search whether the current id alread has an entry
843     // This might be faster using a map instead of a vector, but let's start by taking a safe route
844     if (! activeTraffic.empty()) {
845         //while ((i->getId() != id) && i != activeTraffic.end()) {
846         while (i != activeTraffic.end()) {
847             if (i->getId() == id) {
848                 break;
849             }
850             i++;
851         }
852     }
853     // Add a new TrafficRecord if no one exsists for this aircraft.
854     if (i == activeTraffic.end() || (activeTraffic.empty())) {
855         FGTrafficRecord rec;
856         rec.setId(id);
857
858         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
859         rec.setRunway(intendedRoute->getRunway());
860         rec.setLeg(leg);
861         //rec.setCallSign(callsign);
862         rec.setRadius(radius);
863         rec.setAircraft(ref);
864         activeTraffic.push_back(rec);
865         // Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway.
866         ActiveRunwayVecIterator rwy = activeRunways.begin();
867         if (! activeRunways.empty()) {
868             while (rwy != activeRunways.end()) {
869                 if (rwy->getRunwayName() == intendedRoute->getRunway()) {
870                     break;
871                 }
872                 rwy++;
873             }
874         }
875         if (rwy == activeRunways.end()) {
876             ActiveRunway aRwy(intendedRoute->getRunway(), id);
877             aRwy.addToDepartureCue(ref);
878             activeRunways.push_back(aRwy);
879             rwy = (activeRunways.end()-1);
880         } else {
881             rwy->addToDepartureCue(ref);
882         }
883
884         //cerr << ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getDepartureCueSize() <<  " for takeoff " << endl;
885     } else {
886         i->setPositionAndHeading(lat, lon, heading, speed, alt);
887     }
888 }
889
890 void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
891         double heading, double speed, double alt,
892         double dt)
893 {
894     TrafficVectorIterator i = activeTraffic.begin();
895     // Search whether the current id has an entry
896     // This might be faster using a map instead of a vector, but let's start by taking a safe route
897     TrafficVectorIterator current, closest;
898     if (! activeTraffic.empty()) {
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 //    // update position of the current aircraft
908     if (i == activeTraffic.end() || (activeTraffic.empty())) {
909         SG_LOG(SG_ATC, SG_ALERT,
910                "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
911     } else {
912         i->setPositionAndHeading(lat, lon, heading, speed, alt);
913         current = i;
914     }
915     setDt(getDt() + dt);
916
917     // see if we already have a clearance record for the currently active runway
918     // NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is
919     // already exists here. So, we can simplify the current code.
920     
921     ActiveRunwayVecIterator rwy = activeRunways.begin();
922     //if (parent->getId() == fgGetString("/sim/presets/airport-id")) {
923     //    for (rwy = activeRunways.begin(); rwy != activeRunways.end(); rwy++) {
924     //        rwy->printDepartureCue();
925     //    }
926     //}
927     
928     rwy = activeRunways.begin();
929     while (rwy != activeRunways.end()) {
930         if (rwy->getRunwayName() == current->getRunway()) {
931             break;
932         }
933         rwy++;
934     }
935
936     // only bother running the following code if the current aircraft is the
937     // first in line for depature
938     /* if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
939         if (rwy->getCleared()) {
940             if (id == rwy->getCleared()) {
941                 current->setHoldPosition(false);
942             } else {
943                 current->setHoldPosition(true);
944             }
945         } else {
946             // For now. At later stages, this will probably be the place to check for inbound traffc.
947             rwy->setCleared(id);
948         }
949     } */
950     // only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control
951     FGAIAircraft* ac= rwy->getFirstAircraftInDepartureCue();
952     if (ac->getTakeOffStatus() == 1) {
953         ac->setTakeOffStatus(2);
954     }
955     if (current->getAircraft()->getTakeOffStatus() == 2) {
956         current -> setHoldPosition(false);
957     } else {
958         current->setHoldPosition(true);
959     }
960     int clearanceId = rwy->getCleared();
961     if (clearanceId) {
962         if (id == clearanceId) {
963             current->setHoldPosition(false);
964         }
965     } else {
966         if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
967             rwy->setCleared(id);
968             FGAIAircraft *ac = rwy->getFirstOfStatus(1);
969             if (ac)
970                 ac->setTakeOffStatus(2);
971         }
972     }
973
974
975
976 void FGTowerController::signOff(int id)
977 {
978     TrafficVectorIterator i = activeTraffic.begin();
979     // Search search if the current id alread has an entry
980     // This might be faster using a map instead of a vector, but let's start by taking a safe route
981     if (! activeTraffic.empty()) {
982         //while ((i->getId() != id) && i != activeTraffic.end()) {
983         while (i != activeTraffic.end()) {
984             if (i->getId() == id) {
985                 break;
986             }
987             i++;
988         }
989     }
990     // If this aircraft has left the runway, we can clear the departure record for this runway
991     ActiveRunwayVecIterator rwy = activeRunways.begin();
992     if (! activeRunways.empty()) {
993         //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
994         while (rwy != activeRunways.end()) {
995             if (rwy->getRunwayName() == i->getRunway()) {
996                 break;
997             }
998             rwy++;
999         }
1000         if (rwy != activeRunways.end()) {
1001             rwy->setCleared(0);
1002             rwy->updateDepartureCue();
1003         } else {
1004             SG_LOG(SG_ATC, SG_ALERT,
1005                    "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff at " << SG_ORIGIN);
1006         }
1007     }
1008     if (i == activeTraffic.end() || (activeTraffic.empty())) {
1009         SG_LOG(SG_ATC, SG_ALERT,
1010                "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN);
1011     } else {
1012         i->getAircraft()->resetTakeOffStatus();
1013         i = activeTraffic.erase(i);
1014         //cerr << "Signing off from tower controller" << endl;
1015     }
1016 }
1017
1018 // NOTE:
1019 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
1020 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
1021 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1022 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1023 // Note that this function is probably obsolete
1024 bool FGTowerController::hasInstruction(int id)
1025 {
1026     TrafficVectorIterator i = activeTraffic.begin();
1027     // Search search if the current id has an entry
1028     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1029     if (! activeTraffic.empty()) {
1030         //while ((i->getId() != id) && i != activeTraffic.end()) {
1031         while (i != activeTraffic.end()) {
1032             if (i->getId() == id) {
1033                 break;
1034             }
1035             i++;
1036         }
1037     }
1038     if (i == activeTraffic.end() || activeTraffic.empty()) {
1039         SG_LOG(SG_ATC, SG_ALERT,
1040                "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1041     } else {
1042         return i->hasInstruction();
1043     }
1044     return false;
1045 }
1046
1047
1048 FGATCInstruction FGTowerController::getInstruction(int id)
1049 {
1050     TrafficVectorIterator i = activeTraffic.begin();
1051     // Search search if the current id has an entry
1052     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1053     if (! activeTraffic.empty()) {
1054         //while ((i->getId() != id) && i != activeTraffic.end()) {
1055         while (i != activeTraffic.end()) {
1056             if (i->getId() == id) {
1057                 break;
1058             }
1059             i++;
1060         }
1061     }
1062     if (i == activeTraffic.end() || activeTraffic.empty()) {
1063         SG_LOG(SG_ATC, SG_ALERT,
1064                "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1065     } else {
1066         return i->getInstruction();
1067     }
1068     return FGATCInstruction();
1069 }
1070
1071 void FGTowerController::render(bool visible) {
1072     //std::cerr << "FGTowerController::render function not yet implemented" << std::endl;
1073 }
1074
1075 string FGTowerController::getName() {
1076     return string(parent->getId() + "-tower");
1077 }
1078
1079 void FGTowerController::update(double dt)
1080 {
1081
1082 }
1083
1084
1085
1086 /***************************************************************************
1087  * class FGStartupController
1088  *
1089  **************************************************************************/
1090 FGStartupController::FGStartupController(FGAirportDynamics *par):
1091         FGATCController()
1092 {
1093     parent = par;
1094 }
1095
1096 void FGStartupController::announcePosition(int id,
1097         FGAIFlightPlan * intendedRoute,
1098         int currentPosition, double lat,
1099         double lon, double heading,
1100         double speed, double alt,
1101         double radius, int leg,
1102         FGAIAircraft * ref)
1103 {
1104     init();
1105     TrafficVectorIterator i = activeTraffic.begin();
1106     // Search whether the current id alread has an entry
1107     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1108     if (! activeTraffic.empty()) {
1109         //while ((i->getId() != id) && i != activeTraffic.end()) {
1110         while (i != activeTraffic.end()) {
1111             if (i->getId() == id) {
1112                 break;
1113             }
1114             i++;
1115         }
1116     }
1117     // Add a new TrafficRecord if no one exsists for this aircraft.
1118     if (i == activeTraffic.end() || activeTraffic.empty()) {
1119         FGTrafficRecord rec;
1120         rec.setId(id);
1121
1122         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1123         rec.setRunway(intendedRoute->getRunway());
1124         rec.setLeg(leg);
1125         rec.setPositionAndIntentions(currentPosition, intendedRoute);
1126         //rec.setCallSign(callsign);
1127         rec.setAircraft(ref);
1128         rec.setHoldPosition(true);
1129         activeTraffic.push_back(rec);
1130     } else {
1131         i->setPositionAndIntentions(currentPosition, intendedRoute);
1132         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1133
1134     }
1135 }
1136
1137 // NOTE:
1138 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
1139 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
1140 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1141 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1142 // Note that this function is probably obsolete
1143 bool FGStartupController::hasInstruction(int id)
1144 {
1145     TrafficVectorIterator i = activeTraffic.begin();
1146     // Search search if the current id has an entry
1147     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1148     if (! activeTraffic.empty()) {
1149         //while ((i->getId() != id) && i != activeTraffic.end()) {
1150         while (i != activeTraffic.end()) {
1151             if (i->getId() == id) {
1152                 break;
1153             }
1154             i++;
1155         }
1156     }
1157     if (i == activeTraffic.end() || activeTraffic.empty()) {
1158         SG_LOG(SG_ATC, SG_ALERT,
1159                "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1160     } else {
1161         return i->hasInstruction();
1162     }
1163     return false;
1164 }
1165
1166
1167 FGATCInstruction FGStartupController::getInstruction(int id)
1168 {
1169     TrafficVectorIterator i = activeTraffic.begin();
1170     // Search search if the current id has an entry
1171     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1172     if (! activeTraffic.empty()) {
1173         //while ((i->getId() != id) && i != activeTraffic.end()) {
1174         while (i != activeTraffic.end()) {
1175             if (i->getId() == id) {
1176                 break;
1177             }
1178             i++;
1179         }
1180     }
1181     if (i == activeTraffic.end() || activeTraffic.empty()) {
1182         SG_LOG(SG_ATC, SG_ALERT,
1183                "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1184     } else {
1185         return i->getInstruction();
1186     }
1187     return FGATCInstruction();
1188 }
1189
1190 void FGStartupController::signOff(int id)
1191 {
1192     TrafficVectorIterator i = activeTraffic.begin();
1193     // Search search if the current id alread has an entry
1194     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1195     if (! activeTraffic.empty()) {
1196         //while ((i->getId() != id) && i != activeTraffic.end()) {
1197         while (i != activeTraffic.end()) {
1198             if (i->getId() == id) {
1199                 break;
1200             }
1201             i++;
1202         }
1203     }
1204     if (i == activeTraffic.end() || activeTraffic.empty()) {
1205         SG_LOG(SG_ATC, SG_ALERT,
1206                "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN);
1207     } else {
1208         //cerr << i->getAircraft()->getCallSign() << " signing off from startupcontroller" << endl;
1209         i = activeTraffic.erase(i);
1210     }
1211 }
1212
1213 bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId,
1214         AtcMsgDir msgDir)
1215 {
1216     int state = i->getState();
1217     if ((state == st) && available) {
1218         if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
1219
1220             //cerr << "Checking state " << st << " for " << i->getAircraft()->getCallSign() << endl;
1221             SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
1222             int n = trans_num->getIntValue();
1223             if (n == 0) {
1224                 trans_num->setIntValue(-1);
1225                 // PopupCallback(n);
1226                 //cerr << "Selected transmission message " << n << endl;
1227                 FGATCDialogNew::instance()->removeEntry(1);
1228             } else {
1229                 //cerr << "creading message for " << i->getAircraft()->getCallSign() << endl;
1230                 transmit(&(*i), &(*parent), msgId, msgDir, false);
1231                 return false;
1232             }
1233         }
1234         if (now > startTime) {
1235             //cerr << "Transmitting startup msg" << endl;
1236             transmit(&(*i), &(*parent), msgId, msgDir, true);
1237             i->updateState();
1238             lastTransmission = now;
1239             available = false;
1240             return true;
1241         }
1242     }
1243     return false;
1244 }
1245
1246 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
1247         double heading, double speed, double alt,
1248         double dt)
1249 {
1250     TrafficVectorIterator i = activeTraffic.begin();
1251     // Search search if the current id has an entry
1252     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1253     TrafficVectorIterator current, closest;
1254     if (! activeTraffic.empty()) {
1255         //while ((i->getId() != id) && i != activeTraffic.end()) {
1256         while (i != activeTraffic.end()) {
1257             if (i->getId() == id) {
1258                 break;
1259             }
1260             i++;
1261         }
1262     }
1263 //    // update position of the current aircraft
1264
1265     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1266         SG_LOG(SG_ATC, SG_ALERT,
1267                "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
1268     } else {
1269         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1270         current = i;
1271     }
1272     setDt(getDt() + dt);
1273
1274     int state = i->getState();
1275
1276     // The user controlled aircraft should have crased here, because it doesn't have a traffic reference.
1277     // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
1278     time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
1279     time_t now = globals->get_time_params()->get_cur_time();
1280
1281     //cerr << i->getAircraft()->getTrafficRef()->getCallSign()
1282     //     << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1283     //     << " at parking " << getGateName(i->getAircraft()) << endl;
1284
1285     if ((now - lastTransmission) > 3 + (rand() % 15)) {
1286         available = true;
1287     }
1288
1289     checkTransmissionState(0, now, (startTime + 0  ), i, MSG_ANNOUNCE_ENGINE_START,                     ATC_AIR_TO_GROUND);
1290     checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START,                      ATC_AIR_TO_GROUND);
1291     checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START,                       ATC_GROUND_TO_AIR);
1292     checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START,                  ATC_AIR_TO_GROUND);
1293     if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,       ATC_AIR_TO_GROUND)) {
1294         i->nextFrequency();
1295     }
1296     checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT,                          ATC_AIR_TO_GROUND);
1297     checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT,              ATC_GROUND_TO_AIR);
1298     checkTransmissionState(7, now, (startTime + 180), i, MSG_REQUEST_PUSHBACK_CLEARANCE,                ATC_AIR_TO_GROUND);
1299
1300
1301
1302     if ((state == 8) && available) {
1303         if (now > startTime + 200) {
1304             if (i->pushBackAllowed()) {
1305                 i->allowRepeatedTransmissions();
1306                 transmit(&(*i), &(*parent), MSG_PERMIT_PUSHBACK_CLEARANCE,
1307                          ATC_GROUND_TO_AIR, true);
1308                 i->updateState();
1309             } else {
1310                 transmit(&(*i), &(*parent), MSG_HOLD_PUSHBACK_CLEARANCE,
1311                          ATC_GROUND_TO_AIR, true);
1312                 i->suppressRepeatedTransmissions();
1313             }
1314             lastTransmission = now;
1315             available = false;
1316         }
1317     }
1318     if ((state == 9) && available) {
1319         i->setHoldPosition(false);
1320     }
1321 }
1322
1323 // Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
1324 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
1325                             double lon, double elev, double hdg, double slope)
1326 {
1327     SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
1328     obj_pos = makeZUpFrame(geod);
1329     // hdg is not a compass heading, but a counter-clockwise rotation
1330     // around the Z axis
1331     obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
1332                                         0.0, 0.0, 1.0));
1333     obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
1334                                         0.0, 1.0, 0.0));
1335 }
1336
1337
1338 void FGStartupController::render(bool visible)
1339 {
1340     //std::cerr << "Rendering startup controller" << std::endl;
1341     SGMaterialLib *matlib = globals->get_matlib();
1342     if (group) {
1343         //int nr = ;
1344         globals->get_scenery()->get_scene_graph()->removeChild(group);
1345         //while (group->getNumChildren()) {
1346         //  cerr << "Number of children: " << group->getNumChildren() << endl;
1347         //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
1348         //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
1349         //geode->releaseGLObjects();
1350         //group->removeChild(geode);
1351         //delete geode;
1352         group = 0;
1353     }
1354     if (visible) {
1355         group = new osg::Group;
1356         FGScenery * local_scenery = globals->get_scenery();
1357         //double elevation_meters = 0.0;
1358         //double elevation_feet = 0.0;
1359
1360
1361         //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
1362         double dx = 0;
1363         time_t now = globals->get_time_params()->get_cur_time();
1364
1365         for   (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1366             if (i->isActive(300)) {
1367                 // Handle start point
1368                 int pos = i->getCurrentPosition();
1369                 //cerr << "rendering for " << i->getAircraft()->getCallSign() << "pos = " << pos << endl;
1370                 if (pos > 0) {
1371                     FGTaxiSegment *segment  = parent->getGroundNetwork()->findSegment(pos);
1372                     SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1373                     SGGeod end  (segment->getEnd()->geod());
1374
1375                     double length = SGGeodesy::distanceM(start, end);
1376                     //heading = SGGeodesy::headingDeg(start->geod(), end->geod());
1377
1378                     double az2, heading; //, distanceM;
1379                     SGGeodesy::inverse(start, end, heading, az2, length);
1380                     double coveredDistance = length * 0.5;
1381                     SGGeod center;
1382                     SGGeodesy::direct(start, heading, coveredDistance, center, az2);
1383                     //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
1384                     ///////////////////////////////////////////////////////////////////////////////
1385                     // Make a helper function out of this
1386                     osg::Matrix obj_pos;
1387                     osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1388                     obj_trans->setDataVariance(osg::Object::STATIC);
1389                     // Experimental: Calculate slope here, based on length, and the individual elevations
1390                     double elevationStart;
1391                     if (isUserAircraft((i)->getAircraft())) {
1392                         elevationStart = fgGetDouble("/position/ground-elev-m");
1393                     } else {
1394                         elevationStart = ((i)->getAircraft()->_getAltitude() * SG_FEET_TO_METER);
1395                     }
1396                     double elevationEnd   = segment->getEnd()->getElevationM();
1397                     if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1398                         SGGeod center2 = end;
1399                         center2.setElevationM(SG_MAX_ELEVATION_M);
1400                         if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1401                             //elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1402                             //elevation_meters += 0.5;
1403                         }
1404                         else {
1405                             elevationEnd = parent->getElevation();
1406                         }
1407                         segment->getEnd()->setElevation(elevationEnd);
1408                     }
1409
1410                     double elevationMean  = (elevationStart + elevationEnd) / 2.0;
1411                     double elevDiff       = elevationEnd - elevationStart;
1412
1413                     double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1414
1415                     //cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
1416
1417                     WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean + 0.5 + dx, -(heading), slope );
1418                     ;
1419
1420                     obj_trans->setMatrix( obj_pos );
1421                     //osg::Vec3 center(0, 0, 0)
1422
1423                     float width = length /2.0;
1424                     osg::Vec3 corner(-width, 0, 0.25f);
1425                     osg::Vec3 widthVec(2*width + 1, 0, 0);
1426                     osg::Vec3 heightVec(0, 1, 0);
1427                     osg::Geometry* geometry;
1428                     geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1429                     simgear::EffectGeode* geode = new simgear::EffectGeode;
1430                     geode->setName("test");
1431                     geode->addDrawable(geometry);
1432                     //osg::Node *custom_obj;
1433                     SGMaterial *mat;
1434                     if (segment->hasBlock(now)) {
1435                         mat = matlib->find("UnidirectionalTaperRed", center);
1436                     } else {
1437                         mat = matlib->find("UnidirectionalTaperGreen", center);
1438                     }
1439                     if (mat)
1440                         geode->setEffect(mat->get_effect());
1441                     obj_trans->addChild(geode);
1442                     // wire as much of the scene graph together as we can
1443                     //->addChild( obj_trans );
1444                     group->addChild( obj_trans );
1445                     /////////////////////////////////////////////////////////////////////
1446                 } else {
1447                     //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
1448                 }
1449                 for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
1450                     osg::Matrix obj_pos;
1451                     int k = (*j);
1452                     if (k > 0) {
1453                         //cerr << "rendering for " << i->getAircraft()->getCallSign() << "intention = " << k << endl;
1454                         osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1455                         obj_trans->setDataVariance(osg::Object::STATIC);
1456                         FGTaxiSegment *segment  = parent->getGroundNetwork()->findSegment(k);
1457
1458                         double elevationStart = segment->getStart()->getElevationM();
1459                         double elevationEnd   = segment->getEnd  ()->getElevationM();
1460                         if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
1461                             SGGeod center2 = segment->getStart()->geod();
1462                             center2.setElevationM(SG_MAX_ELEVATION_M);
1463                             if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
1464                                 //elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
1465                                 //elevation_meters += 0.5;
1466                             }
1467                             else {
1468                                 elevationStart = parent->getElevation();
1469                             }
1470                             segment->getStart()->setElevation(elevationStart);
1471                         }
1472                         if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1473                             SGGeod center2 = segment->getEnd()->geod();
1474                             center2.setElevationM(SG_MAX_ELEVATION_M);
1475                             if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1476                                 //elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1477                                 //elevation_meters += 0.5;
1478                             }
1479                             else {
1480                                 elevationEnd = parent->getElevation();
1481                             }
1482                             segment->getEnd()->setElevation(elevationEnd);
1483                         }
1484
1485                         double elevationMean  = (elevationStart + elevationEnd) / 2.0;
1486                         double elevDiff       = elevationEnd - elevationStart;
1487                         double length         = segment->getLength();
1488                         double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1489
1490                         //cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
1491
1492                         SGGeod segCenter(segment->getCenter());
1493                         WorldCoordinate( obj_pos, segCenter.getLatitudeDeg(),
1494                                         segCenter.getLongitudeDeg(), elevationMean + 0.5 + dx, -(segment->getHeading()), slope );
1495
1496                         //WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), parent->getElevation()+8+dx, -(segment->getHeading()) );
1497
1498                         obj_trans->setMatrix( obj_pos );
1499                         //osg::Vec3 center(0, 0, 0)
1500
1501                         float width = segment->getLength() /2.0;
1502                         osg::Vec3 corner(-width, 0, 0.25f);
1503                         osg::Vec3 widthVec(2*width + 1, 0, 0);
1504                         osg::Vec3 heightVec(0, 1, 0);
1505                         osg::Geometry* geometry;
1506                         geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1507                         simgear::EffectGeode* geode = new simgear::EffectGeode;
1508                         geode->setName("test");
1509                         geode->addDrawable(geometry);
1510                         //osg::Node *custom_obj;
1511                         SGMaterial *mat;
1512                         if (segment->hasBlock(now)) {
1513                             mat = matlib->find("UnidirectionalTaperRed", segCenter);
1514                         } else {
1515                             mat = matlib->find("UnidirectionalTaperGreen", segCenter);
1516                         }
1517                         if (mat)
1518                             geode->setEffect(mat->get_effect());
1519                         obj_trans->addChild(geode);
1520                         // wire as much of the scene graph together as we can
1521                         //->addChild( obj_trans );
1522                         group->addChild( obj_trans );
1523                     } else {
1524                         //cerr << "BIG FAT WARNING: k is here : " << pos << endl;
1525                     }
1526                 }
1527                 dx += 0.2;
1528             }
1529         }
1530         globals->get_scenery()->get_scene_graph()->addChild(group);
1531     }
1532 }
1533
1534 string FGStartupController::getName() {
1535     return string(parent->getId() + "-startup");
1536 }
1537
1538 void FGStartupController::update(double dt)
1539 {
1540
1541 }
1542
1543
1544
1545 /***************************************************************************
1546  * class FGApproachController
1547  *
1548  **************************************************************************/
1549 FGApproachController::FGApproachController(FGAirportDynamics *par):
1550         FGATCController()
1551 {
1552     parent = par;
1553 }
1554
1555 //
1556 void FGApproachController::announcePosition(int id,
1557         FGAIFlightPlan * intendedRoute,
1558         int currentPosition,
1559         double lat, double lon,
1560         double heading, double speed,
1561         double alt, double radius,
1562         int leg, FGAIAircraft * ref)
1563 {
1564     init();
1565     TrafficVectorIterator i = activeTraffic.begin();
1566     // Search whether the current id alread has an entry
1567     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1568     if (! activeTraffic.empty()) {
1569         //while ((i->getId() != id) && i != activeTraffic.end()) {
1570         while (i != activeTraffic.end()) {
1571             if (i->getId() == id) {
1572                 break;
1573             }
1574             i++;
1575         }
1576     }
1577     // Add a new TrafficRecord if no one exsists for this aircraft.
1578     if (i == activeTraffic.end() || activeTraffic.empty()) {
1579         FGTrafficRecord rec;
1580         rec.setId(id);
1581
1582         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1583         rec.setRunway(intendedRoute->getRunway());
1584         rec.setLeg(leg);
1585         //rec.setCallSign(callsign);
1586         rec.setAircraft(ref);
1587         activeTraffic.push_back(rec);
1588     } else {
1589         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1590     }
1591 }
1592
1593 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1594         double heading, double speed, double alt,
1595         double dt)
1596 {
1597     TrafficVectorIterator i = activeTraffic.begin();
1598     // Search search if the current id has an entry
1599     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1600     TrafficVectorIterator current, closest;
1601     if (! activeTraffic.empty()) {
1602         //while ((i->getId() != id) && i != activeTraffic.end()) {
1603         while (i != activeTraffic.end()) {
1604             if (i->getId() == id) {
1605                 break;
1606             }
1607             i++;
1608         }
1609     }
1610 //    // update position of the current aircraft
1611     if (i == activeTraffic.end() || activeTraffic.empty()) {
1612         SG_LOG(SG_ATC, SG_ALERT,
1613                "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
1614     } else {
1615         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1616         current = i;
1617         //cerr << "ApproachController: checking for speed" << endl;
1618         time_t time_diff =
1619             current->getAircraft()->
1620             checkForArrivalTime(string("final001"));
1621         if (time_diff > 15) {
1622             current->setSpeedAdjustment(current->getAircraft()->
1623                                         getPerformance()->vDescent() *
1624                                         1.35);
1625         } else if (time_diff > 5) {
1626             current->setSpeedAdjustment(current->getAircraft()->
1627                                         getPerformance()->vDescent() *
1628                                         1.2);
1629         } else if (time_diff < -15) {
1630             current->setSpeedAdjustment(current->getAircraft()->
1631                                         getPerformance()->vDescent() *
1632                                         0.65);
1633         } else if (time_diff < -5) {
1634             current->setSpeedAdjustment(current->getAircraft()->
1635                                         getPerformance()->vDescent() *
1636                                         0.8);
1637         } else {
1638             current->clearSpeedAdjustment();
1639         }
1640         //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1641     }
1642     setDt(getDt() + dt);
1643 }
1644
1645 void FGApproachController::signOff(int id)
1646 {
1647     TrafficVectorIterator i = activeTraffic.begin();
1648     // Search search if the current id alread has an entry
1649     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1650     if (! activeTraffic.empty()) {
1651         //while ((i->getId() != id) && i != activeTraffic.end()) {
1652         while (i != activeTraffic.end()) {
1653             if (i->getId() == id) {
1654                 break;
1655             }
1656             i++;
1657         }
1658     }
1659     if (i == activeTraffic.end() || activeTraffic.empty()) {
1660         SG_LOG(SG_ATC, SG_ALERT,
1661                "AI error: Aircraft without traffic record is signing off from approach at " << SG_ORIGIN);
1662     } else {
1663         i = activeTraffic.erase(i);
1664     }
1665 }
1666
1667 void FGApproachController::update(double dt)
1668 {
1669
1670 }
1671
1672
1673
1674 bool FGApproachController::hasInstruction(int id)
1675 {
1676     TrafficVectorIterator i = activeTraffic.begin();
1677     // Search search if the current id has an entry
1678     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1679     if (! activeTraffic.empty()) {
1680         //while ((i->getId() != id) && i != activeTraffic.end()) {
1681         while (i != activeTraffic.end()) {
1682             if (i->getId() == id) {
1683                 break;
1684             }
1685             i++;
1686         }
1687     }
1688     if (i == activeTraffic.end() || activeTraffic.empty()) {
1689         SG_LOG(SG_ATC, SG_ALERT,
1690                "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1691     } else {
1692         return i->hasInstruction();
1693     }
1694     return false;
1695 }
1696
1697
1698 FGATCInstruction FGApproachController::getInstruction(int id)
1699 {
1700     TrafficVectorIterator i = activeTraffic.begin();
1701     // Search search if the current id has an entry
1702     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1703     if (activeTraffic.size()) {
1704         //while ((i->getId() != id) && i != activeTraffic.end()) {
1705         while (i != activeTraffic.end()) {
1706             if (i->getId() == id) {
1707                 break;
1708             }
1709             i++;
1710         }
1711     }
1712     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1713         SG_LOG(SG_ATC, SG_ALERT,
1714                "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1715     } else {
1716         return i->getInstruction();
1717     }
1718     return FGATCInstruction();
1719 }
1720
1721
1722 ActiveRunway *FGApproachController::getRunway(const string& name)
1723 {
1724     ActiveRunwayVecIterator rwy = activeRunways.begin();
1725     if (activeRunways.size()) {
1726         while (rwy != activeRunways.end()) {
1727             if (rwy->getRunwayName() == name) {
1728                 break;
1729             }
1730             rwy++;
1731         }
1732     }
1733     if (rwy == activeRunways.end()) {
1734         ActiveRunway aRwy(name, 0);
1735         activeRunways.push_back(aRwy);
1736         rwy = activeRunways.end() - 1;
1737     }
1738     return &(*rwy);
1739 }
1740
1741 void FGApproachController::render(bool visible) {
1742     //std::cerr << "FGApproachController::render function not yet implemented" << std::endl;
1743 }
1744
1745
1746
1747 string FGApproachController::getName() {
1748     return string(parent->getId() + "-approach");
1749 }