]> git.mxchange.org Git - flightgear.git/blob - src/ATC/trafficcontrol.cxx
traffic: more pass by reference + member init
[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 <simgear/scene/util/OsgMath.hxx>
38 #include <Scenery/scenery.hxx>
39
40 #include "trafficcontrol.hxx"
41 #include "atc_mgr.hxx"
42 #include <AIModel/AIAircraft.hxx>
43 #include <AIModel/AIFlightPlan.hxx>
44 #include <AIModel/performancedata.hxx>
45 #include <AIModel/performancedb.hxx>
46 #include <ATC/atc_mgr.hxx>
47 #include <Traffic/TrafficMgr.hxx>
48 #include <Airports/groundnetwork.hxx>
49 #include <Airports/dynamics.hxx>
50 #include <Airports/simple.hxx>
51 #include <Radio/radio.hxx>
52 #include <signal.h>
53
54 using std::sort;
55 using std::string;
56 using std::cout;
57 using std::endl;
58
59 /***************************************************************************
60  * ActiveRunway
61  **************************************************************************/
62 time_t ActiveRunway::requestTimeSlot(time_t eta)
63 {
64     time_t newEta;
65     time_t separation = 90;
66     bool found = false;
67     if (estimatedArrivalTimes.size() == 0) {
68         estimatedArrivalTimes.push_back(eta);
69         return eta;
70     } else {
71         TimeVectorIterator i = estimatedArrivalTimes.begin();
72         //cerr << "Checking eta slots " << eta << ": " << endl;
73         for (i = estimatedArrivalTimes.begin();
74                 i != estimatedArrivalTimes.end(); i++) {
75             //cerr << "Stored time : " << (*i) << endl;
76         }
77         i = estimatedArrivalTimes.begin();
78         if ((eta + separation) < (*i)) {
79             newEta = eta;
80             found = true;
81             //cerr << "Storing at beginning" << endl;
82         }
83         while ((i != estimatedArrivalTimes.end()) && (!found)) {
84             TimeVectorIterator j = i + 1;
85             if (j == estimatedArrivalTimes.end()) {
86                 if (((*i) + separation) < eta) {
87                     //cerr << "Storing at end" << endl;
88                     newEta = eta;
89                 } else {
90                     newEta = (*i) + separation;
91                     //cerr << "Storing at end + separation" << endl;
92                 }
93             } else {
94                 if ((((*j) - (*i)) > (separation * 2))) {       // found a potential slot
95                     // now check whether this slot is usable:
96                     // 1) eta should fall between the two points
97                     //    i.e. eta > i AND eta < j
98                     //
99                     //cerr << "Found potential slot after " << (*i) << endl;
100                     if (eta > (*i) && (eta < (*j))) {
101                         found = true;
102                         if (eta < ((*i) + separation)) {
103                             newEta = (*i) + separation;
104                             //cerr << "Using  original" << (*i) << " + separation " << endl;
105                         } else {
106                             newEta = eta;
107                             //cerr << "Using original after " << (*i) << endl;
108                         }
109                     } else if (eta < (*i)) {
110                         found = true;
111                         newEta = (*i) + separation;
112                         //cerr << "Using delayed slot after " << (*i) << endl;
113                     }
114                     /*
115                        if (((*j) - separation) < eta) {
116                        found = true;
117                        if (((*i) + separation) < eta) {
118                        newEta = eta;
119                        cerr << "Using original after " << (*i) << endl;
120                        } else {
121                        newEta = (*i) + separation;
122                        cerr << "Using  " << (*i) << " + separation " << endl;
123                        }
124                        } */
125                 }
126             }
127             i++;
128         }
129     }
130     //cerr << ". done. New ETA : " << newEta << endl;
131
132     estimatedArrivalTimes.push_back(newEta);
133     sort(estimatedArrivalTimes.begin(), estimatedArrivalTimes.end());
134     // do some housekeeping : remove any timestamps that are past
135     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
136     TimeVectorIterator i = estimatedArrivalTimes.begin();
137     while (i != estimatedArrivalTimes.end()) {
138         if ((*i) < now) {
139             //cerr << "Deleting timestamp " << (*i) << " (now = " << now << "). " << endl;
140             estimatedArrivalTimes.erase(i);
141             i = estimatedArrivalTimes.begin();
142         } else {
143             i++;
144         }
145     }
146     return newEta;
147 }
148
149 void ActiveRunway::printDepartureCue()
150 {
151     cout << "Departure cue for " << rwy << ": " << endl;
152     for (AircraftVecIterator atc = departureCue.begin(); atc != departureCue.end(); atc++) {
153         cout << "     " << (*atc)->getCallSign() << " "  << (*atc)->getTakeOffStatus();
154         cout << " " << (*atc)->_getLatitude() << " " << (*atc)->_getLongitude() << (*atc)-> getSpeed() << " " << (*atc)->getAltitude() << endl;
155     }
156     
157 }
158
159 FGAIAircraft* ActiveRunway::getFirstOfStatus(int stat)
160 {
161     for (AircraftVecIterator atc =departureCue.begin(); atc != departureCue.end(); atc++) {
162         if ((*atc)->getTakeOffStatus() == stat)
163             return (*atc);
164     }
165     return 0;
166 }
167
168
169
170 /***************************************************************************
171  * FGTrafficRecord
172  **************************************************************************/
173 FGTrafficRecord::FGTrafficRecord():
174         id(0), waitsForId(0),
175         currentPos(0),
176         leg(0),
177         frequencyId(0),
178         state(0),
179         allowTransmission(true),
180         allowPushback(true),
181         priority(0),
182         timer(0),
183         latitude(0), longitude(0), heading(0), speed(0), altitude(0), radius(0),
184         aircraft(NULL)
185 {
186 }
187
188 void FGTrafficRecord::setPositionAndIntentions(int pos,
189         FGAIFlightPlan * route)
190 {
191
192     currentPos = pos;
193     if (intentions.size()) {
194         intVecIterator i = intentions.begin();
195         if ((*i) != pos) {
196             SG_LOG(SG_ATC, SG_ALERT,
197                    "Error in FGTrafficRecord::setPositionAndIntentions at " << SG_ORIGIN);
198         }
199         intentions.erase(i);
200     } else {
201         //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
202         int size = route->getNrOfWayPoints();
203         //cerr << "Setting pos" << pos << " ";
204         //cerr << "setting intentions ";
205         for (int i = 2; i < size; i++) {
206             int val = route->getRouteIndex(i);
207             intentions.push_back(val);
208         }
209     }
210 }
211 /**
212  * Check if another aircraft is ahead of the current one, and on the same
213  * return true / false is the is/isn't the case.
214  *
215  ****************************************************************************/
216
217 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other)
218 {
219     bool result = false;
220     //cerr << "Start check 1" << endl;
221     if (currentPos == other.currentPos) {
222         //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
223         result = true;
224     }
225     //  else if (other.intentions.size())
226     //     {
227     //       cerr << "Start check 2" << endl;
228     //       intVecIterator i = other.intentions.begin();
229     //       while (!((i == other.intentions.end()) || ((*i) == currentPos)))
230     //     i++;
231     //       if (i != other.intentions.end()) {
232     //     cerr << "Check Position and intentions: current matches other.intentions" << endl;
233     //     result = true;
234     //       }
235     else if (intentions.size()) {
236         //cerr << "Start check 3" << endl;
237         intVecIterator i = intentions.begin();
238         //while (!((i == intentions.end()) || ((*i) == other.currentPos)))
239         while (i != intentions.end()) {
240             if ((*i) == other.currentPos) {
241                 break;
242             }
243             i++;
244         }
245         if (i != intentions.end()) {
246             //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
247             result = true;
248         }
249     }
250     //cerr << "Done !!" << endl;
251     return result;
252 }
253
254 void FGTrafficRecord::setPositionAndHeading(double lat, double lon,
255         double hdg, double spd,
256         double alt)
257 {
258     latitude = lat;
259     longitude = lon;
260     heading = hdg;
261     speed = spd;
262     altitude = alt;
263 }
264
265 int FGTrafficRecord::crosses(FGGroundNetwork * net,
266                              FGTrafficRecord & other)
267 {
268     if (checkPositionAndIntentions(other)
269             || (other.checkPositionAndIntentions(*this)))
270         return -1;
271     intVecIterator i, j;
272     int currentTargetNode = 0, otherTargetNode = 0;
273     if (currentPos > 0)
274         currentTargetNode = net->findSegment(currentPos)->getEnd()->getIndex(); // OKAY,...
275     if (other.currentPos > 0)
276         otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex();     // OKAY,...
277     if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
278         return currentTargetNode;
279     if (intentions.size()) {
280         for (i = intentions.begin(); i != intentions.end(); i++) {
281             if ((*i) > 0) {
282                 if (currentTargetNode ==
283                         net->findSegment(*i)->getEnd()->getIndex()) {
284                     //cerr << "Current crosses at " << currentTargetNode <<endl;
285                     return currentTargetNode;
286                 }
287             }
288         }
289     }
290     if (other.intentions.size()) {
291         for (i = other.intentions.begin(); i != other.intentions.end();
292                 i++) {
293             if ((*i) > 0) {
294                 if (otherTargetNode ==
295                         net->findSegment(*i)->getEnd()->getIndex()) {
296                     //cerr << "Other crosses at " << currentTargetNode <<endl;
297                     return otherTargetNode;
298                 }
299             }
300         }
301     }
302     if (intentions.size() && other.intentions.size()) {
303         for (i = intentions.begin(); i != intentions.end(); i++) {
304             for (j = other.intentions.begin(); j != other.intentions.end();
305                     j++) {
306                 //cerr << "finding segment " << *i << " and " << *j << endl;
307                 if (((*i) > 0) && ((*j) > 0)) {
308                     currentTargetNode =
309                         net->findSegment(*i)->getEnd()->getIndex();
310                     otherTargetNode =
311                         net->findSegment(*j)->getEnd()->getIndex();
312                     if (currentTargetNode == otherTargetNode) {
313                         //cerr << "Routes will cross at " << currentTargetNode << endl;
314                         return currentTargetNode;
315                     }
316                 }
317             }
318         }
319     }
320     return -1;
321 }
322
323 bool FGTrafficRecord::onRoute(FGGroundNetwork * net,
324                               FGTrafficRecord & other)
325 {
326     int node = -1, othernode = -1;
327     if (currentPos > 0)
328         node = net->findSegment(currentPos)->getEnd()->getIndex();
329     if (other.currentPos > 0)
330         othernode =
331             net->findSegment(other.currentPos)->getEnd()->getIndex();
332     if ((node == othernode) && (node != -1))
333         return true;
334     if (other.intentions.size()) {
335         for (intVecIterator i = other.intentions.begin();
336                 i != other.intentions.end(); i++) {
337             if (*i > 0) {
338                 othernode = net->findSegment(*i)->getEnd()->getIndex();
339                 if ((node == othernode) && (node > -1))
340                     return true;
341             }
342         }
343     }
344     //if (other.currentPos > 0)
345     //  othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
346     //if (intentions.size())
347     //  {
348     //    for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
349     //    {
350     //      if (*i > 0)
351     //        {
352     //          node = net->findSegment(*i)->getEnd()->getIndex();
353     //          if ((node == othernode) && (node > -1))
354     //            return true;
355     //        }
356     //    }
357     //  }
358     return false;
359 }
360
361
362 bool FGTrafficRecord::isOpposing(FGGroundNetwork * net,
363                                  FGTrafficRecord & other, int node)
364 {
365     // Check if current segment is the reverse segment for the other aircraft
366     FGTaxiSegment *opp;
367     //cerr << "Current segment " << currentPos << endl;
368     if ((currentPos > 0) && (other.currentPos > 0)) {
369         opp = net->findSegment(currentPos)->opposite();
370         if (opp) {
371             if (opp->getIndex() == other.currentPos)
372                 return true;
373         }
374
375         for (intVecIterator i = intentions.begin(); i != intentions.end();
376                 i++) {
377             if ((opp = net->findSegment(other.currentPos)->opposite())) {
378                 if ((*i) > 0)
379                     if (opp->getIndex() ==
380                             net->findSegment(*i)->getIndex()) {
381                         if (net->findSegment(*i)->getStart()->getIndex() ==
382                                 node) {
383                             {
384                                 //cerr << "Found the node " << node << endl;
385                                 return true;
386                             }
387                         }
388                     }
389             }
390             if (other.intentions.size()) {
391                 for (intVecIterator j = other.intentions.begin();
392                         j != other.intentions.end(); j++) {
393                     // cerr << "Current segment 1 " << (*i) << endl;
394                     if ((*i) > 0) {
395                         if ((opp = net->findSegment(*i)->opposite())) {
396                             if (opp->getIndex() ==
397                                     net->findSegment(*j)->getIndex()) {
398                                 //cerr << "Nodes " << net->findSegment(*i)->getIndex()
399                                 //   << " and  " << net->findSegment(*j)->getIndex()
400                                 //   << " are opposites " << endl;
401                                 if (net->findSegment(*i)->getStart()->
402                                         getIndex() == node) {
403                                     {
404                                         //cerr << "Found the node " << node << endl;
405                                         return true;
406                                     }
407                                 }
408                             }
409                         }
410                     }
411                 }
412             }
413         }
414     }
415     return false;
416 }
417
418 bool FGTrafficRecord::isActive(int margin) const
419 {
420     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
421     time_t deptime = aircraft->getTrafficRef()->getDepartureTime();
422     return ((now + margin) > deptime);
423 }
424
425
426 void FGTrafficRecord::setSpeedAdjustment(double spd)
427 {
428     instruction.setChangeSpeed(true);
429     instruction.setSpeed(spd);
430 }
431
432 void FGTrafficRecord::setHeadingAdjustment(double heading)
433 {
434     instruction.setChangeHeading(true);
435     instruction.setHeading(heading);
436 }
437
438 bool FGTrafficRecord::pushBackAllowed() const
439 {
440     return allowPushback;
441 }
442
443
444
445
446 /***************************************************************************
447  * FGATCInstruction
448  *
449  **************************************************************************/
450 FGATCInstruction::FGATCInstruction()
451 {
452     holdPattern = false;
453     holdPosition = false;
454     changeSpeed = false;
455     changeHeading = false;
456     changeAltitude = false;
457     resolveCircularWait = false;
458
459     speed = 0;
460     heading = 0;
461     alt = 0;
462 }
463
464
465 bool FGATCInstruction::hasInstruction() const
466 {
467     return (holdPattern || holdPosition || changeSpeed || changeHeading
468             || changeAltitude || resolveCircularWait);
469 }
470
471 /***************************************************************************
472  * FGATCController
473  *
474  **************************************************************************/
475
476
477
478
479 FGATCController::FGATCController()
480 {
481     //cerr << "running FGATController constructor" << endl;
482     dt_count = 0;
483     available = true;
484     lastTransmission = 0;
485     initialized = false;
486     lastTransmissionDirection = ATC_AIR_TO_GROUND;
487     group = NULL;
488 }
489
490 FGATCController::~FGATCController()
491 {
492     //cerr << "running FGATController destructor" << endl;
493 }
494
495 string FGATCController::getGateName(FGAIAircraft * ref)
496 {
497     return ref->atGate();
498 }
499
500 bool FGATCController::isUserAircraft(FGAIAircraft* ac)
501 {
502     return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false;
503 };
504
505 void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, AtcMsgId msgId,
506                                AtcMsgDir msgDir, bool audible)
507 {
508     string sender, receiver;
509     int stationFreq = 0;
510     int taxiFreq = 0;
511     int towerFreq = 0;
512     int freqId = 0;
513     string atisInformation;
514     string text;
515     string taxiFreqStr;
516     string towerFreqStr;
517     double heading = 0;
518     string activeRunway;
519     string fltType;
520     string rwyClass;
521     string SID;
522     string transponderCode;
523     FGAIFlightPlan *fp;
524     string fltRules;
525     string instructionText;
526     int ground_to_air=0;
527
528     //double commFreqD;
529     sender = rec->getAircraft()->getTrafficRef()->getCallSign();
530     if (rec->getAircraft()->getTaxiClearanceRequest()) {
531         instructionText = "push-back and taxi";
532     } else {
533         instructionText = "taxi";
534     }
535     //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
536     switch (rec->getLeg()) {
537     case 1:
538     case 2:
539         freqId = rec->getNextFrequency();
540         stationFreq =
541             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
542             getDynamics()->getGroundFrequency(rec->getLeg() + freqId);
543         taxiFreq =
544             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
545             getDynamics()->getGroundFrequency(2);
546         towerFreq =
547             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
548             getDynamics()->getTowerFrequency(2);
549         receiver =
550             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
551             getName() + "-Ground";
552         atisInformation =
553             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
554             getDynamics()->getAtisSequence();
555         break;
556     case 3:
557         receiver =
558             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
559             getName() + "-Tower";
560         break;
561     }
562     // Swap sender and receiver value in case of a ground to air transmission
563     if (msgDir == ATC_GROUND_TO_AIR) {
564         string tmp = sender;
565         sender = receiver;
566         receiver = tmp;
567         ground_to_air=1;
568     }
569     switch (msgId) {
570     case MSG_ANNOUNCE_ENGINE_START:
571         text = sender + ". Ready to Start up";
572         break;
573     case MSG_REQUEST_ENGINE_START:
574         text =
575             receiver + ", This is " + sender + ". Position " +
576             getGateName(rec->getAircraft()) + ". Information " +
577             atisInformation + ". " +
578             rec->getAircraft()->getTrafficRef()->getFlightRules() +
579             " to " +
580             rec->getAircraft()->getTrafficRef()->getArrivalAirport()->
581             getName() + ". Request start-up";
582         break;
583         // Acknowledge engine startup permission
584         // Assign departure runway
585         // Assign SID, if necessery (TODO)
586     case MSG_PERMIT_ENGINE_START:
587         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
588
589         heading = rec->getAircraft()->getTrafficRef()->getCourse();
590         fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
591         rwyClass =
592             rec->getAircraft()->GetFlightPlan()->
593             getRunwayClassFromTrafficType(fltType);
594
595         rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
596         getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
597                                        heading);
598         rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
599         fp = NULL;
600         rec->getAircraft()->GetFlightPlan()->setSID(fp);
601         if (fp) {
602             SID = fp->getName() + " departure";
603         } else {
604             SID = "fly runway heading ";
605         }
606         //snprintf(buffer, 7, "%3.2f", heading);
607         fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
608         transponderCode = genTransponderCode(fltRules);
609         rec->getAircraft()->SetTransponderCode(transponderCode);
610         text =
611             receiver + ". Start-up approved. " + atisInformation +
612             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
613             transponderCode + ". " +
614             "For "+ instructionText + " clearance call " + taxiFreqStr + ". " +
615             sender + " control.";
616         break;
617     case MSG_DENY_ENGINE_START:
618         text = receiver + ". Standby";
619         break;
620     case MSG_ACKNOWLEDGE_ENGINE_START:
621         fp = rec->getAircraft()->GetFlightPlan()->getSID();
622         if (fp) {
623             SID =
624                 rec->getAircraft()->GetFlightPlan()->getSID()->getName() +
625                 " departure";
626         } else {
627             SID = "fly runway heading ";
628         }
629         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
630         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
631         transponderCode = rec->getAircraft()->GetTransponderCode();
632
633         text =
634             receiver + ". Start-up approved. " + atisInformation +
635             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
636             transponderCode + ". " +
637             "For " + instructionText + " clearance call " + taxiFreqStr + ". " +
638             sender;
639         break;
640     case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
641         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
642         text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
643         break;
644     case MSG_INITIATE_CONTACT:
645         text = receiver + ". With you. " + sender;
646         break;
647     case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
648         text = receiver + ". Roger. " + sender;
649         break;
650     case MSG_REQUEST_PUSHBACK_CLEARANCE:
651         if (rec->getAircraft()->getTaxiClearanceRequest()) {
652             text = receiver + ". Request push-back. " + sender;
653         } else {
654             text = receiver + ". Request Taxi clearance. " + sender;
655         }
656         break;
657     case MSG_PERMIT_PUSHBACK_CLEARANCE:
658         if (rec->getAircraft()->getTaxiClearanceRequest()) {
659             text = receiver + ". Push-back approved. " + sender;
660         } else {
661             text = receiver + ". Cleared to Taxi." + sender;
662         }
663         break;
664     case MSG_HOLD_PUSHBACK_CLEARANCE:
665         text = receiver + ". Standby. " + sender;
666         break;
667     case MSG_REQUEST_TAXI_CLEARANCE:
668         text = receiver + ". Ready to Taxi. " + sender;
669         break;
670     case MSG_ISSUE_TAXI_CLEARANCE:
671         text = receiver + ". Cleared to taxi. " + sender;
672         break;
673     case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
674         text = receiver + ". Cleared to taxi. " + sender;
675         break;
676     case MSG_HOLD_POSITION:
677         text = receiver + ". Hold Position. " + sender;
678         break;
679     case MSG_ACKNOWLEDGE_HOLD_POSITION:
680         text = receiver + ". Holding Position. " + sender;
681         break;
682     case MSG_RESUME_TAXI:
683         text = receiver + ". Resume Taxiing. " + sender;
684         break;
685     case MSG_ACKNOWLEDGE_RESUME_TAXI:
686         text = receiver + ". Continuing Taxi. " + sender;
687         break;
688     case MSG_REPORT_RUNWAY_HOLD_SHORT:
689         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
690         //activeRunway = "test";
691         text = receiver + ". Holding short runway "
692                + activeRunway
693                + ". " + sender;
694         //text = "test1";
695         //cerr << "1 Currently at leg " << rec->getLeg() << endl;
696         break;
697     case MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT:
698         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
699         text = receiver + "Roger. Holding short runway "
700                //                + activeRunway
701                + ". " + sender;
702         //text = "test2";
703         //cerr << "2 Currently at leg " << rec->getLeg() << endl;
704         break;
705     case MSG_SWITCH_TOWER_FREQUENCY:
706         towerFreqStr = formatATCFrequency3_2(towerFreq);
707         text = receiver + "Contact Tower at " + towerFreqStr + ". " + sender;
708         //text = "test3";
709         //cerr << "3 Currently at leg " << rec->getLeg() << endl;
710         break;
711     case MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY:
712         towerFreqStr = formatATCFrequency3_2(towerFreq);
713         text = receiver + "Roger, switching to tower at " + towerFreqStr + ". " + sender;
714         //text = "test4";
715         //cerr << "4 Currently at leg " << rec->getLeg() << endl;
716         break;
717     default:
718         //text = "test3";
719         text = text + sender + ". Transmitting unknown Message";
720         break;
721     }
722     if (audible) {
723         double onBoardRadioFreq0 =
724             fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
725         double onBoardRadioFreq1 =
726             fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
727         int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
728         int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
729         //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl;
730
731         // Display ATC message only when one of the radios is tuned
732         // the relevant frequency.
733         // Note that distance attenuation is currently not yet implemented
734                 
735         if ((stationFreq > 0)&&
736             ((onBoardRadioFreqI0 == stationFreq)||
737              (onBoardRadioFreqI1 == stationFreq))) {
738             if (rec->allowTransmissions()) {
739                 
740                 if( fgGetBool( "/sim/radio/use-itm-attenuation", false ) ) {
741                         //cerr << "Using ITM radio propagation" << endl;
742                         FGRadioTransmission* radio = new FGRadioTransmission();
743                         SGGeod sender_pos;
744                         double sender_alt_ft, sender_alt;
745                         if(ground_to_air) {
746                   sender_pos = parent->parent()->geod();
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(const 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             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 = makeZUpFrame(geod);
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  (segment->getEnd()->geod());
1350
1351                     double length = SGGeodesy::distanceM(start, end);
1352                     //heading = SGGeodesy::headingDeg(start->geod(), end->geod());
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();
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();
1435                         double elevationEnd   = segment->getEnd  ()->getElevationM();
1436                         if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
1437                             SGGeod center2 = segment->getStart()->geod();
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()->geod();
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                         SGGeod segCenter(segment->getCenter());
1469                         WorldCoordinate( obj_pos, segCenter.getLatitudeDeg(),
1470                                         segCenter.getLongitudeDeg(), elevationMean + 0.5 + dx, -(segment->getHeading()), slope );
1471
1472                         //WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), parent->getElevation()+8+dx, -(segment->getHeading()) );
1473
1474                         obj_trans->setMatrix( obj_pos );
1475                         //osg::Vec3 center(0, 0, 0)
1476
1477                         float width = segment->getLength() /2.0;
1478                         osg::Vec3 corner(-width, 0, 0.25f);
1479                         osg::Vec3 widthVec(2*width + 1, 0, 0);
1480                         osg::Vec3 heightVec(0, 1, 0);
1481                         osg::Geometry* geometry;
1482                         geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1483                         simgear::EffectGeode* geode = new simgear::EffectGeode;
1484                         geode->setName("test");
1485                         geode->addDrawable(geometry);
1486                         //osg::Node *custom_obj;
1487                         SGMaterial *mat;
1488                         if (segment->hasBlock(now)) {
1489                             mat = matlib->find("UnidirectionalTaperRed");
1490                         } else {
1491                             mat = matlib->find("UnidirectionalTaperGreen");
1492                         }
1493                         if (mat)
1494                             geode->setEffect(mat->get_effect());
1495                         obj_trans->addChild(geode);
1496                         // wire as much of the scene graph together as we can
1497                         //->addChild( obj_trans );
1498                         group->addChild( obj_trans );
1499                     } else {
1500                         //cerr << "BIG FAT WARNING: k is here : " << pos << endl;
1501                     }
1502                 }
1503                 dx += 0.2;
1504             }
1505         }
1506         globals->get_scenery()->get_scene_graph()->addChild(group);
1507     }
1508 }
1509
1510 string FGStartupController::getName() {
1511     return string(parent->getId() + "-startup");
1512 }
1513
1514 void FGStartupController::update(double dt)
1515 {
1516
1517 }
1518
1519
1520
1521 /***************************************************************************
1522  * class FGApproachController
1523  *
1524  **************************************************************************/
1525 FGApproachController::FGApproachController(FGAirportDynamics *par):
1526         FGATCController()
1527 {
1528     parent = par;
1529 }
1530
1531 //
1532 void FGApproachController::announcePosition(int id,
1533         FGAIFlightPlan * intendedRoute,
1534         int currentPosition,
1535         double lat, double lon,
1536         double heading, double speed,
1537         double alt, double radius,
1538         int leg, FGAIAircraft * ref)
1539 {
1540     init();
1541     TrafficVectorIterator i = activeTraffic.begin();
1542     // Search whether the current id alread has an entry
1543     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1544     if (activeTraffic.size()) {
1545         //while ((i->getId() != id) && i != activeTraffic.end()) {
1546         while (i != activeTraffic.end()) {
1547             if (i->getId() == id) {
1548                 break;
1549             }
1550             i++;
1551         }
1552     }
1553     // Add a new TrafficRecord if no one exsists for this aircraft.
1554     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1555         FGTrafficRecord rec;
1556         rec.setId(id);
1557
1558         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1559         rec.setRunway(intendedRoute->getRunway());
1560         rec.setLeg(leg);
1561         //rec.setCallSign(callsign);
1562         rec.setAircraft(ref);
1563         activeTraffic.push_back(rec);
1564     } else {
1565         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1566     }
1567 }
1568
1569 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1570         double heading, double speed, double alt,
1571         double dt)
1572 {
1573     TrafficVectorIterator i = activeTraffic.begin();
1574     // Search search if the current id has an entry
1575     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1576     TrafficVectorIterator current, closest;
1577     if (activeTraffic.size()) {
1578         //while ((i->getId() != id) && i != activeTraffic.end()) {
1579         while (i != activeTraffic.end()) {
1580             if (i->getId() == id) {
1581                 break;
1582             }
1583             i++;
1584         }
1585     }
1586 //    // update position of the current aircraft
1587     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1588         SG_LOG(SG_ATC, SG_ALERT,
1589                "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
1590     } else {
1591         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1592         current = i;
1593         //cerr << "ApproachController: checking for speed" << endl;
1594         time_t time_diff =
1595             current->getAircraft()->
1596             checkForArrivalTime(string("final001"));
1597         if (time_diff > 15) {
1598             current->setSpeedAdjustment(current->getAircraft()->
1599                                         getPerformance()->vDescent() *
1600                                         1.35);
1601         } else if (time_diff > 5) {
1602             current->setSpeedAdjustment(current->getAircraft()->
1603                                         getPerformance()->vDescent() *
1604                                         1.2);
1605         } else if (time_diff < -15) {
1606             current->setSpeedAdjustment(current->getAircraft()->
1607                                         getPerformance()->vDescent() *
1608                                         0.65);
1609         } else if (time_diff < -5) {
1610             current->setSpeedAdjustment(current->getAircraft()->
1611                                         getPerformance()->vDescent() *
1612                                         0.8);
1613         } else {
1614             current->clearSpeedAdjustment();
1615         }
1616         //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1617     }
1618     setDt(getDt() + dt);
1619 }
1620
1621 void FGApproachController::signOff(int id)
1622 {
1623     TrafficVectorIterator i = activeTraffic.begin();
1624     // Search search if the current id alread has an entry
1625     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1626     if (activeTraffic.size()) {
1627         //while ((i->getId() != id) && i != activeTraffic.end()) {
1628         while (i != activeTraffic.end()) {
1629             if (i->getId() == id) {
1630                 break;
1631             }
1632             i++;
1633         }
1634     }
1635     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1636         SG_LOG(SG_ATC, SG_ALERT,
1637                "AI error: Aircraft without traffic record is signing off from approach at " << SG_ORIGIN);
1638     } else {
1639         i = activeTraffic.erase(i);
1640     }
1641 }
1642
1643 void FGApproachController::update(double dt)
1644 {
1645
1646 }
1647
1648
1649
1650 bool FGApproachController::hasInstruction(int id)
1651 {
1652     TrafficVectorIterator i = activeTraffic.begin();
1653     // Search search if the current id has an entry
1654     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1655     if (activeTraffic.size()) {
1656         //while ((i->getId() != id) && i != activeTraffic.end()) {
1657         while (i != activeTraffic.end()) {
1658             if (i->getId() == id) {
1659                 break;
1660             }
1661             i++;
1662         }
1663     }
1664     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1665         SG_LOG(SG_ATC, SG_ALERT,
1666                "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1667     } else {
1668         return i->hasInstruction();
1669     }
1670     return false;
1671 }
1672
1673
1674 FGATCInstruction FGApproachController::getInstruction(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.size()) {
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.size() == 0)) {
1689         SG_LOG(SG_ATC, SG_ALERT,
1690                "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1691     } else {
1692         return i->getInstruction();
1693     }
1694     return FGATCInstruction();
1695 }
1696
1697
1698 ActiveRunway *FGApproachController::getRunway(const string& name)
1699 {
1700     ActiveRunwayVecIterator rwy = activeRunways.begin();
1701     if (activeRunways.size()) {
1702         while (rwy != activeRunways.end()) {
1703             if (rwy->getRunwayName() == name) {
1704                 break;
1705             }
1706             rwy++;
1707         }
1708     }
1709     if (rwy == activeRunways.end()) {
1710         ActiveRunway aRwy(name, 0);
1711         activeRunways.push_back(aRwy);
1712         rwy = activeRunways.end() - 1;
1713     }
1714     return &(*rwy);
1715 }
1716
1717 void FGApproachController::render(bool visible) {
1718     //cerr << "FGApproachController::render function not yet implemented" << endl;
1719 }
1720
1721
1722
1723 string FGApproachController::getName() {
1724     return string(parent->getId() + "-approach");
1725 }