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