]> git.mxchange.org Git - flightgear.git/blob - src/ATC/trafficcontrol.cxx
Make PerformanceDB a real subsystem
[flightgear.git] / src / ATC / trafficcontrol.cxx
1 // trafficrecord.cxx - Implementation of AIModels ATC code.
2 //
3 // Written by Durk Talsma, started September 2006.
4 //
5 // Copyright (C) 2006 Durk Talsma.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #include <algorithm>
28 #include <cstdio>
29
30 #include <osg/Geode>
31 #include <osg/Geometry>
32 #include <osg/MatrixTransform>
33 #include <osg/Shape>
34
35 #include <simgear/scene/material/EffectGeode.hxx>
36 #include <simgear/scene/material/matlib.hxx>
37 #include <simgear/scene/material/mat.hxx>
38 #include <simgear/scene/util/OsgMath.hxx>
39 #include <Scenery/scenery.hxx>
40
41 #include "trafficcontrol.hxx"
42 #include "atc_mgr.hxx"
43 #include <AIModel/AIAircraft.hxx>
44 #include <AIModel/AIFlightPlan.hxx>
45 #include <AIModel/performancedata.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/airport.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.empty()) {
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.empty()) {
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.empty())
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.empty()) {
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.empty()) {
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.empty()) {
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.empty() && ! other.intentions.empty()) {
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.empty()) {
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.empty())
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.empty()) {
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     FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
493     mgr->removeController(this);
494 }
495
496 string FGATCController::getGateName(FGAIAircraft * ref)
497 {
498     return ref->atGate();
499 }
500
501 bool FGATCController::isUserAircraft(FGAIAircraft* ac)
502 {
503     return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false;
504 };
505
506 void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, AtcMsgId msgId,
507                                AtcMsgDir msgDir, bool audible)
508 {
509     string sender, receiver;
510     int stationFreq = 0;
511     int taxiFreq = 0;
512     int towerFreq = 0;
513     int freqId = 0;
514     string atisInformation;
515     string text;
516     string taxiFreqStr;
517     string towerFreqStr;
518     double heading = 0;
519     string activeRunway;
520     string fltType;
521     string rwyClass;
522     string SID;
523     string transponderCode;
524     FGAIFlightPlan *fp;
525     string fltRules;
526     string instructionText;
527     int ground_to_air=0;
528
529     //double commFreqD;
530     sender = rec->getAircraft()->getTrafficRef()->getCallSign();
531     if (rec->getAircraft()->getTaxiClearanceRequest()) {
532         instructionText = "push-back and taxi";
533     } else {
534         instructionText = "taxi";
535     }
536     //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
537     switch (rec->getLeg()) {
538     case 1:
539     case 2:
540         freqId = rec->getNextFrequency();
541         stationFreq =
542             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
543             getDynamics()->getGroundFrequency(rec->getLeg() + freqId);
544         taxiFreq =
545             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
546             getDynamics()->getGroundFrequency(2);
547         towerFreq =
548             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
549             getDynamics()->getTowerFrequency(2);
550         receiver =
551             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
552             getName() + "-Ground";
553         atisInformation =
554             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
555             getDynamics()->getAtisSequence();
556         break;
557     case 3:
558         receiver =
559             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
560             getName() + "-Tower";
561         break;
562     }
563     // Swap sender and receiver value in case of a ground to air transmission
564     if (msgDir == ATC_GROUND_TO_AIR) {
565         string tmp = sender;
566         sender = receiver;
567         receiver = tmp;
568         ground_to_air=1;
569     }
570     switch (msgId) {
571     case MSG_ANNOUNCE_ENGINE_START:
572         text = sender + ". Ready to Start up";
573         break;
574     case MSG_REQUEST_ENGINE_START:
575         text =
576             receiver + ", This is " + sender + ". Position " +
577             getGateName(rec->getAircraft()) + ". Information " +
578             atisInformation + ". " +
579             rec->getAircraft()->getTrafficRef()->getFlightRules() +
580             " to " +
581             rec->getAircraft()->getTrafficRef()->getArrivalAirport()->
582             getName() + ". Request start-up";
583         break;
584         // Acknowledge engine startup permission
585         // Assign departure runway
586         // Assign SID, if necessery (TODO)
587     case MSG_PERMIT_ENGINE_START:
588         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
589
590         heading = rec->getAircraft()->getTrafficRef()->getCourse();
591         fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
592         rwyClass =
593             rec->getAircraft()->GetFlightPlan()->
594             getRunwayClassFromTrafficType(fltType);
595
596         rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
597         getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
598                                        heading);
599         rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
600         fp = NULL;
601         rec->getAircraft()->GetFlightPlan()->setSID(fp);
602         if (fp) {
603             SID = fp->getName() + " departure";
604         } else {
605             SID = "fly runway heading ";
606         }
607         //snprintf(buffer, 7, "%3.2f", heading);
608         fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
609         transponderCode = genTransponderCode(fltRules);
610         rec->getAircraft()->SetTransponderCode(transponderCode);
611         text =
612             receiver + ". Start-up approved. " + atisInformation +
613             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
614             transponderCode + ". " +
615             "For "+ instructionText + " clearance call " + taxiFreqStr + ". " +
616             sender + " control.";
617         break;
618     case MSG_DENY_ENGINE_START:
619         text = receiver + ". Standby";
620         break;
621     case MSG_ACKNOWLEDGE_ENGINE_START:
622         fp = rec->getAircraft()->GetFlightPlan()->getSID();
623         if (fp) {
624             SID =
625                 rec->getAircraft()->GetFlightPlan()->getSID()->getName() +
626                 " departure";
627         } else {
628             SID = "fly runway heading ";
629         }
630         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
631         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
632         transponderCode = rec->getAircraft()->GetTransponderCode();
633
634         text =
635             receiver + ". Start-up approved. " + atisInformation +
636             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
637             transponderCode + ". " +
638             "For " + instructionText + " clearance call " + taxiFreqStr + ". " +
639             sender;
640         break;
641     case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
642         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
643         text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
644         break;
645     case MSG_INITIATE_CONTACT:
646         text = receiver + ". With you. " + sender;
647         break;
648     case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
649         text = receiver + ". Roger. " + sender;
650         break;
651     case MSG_REQUEST_PUSHBACK_CLEARANCE:
652         if (rec->getAircraft()->getTaxiClearanceRequest()) {
653             text = receiver + ". Request push-back. " + sender;
654         } else {
655             text = receiver + ". Request Taxi clearance. " + sender;
656         }
657         break;
658     case MSG_PERMIT_PUSHBACK_CLEARANCE:
659         if (rec->getAircraft()->getTaxiClearanceRequest()) {
660             text = receiver + ". Push-back approved. " + sender;
661         } else {
662             text = receiver + ". Cleared to Taxi." + sender;
663         }
664         break;
665     case MSG_HOLD_PUSHBACK_CLEARANCE:
666         text = receiver + ". Standby. " + sender;
667         break;
668     case MSG_REQUEST_TAXI_CLEARANCE:
669         text = receiver + ". Ready to Taxi. " + sender;
670         break;
671     case MSG_ISSUE_TAXI_CLEARANCE:
672         text = receiver + ". Cleared to taxi. " + sender;
673         break;
674     case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
675         text = receiver + ". Cleared to taxi. " + sender;
676         break;
677     case MSG_HOLD_POSITION:
678         text = receiver + ". Hold Position. " + sender;
679         break;
680     case MSG_ACKNOWLEDGE_HOLD_POSITION:
681         text = receiver + ". Holding Position. " + sender;
682         break;
683     case MSG_RESUME_TAXI:
684         text = receiver + ". Resume Taxiing. " + sender;
685         break;
686     case MSG_ACKNOWLEDGE_RESUME_TAXI:
687         text = receiver + ". Continuing Taxi. " + sender;
688         break;
689     case MSG_REPORT_RUNWAY_HOLD_SHORT:
690         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
691         //activeRunway = "test";
692         text = receiver + ". Holding short runway "
693                + activeRunway
694                + ". " + sender;
695         //text = "test1";
696         //cerr << "1 Currently at leg " << rec->getLeg() << endl;
697         break;
698     case MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT:
699         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
700         text = receiver + "Roger. Holding short runway "
701                //                + activeRunway
702                + ". " + sender;
703         //text = "test2";
704         //cerr << "2 Currently at leg " << rec->getLeg() << endl;
705         break;
706     case MSG_SWITCH_TOWER_FREQUENCY:
707         towerFreqStr = formatATCFrequency3_2(towerFreq);
708         text = receiver + "Contact Tower at " + towerFreqStr + ". " + sender;
709         //text = "test3";
710         //cerr << "3 Currently at leg " << rec->getLeg() << endl;
711         break;
712     case MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY:
713         towerFreqStr = formatATCFrequency3_2(towerFreq);
714         text = receiver + "Roger, switching to tower at " + towerFreqStr + ". " + sender;
715         //text = "test4";
716         //cerr << "4 Currently at leg " << rec->getLeg() << endl;
717         break;
718     default:
719         //text = "test3";
720         text = text + sender + ". Transmitting unknown Message";
721         break;
722     }
723     if (audible) {
724         double onBoardRadioFreq0 =
725             fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
726         double onBoardRadioFreq1 =
727             fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
728         int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
729         int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
730         //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl;
731
732         // Display ATC message only when one of the radios is tuned
733         // the relevant frequency.
734         // Note that distance attenuation is currently not yet implemented
735                 
736         if ((stationFreq > 0)&&
737             ((onBoardRadioFreqI0 == stationFreq)||
738              (onBoardRadioFreqI1 == stationFreq))) {
739             if (rec->allowTransmissions()) {
740                 
741                 if( fgGetBool( "/sim/radio/use-itm-attenuation", false ) ) {
742                         //cerr << "Using ITM radio propagation" << endl;
743                         FGRadioTransmission* radio = new FGRadioTransmission();
744                         SGGeod sender_pos;
745                         double sender_alt_ft, sender_alt;
746                         if(ground_to_air) {
747                   sender_pos = parent->parent()->geod();
748                                  }
749                                 else {
750                                       sender_alt_ft = rec->getAltitude();
751                                       sender_alt = sender_alt_ft * SG_FEET_TO_METER;
752                                       sender_pos= SGGeod::fromDegM( rec->getLongitude(),
753                                              rec->getLatitude(), sender_alt );
754                                 }
755                                 double frequency = ((double)stationFreq) / 100;
756                         radio->receiveATC(sender_pos, frequency, text, ground_to_air);
757                         delete radio;
758                 }
759                 else {
760                         fgSetString("/sim/messages/atc", text.c_str());
761                 }
762             }
763         }
764     } else {
765         FGATCDialogNew::instance()->addEntry(1, text);
766     }
767 }
768
769
770 string FGATCController::formatATCFrequency3_2(int freq)
771 {
772     char buffer[7];
773     snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
774     return string(buffer);
775 }
776
777 // TODO: Set transponder codes according to real-world routes.
778 // The current version just returns a random string of four octal numbers.
779 string FGATCController::genTransponderCode(const string& fltRules)
780 {
781     if (fltRules == "VFR") {
782         return string("1200");
783     } else {
784         char buffer[5];
785         snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
786                  rand() % 8);
787         return string(buffer);
788     }
789 }
790
791 void FGATCController::init()
792 {
793     if (!initialized) {
794         FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
795         mgr->addController(this);
796         initialized = true;
797     }
798 }
799
800 /***************************************************************************
801  * class FGTowerController
802  *
803  **************************************************************************/
804 FGTowerController::FGTowerController(FGAirportDynamics *par) :
805         FGATCController()
806 {
807     parent = par;
808 }
809
810 //
811 void FGTowerController::announcePosition(int id,
812         FGAIFlightPlan * intendedRoute,
813         int currentPosition, double lat,
814         double lon, double heading,
815         double speed, double alt,
816         double radius, int leg,
817         FGAIAircraft * ref)
818 {
819     init();
820     TrafficVectorIterator i = activeTraffic.begin();
821     // Search whether the current id alread has an entry
822     // This might be faster using a map instead of a vector, but let's start by taking a safe route
823     if (! activeTraffic.empty()) {
824         //while ((i->getId() != id) && i != activeTraffic.end()) {
825         while (i != activeTraffic.end()) {
826             if (i->getId() == id) {
827                 break;
828             }
829             i++;
830         }
831     }
832     // Add a new TrafficRecord if no one exsists for this aircraft.
833     if (i == activeTraffic.end() || (activeTraffic.empty())) {
834         FGTrafficRecord rec;
835         rec.setId(id);
836
837         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
838         rec.setRunway(intendedRoute->getRunway());
839         rec.setLeg(leg);
840         //rec.setCallSign(callsign);
841         rec.setRadius(radius);
842         rec.setAircraft(ref);
843         activeTraffic.push_back(rec);
844         // Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway.
845         ActiveRunwayVecIterator rwy = activeRunways.begin();
846         if (! activeRunways.empty()) {
847             while (rwy != activeRunways.end()) {
848                 if (rwy->getRunwayName() == intendedRoute->getRunway()) {
849                     break;
850                 }
851                 rwy++;
852             }
853         }
854         if (rwy == activeRunways.end()) {
855             ActiveRunway aRwy(intendedRoute->getRunway(), id);
856             aRwy.addToDepartureCue(ref);
857             activeRunways.push_back(aRwy);
858             rwy = (activeRunways.end()-1);
859         } else {
860             rwy->addToDepartureCue(ref);
861         }
862
863         //cerr << ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getDepartureCueSize() <<  " for takeoff " << endl;
864     } else {
865         i->setPositionAndHeading(lat, lon, heading, speed, alt);
866     }
867 }
868
869 void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
870         double heading, double speed, double alt,
871         double dt)
872 {
873     TrafficVectorIterator i = activeTraffic.begin();
874     // Search whether the current id has an entry
875     // This might be faster using a map instead of a vector, but let's start by taking a safe route
876     TrafficVectorIterator current, closest;
877     if (! activeTraffic.empty()) {
878         //while ((i->getId() != id) && i != activeTraffic.end()) {
879         while (i != activeTraffic.end()) {
880             if (i->getId() == id) {
881                 break;
882             }
883             i++;
884         }
885     }
886 //    // update position of the current aircraft
887     if (i == activeTraffic.end() || (activeTraffic.empty())) {
888         SG_LOG(SG_ATC, SG_ALERT,
889                "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
890     } else {
891         i->setPositionAndHeading(lat, lon, heading, speed, alt);
892         current = i;
893     }
894     setDt(getDt() + dt);
895
896     // see if we already have a clearance record for the currently active runway
897     // NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is
898     // already exists here. So, we can simplify the current code.
899     
900     ActiveRunwayVecIterator rwy = activeRunways.begin();
901     //if (parent->getId() == fgGetString("/sim/presets/airport-id")) {
902     //    for (rwy = activeRunways.begin(); rwy != activeRunways.end(); rwy++) {
903     //        rwy->printDepartureCue();
904     //    }
905     //}
906     
907     rwy = activeRunways.begin();
908     while (rwy != activeRunways.end()) {
909         if (rwy->getRunwayName() == current->getRunway()) {
910             break;
911         }
912         rwy++;
913     }
914
915     // only bother running the following code if the current aircraft is the
916     // first in line for depature
917     /* if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
918         if (rwy->getCleared()) {
919             if (id == rwy->getCleared()) {
920                 current->setHoldPosition(false);
921             } else {
922                 current->setHoldPosition(true);
923             }
924         } else {
925             // For now. At later stages, this will probably be the place to check for inbound traffc.
926             rwy->setCleared(id);
927         }
928     } */
929     // only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control
930     FGAIAircraft* ac= rwy->getFirstAircraftInDepartureCue();
931     if (ac->getTakeOffStatus() == 1) {
932         ac->setTakeOffStatus(2);
933     }
934     if (current->getAircraft()->getTakeOffStatus() == 2) {
935         current -> setHoldPosition(false);
936     } else {
937         current->setHoldPosition(true);
938     }
939     int clearanceId = rwy->getCleared();
940     if (clearanceId) {
941         if (id == clearanceId) {
942             current->setHoldPosition(false);
943         }
944     } else {
945         if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
946             rwy->setCleared(id);
947             FGAIAircraft *ac = rwy->getFirstOfStatus(1);
948             if (ac)
949                 ac->setTakeOffStatus(2);
950         }
951     }
952
953
954
955 void FGTowerController::signOff(int id)
956 {
957     TrafficVectorIterator i = activeTraffic.begin();
958     // Search search if the current id alread has an entry
959     // This might be faster using a map instead of a vector, but let's start by taking a safe route
960     if (! activeTraffic.empty()) {
961         //while ((i->getId() != id) && i != activeTraffic.end()) {
962         while (i != activeTraffic.end()) {
963             if (i->getId() == id) {
964                 break;
965             }
966             i++;
967         }
968     }
969     // If this aircraft has left the runway, we can clear the departure record for this runway
970     ActiveRunwayVecIterator rwy = activeRunways.begin();
971     if (! activeRunways.empty()) {
972         //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
973         while (rwy != activeRunways.end()) {
974             if (rwy->getRunwayName() == i->getRunway()) {
975                 break;
976             }
977             rwy++;
978         }
979         if (rwy != activeRunways.end()) {
980             rwy->setCleared(0);
981             rwy->updateDepartureCue();
982         } else {
983             SG_LOG(SG_ATC, SG_ALERT,
984                    "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff at " << SG_ORIGIN);
985         }
986     }
987     if (i == activeTraffic.end() || (activeTraffic.empty())) {
988         SG_LOG(SG_ATC, SG_ALERT,
989                "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN);
990     } else {
991         i->getAircraft()->resetTakeOffStatus();
992         i = activeTraffic.erase(i);
993         //cerr << "Signing off from tower controller" << endl;
994     }
995 }
996
997 // NOTE:
998 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
999 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
1000 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1001 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1002 // Note that this function is probably obsolete
1003 bool FGTowerController::hasInstruction(int id)
1004 {
1005     TrafficVectorIterator i = activeTraffic.begin();
1006     // Search search if the current id has an entry
1007     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1008     if (! activeTraffic.empty()) {
1009         //while ((i->getId() != id) && i != activeTraffic.end()) {
1010         while (i != activeTraffic.end()) {
1011             if (i->getId() == id) {
1012                 break;
1013             }
1014             i++;
1015         }
1016     }
1017     if (i == activeTraffic.end() || activeTraffic.empty()) {
1018         SG_LOG(SG_ATC, SG_ALERT,
1019                "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1020     } else {
1021         return i->hasInstruction();
1022     }
1023     return false;
1024 }
1025
1026
1027 FGATCInstruction FGTowerController::getInstruction(int id)
1028 {
1029     TrafficVectorIterator i = activeTraffic.begin();
1030     // Search search if the current id has an entry
1031     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1032     if (! activeTraffic.empty()) {
1033         //while ((i->getId() != id) && i != activeTraffic.end()) {
1034         while (i != activeTraffic.end()) {
1035             if (i->getId() == id) {
1036                 break;
1037             }
1038             i++;
1039         }
1040     }
1041     if (i == activeTraffic.end() || activeTraffic.empty()) {
1042         SG_LOG(SG_ATC, SG_ALERT,
1043                "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1044     } else {
1045         return i->getInstruction();
1046     }
1047     return FGATCInstruction();
1048 }
1049
1050 void FGTowerController::render(bool visible) {
1051     //std::cerr << "FGTowerController::render function not yet implemented" << std::endl;
1052 }
1053
1054 string FGTowerController::getName() {
1055     return string(parent->getId() + "-tower");
1056 }
1057
1058 void FGTowerController::update(double dt)
1059 {
1060
1061 }
1062
1063
1064
1065 /***************************************************************************
1066  * class FGStartupController
1067  *
1068  **************************************************************************/
1069 FGStartupController::FGStartupController(FGAirportDynamics *par):
1070         FGATCController()
1071 {
1072     parent = par;
1073 }
1074
1075 void FGStartupController::announcePosition(int id,
1076         FGAIFlightPlan * intendedRoute,
1077         int currentPosition, double lat,
1078         double lon, double heading,
1079         double speed, double alt,
1080         double radius, int leg,
1081         FGAIAircraft * ref)
1082 {
1083     init();
1084     TrafficVectorIterator i = activeTraffic.begin();
1085     // Search whether the current id alread has an entry
1086     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1087     if (! activeTraffic.empty()) {
1088         //while ((i->getId() != id) && i != activeTraffic.end()) {
1089         while (i != activeTraffic.end()) {
1090             if (i->getId() == id) {
1091                 break;
1092             }
1093             i++;
1094         }
1095     }
1096     // Add a new TrafficRecord if no one exsists for this aircraft.
1097     if (i == activeTraffic.end() || activeTraffic.empty()) {
1098         FGTrafficRecord rec;
1099         rec.setId(id);
1100
1101         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1102         rec.setRunway(intendedRoute->getRunway());
1103         rec.setLeg(leg);
1104         rec.setPositionAndIntentions(currentPosition, intendedRoute);
1105         //rec.setCallSign(callsign);
1106         rec.setAircraft(ref);
1107         rec.setHoldPosition(true);
1108         activeTraffic.push_back(rec);
1109     } else {
1110         i->setPositionAndIntentions(currentPosition, intendedRoute);
1111         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1112
1113     }
1114 }
1115
1116 // NOTE:
1117 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
1118 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
1119 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1120 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1121 // Note that this function is probably obsolete
1122 bool FGStartupController::hasInstruction(int id)
1123 {
1124     TrafficVectorIterator i = activeTraffic.begin();
1125     // Search search if the current id has an entry
1126     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1127     if (! activeTraffic.empty()) {
1128         //while ((i->getId() != id) && i != activeTraffic.end()) {
1129         while (i != activeTraffic.end()) {
1130             if (i->getId() == id) {
1131                 break;
1132             }
1133             i++;
1134         }
1135     }
1136     if (i == activeTraffic.end() || activeTraffic.empty()) {
1137         SG_LOG(SG_ATC, SG_ALERT,
1138                "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1139     } else {
1140         return i->hasInstruction();
1141     }
1142     return false;
1143 }
1144
1145
1146 FGATCInstruction FGStartupController::getInstruction(int id)
1147 {
1148     TrafficVectorIterator i = activeTraffic.begin();
1149     // Search search if the current id has an entry
1150     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1151     if (! activeTraffic.empty()) {
1152         //while ((i->getId() != id) && i != activeTraffic.end()) {
1153         while (i != activeTraffic.end()) {
1154             if (i->getId() == id) {
1155                 break;
1156             }
1157             i++;
1158         }
1159     }
1160     if (i == activeTraffic.end() || activeTraffic.empty()) {
1161         SG_LOG(SG_ATC, SG_ALERT,
1162                "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1163     } else {
1164         return i->getInstruction();
1165     }
1166     return FGATCInstruction();
1167 }
1168
1169 void FGStartupController::signOff(int id)
1170 {
1171     TrafficVectorIterator i = activeTraffic.begin();
1172     // Search search if the current id alread has an entry
1173     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1174     if (! activeTraffic.empty()) {
1175         //while ((i->getId() != id) && i != activeTraffic.end()) {
1176         while (i != activeTraffic.end()) {
1177             if (i->getId() == id) {
1178                 break;
1179             }
1180             i++;
1181         }
1182     }
1183     if (i == activeTraffic.end() || activeTraffic.empty()) {
1184         SG_LOG(SG_ATC, SG_ALERT,
1185                "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN);
1186     } else {
1187         //cerr << i->getAircraft()->getCallSign() << " signing off from startupcontroller" << endl;
1188         i = activeTraffic.erase(i);
1189     }
1190 }
1191
1192 bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId,
1193         AtcMsgDir msgDir)
1194 {
1195     int state = i->getState();
1196     if ((state == st) && available) {
1197         if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
1198
1199             //cerr << "Checking state " << st << " for " << i->getAircraft()->getCallSign() << endl;
1200             SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
1201             int n = trans_num->getIntValue();
1202             if (n == 0) {
1203                 trans_num->setIntValue(-1);
1204                 // PopupCallback(n);
1205                 //cerr << "Selected transmission message " << n << endl;
1206                 FGATCDialogNew::instance()->removeEntry(1);
1207             } else {
1208                 //cerr << "creading message for " << i->getAircraft()->getCallSign() << endl;
1209                 transmit(&(*i), &(*parent), msgId, msgDir, false);
1210                 return false;
1211             }
1212         }
1213         if (now > startTime) {
1214             //cerr << "Transmitting startup msg" << endl;
1215             transmit(&(*i), &(*parent), msgId, msgDir, true);
1216             i->updateState();
1217             lastTransmission = now;
1218             available = false;
1219             return true;
1220         }
1221     }
1222     return false;
1223 }
1224
1225 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
1226         double heading, double speed, double alt,
1227         double dt)
1228 {
1229     TrafficVectorIterator i = activeTraffic.begin();
1230     // Search search if the current id has an entry
1231     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1232     TrafficVectorIterator current, closest;
1233     if (! activeTraffic.empty()) {
1234         //while ((i->getId() != id) && i != activeTraffic.end()) {
1235         while (i != activeTraffic.end()) {
1236             if (i->getId() == id) {
1237                 break;
1238             }
1239             i++;
1240         }
1241     }
1242 //    // update position of the current aircraft
1243
1244     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1245         SG_LOG(SG_ATC, SG_ALERT,
1246                "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
1247     } else {
1248         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1249         current = i;
1250     }
1251     setDt(getDt() + dt);
1252
1253     int state = i->getState();
1254
1255     // The user controlled aircraft should have crased here, because it doesn't have a traffic reference.
1256     // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
1257     time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
1258     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1259     //cerr << i->getAircraft()->getTrafficRef()->getCallSign()
1260     //     << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1261     //     << " at parking " << getGateName(i->getAircraft()) << endl;
1262
1263     if ((now - lastTransmission) > 3 + (rand() % 15)) {
1264         available = true;
1265     }
1266
1267     checkTransmissionState(0, now, (startTime + 0  ), i, MSG_ANNOUNCE_ENGINE_START,                     ATC_AIR_TO_GROUND);
1268     checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START,                      ATC_AIR_TO_GROUND);
1269     checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START,                       ATC_GROUND_TO_AIR);
1270     checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START,                  ATC_AIR_TO_GROUND);
1271     if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,       ATC_AIR_TO_GROUND)) {
1272         i->nextFrequency();
1273     }
1274     checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT,                          ATC_AIR_TO_GROUND);
1275     checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT,              ATC_GROUND_TO_AIR);
1276     checkTransmissionState(7, now, (startTime + 180), i, MSG_REQUEST_PUSHBACK_CLEARANCE,                ATC_AIR_TO_GROUND);
1277
1278
1279
1280     if ((state == 8) && available) {
1281         if (now > startTime + 200) {
1282             if (i->pushBackAllowed()) {
1283                 i->allowRepeatedTransmissions();
1284                 transmit(&(*i), &(*parent), MSG_PERMIT_PUSHBACK_CLEARANCE,
1285                          ATC_GROUND_TO_AIR, true);
1286                 i->updateState();
1287             } else {
1288                 transmit(&(*i), &(*parent), MSG_HOLD_PUSHBACK_CLEARANCE,
1289                          ATC_GROUND_TO_AIR, true);
1290                 i->suppressRepeatedTransmissions();
1291             }
1292             lastTransmission = now;
1293             available = false;
1294         }
1295     }
1296     if ((state == 9) && available) {
1297         i->setHoldPosition(false);
1298     }
1299 }
1300
1301 // Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
1302 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
1303                             double lon, double elev, double hdg, double slope)
1304 {
1305     SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
1306     obj_pos = makeZUpFrame(geod);
1307     // hdg is not a compass heading, but a counter-clockwise rotation
1308     // around the Z axis
1309     obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
1310                                         0.0, 0.0, 1.0));
1311     obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
1312                                         0.0, 1.0, 0.0));
1313 }
1314
1315
1316 void FGStartupController::render(bool visible)
1317 {
1318     //std::cerr << "Rendering startup controller" << std::endl;
1319     SGMaterialLib *matlib = globals->get_matlib();
1320     if (group) {
1321         //int nr = ;
1322         globals->get_scenery()->get_scene_graph()->removeChild(group);
1323         //while (group->getNumChildren()) {
1324         //  cerr << "Number of children: " << group->getNumChildren() << endl;
1325         //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
1326         //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
1327         //geode->releaseGLObjects();
1328         //group->removeChild(geode);
1329         //delete geode;
1330         group = 0;
1331     }
1332     if (visible) {
1333         group = new osg::Group;
1334         FGScenery * local_scenery = globals->get_scenery();
1335         //double elevation_meters = 0.0;
1336         //double elevation_feet = 0.0;
1337
1338
1339         //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
1340         double dx = 0;
1341         time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1342         for   (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1343             if (i->isActive(300)) {
1344                 // Handle start point
1345                 int pos = i->getCurrentPosition();
1346                 //cerr << "rendering for " << i->getAircraft()->getCallSign() << "pos = " << pos << endl;
1347                 if (pos > 0) {
1348                     FGTaxiSegment *segment  = parent->getGroundNetwork()->findSegment(pos);
1349                     SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1350                     SGGeod end  (segment->getEnd()->geod());
1351
1352                     double length = SGGeodesy::distanceM(start, end);
1353                     //heading = SGGeodesy::headingDeg(start->geod(), end->geod());
1354
1355                     double az2, heading; //, distanceM;
1356                     SGGeodesy::inverse(start, end, heading, az2, length);
1357                     double coveredDistance = length * 0.5;
1358                     SGGeod center;
1359                     SGGeodesy::direct(start, heading, coveredDistance, center, az2);
1360                     //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
1361                     ///////////////////////////////////////////////////////////////////////////////
1362                     // Make a helper function out of this
1363                     osg::Matrix obj_pos;
1364                     osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1365                     obj_trans->setDataVariance(osg::Object::STATIC);
1366                     // Experimental: Calculate slope here, based on length, and the individual elevations
1367                     double elevationStart;
1368                     if (isUserAircraft((i)->getAircraft())) {
1369                         elevationStart = fgGetDouble("/position/ground-elev-m");
1370                     } else {
1371                         elevationStart = ((i)->getAircraft()->_getAltitude() * SG_FEET_TO_METER);
1372                     }
1373                     double elevationEnd   = segment->getEnd()->getElevationM();
1374                     if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1375                         SGGeod center2 = end;
1376                         center2.setElevationM(SG_MAX_ELEVATION_M);
1377                         if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1378                             //elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1379                             //elevation_meters += 0.5;
1380                         }
1381                         else {
1382                             elevationEnd = parent->getElevation();
1383                         }
1384                         segment->getEnd()->setElevation(elevationEnd);
1385                     }
1386
1387                     double elevationMean  = (elevationStart + elevationEnd) / 2.0;
1388                     double elevDiff       = elevationEnd - elevationStart;
1389
1390                     double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1391
1392                     //cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
1393
1394                     WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean + 0.5 + dx, -(heading), slope );
1395                     ;
1396
1397                     obj_trans->setMatrix( obj_pos );
1398                     //osg::Vec3 center(0, 0, 0)
1399
1400                     float width = length /2.0;
1401                     osg::Vec3 corner(-width, 0, 0.25f);
1402                     osg::Vec3 widthVec(2*width + 1, 0, 0);
1403                     osg::Vec3 heightVec(0, 1, 0);
1404                     osg::Geometry* geometry;
1405                     geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1406                     simgear::EffectGeode* geode = new simgear::EffectGeode;
1407                     geode->setName("test");
1408                     geode->addDrawable(geometry);
1409                     //osg::Node *custom_obj;
1410                     SGMaterial *mat;
1411                     if (segment->hasBlock(now)) {
1412                         mat = matlib->find("UnidirectionalTaperRed", center);
1413                     } else {
1414                         mat = matlib->find("UnidirectionalTaperGreen", center);
1415                     }
1416                     if (mat)
1417                         geode->setEffect(mat->get_effect());
1418                     obj_trans->addChild(geode);
1419                     // wire as much of the scene graph together as we can
1420                     //->addChild( obj_trans );
1421                     group->addChild( obj_trans );
1422                     /////////////////////////////////////////////////////////////////////
1423                 } else {
1424                     //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
1425                 }
1426                 for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
1427                     osg::Matrix obj_pos;
1428                     int k = (*j);
1429                     if (k > 0) {
1430                         //cerr << "rendering for " << i->getAircraft()->getCallSign() << "intention = " << k << endl;
1431                         osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1432                         obj_trans->setDataVariance(osg::Object::STATIC);
1433                         FGTaxiSegment *segment  = parent->getGroundNetwork()->findSegment(k);
1434
1435                         double elevationStart = segment->getStart()->getElevationM();
1436                         double elevationEnd   = segment->getEnd  ()->getElevationM();
1437                         if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
1438                             SGGeod center2 = segment->getStart()->geod();
1439                             center2.setElevationM(SG_MAX_ELEVATION_M);
1440                             if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
1441                                 //elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
1442                                 //elevation_meters += 0.5;
1443                             }
1444                             else {
1445                                 elevationStart = parent->getElevation();
1446                             }
1447                             segment->getStart()->setElevation(elevationStart);
1448                         }
1449                         if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1450                             SGGeod center2 = segment->getEnd()->geod();
1451                             center2.setElevationM(SG_MAX_ELEVATION_M);
1452                             if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1453                                 //elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1454                                 //elevation_meters += 0.5;
1455                             }
1456                             else {
1457                                 elevationEnd = parent->getElevation();
1458                             }
1459                             segment->getEnd()->setElevation(elevationEnd);
1460                         }
1461
1462                         double elevationMean  = (elevationStart + elevationEnd) / 2.0;
1463                         double elevDiff       = elevationEnd - elevationStart;
1464                         double length         = segment->getLength();
1465                         double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1466
1467                         //cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
1468
1469                         SGGeod segCenter(segment->getCenter());
1470                         WorldCoordinate( obj_pos, segCenter.getLatitudeDeg(),
1471                                         segCenter.getLongitudeDeg(), elevationMean + 0.5 + dx, -(segment->getHeading()), slope );
1472
1473                         //WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), parent->getElevation()+8+dx, -(segment->getHeading()) );
1474
1475                         obj_trans->setMatrix( obj_pos );
1476                         //osg::Vec3 center(0, 0, 0)
1477
1478                         float width = segment->getLength() /2.0;
1479                         osg::Vec3 corner(-width, 0, 0.25f);
1480                         osg::Vec3 widthVec(2*width + 1, 0, 0);
1481                         osg::Vec3 heightVec(0, 1, 0);
1482                         osg::Geometry* geometry;
1483                         geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1484                         simgear::EffectGeode* geode = new simgear::EffectGeode;
1485                         geode->setName("test");
1486                         geode->addDrawable(geometry);
1487                         //osg::Node *custom_obj;
1488                         SGMaterial *mat;
1489                         if (segment->hasBlock(now)) {
1490                             mat = matlib->find("UnidirectionalTaperRed", segCenter);
1491                         } else {
1492                             mat = matlib->find("UnidirectionalTaperGreen", segCenter);
1493                         }
1494                         if (mat)
1495                             geode->setEffect(mat->get_effect());
1496                         obj_trans->addChild(geode);
1497                         // wire as much of the scene graph together as we can
1498                         //->addChild( obj_trans );
1499                         group->addChild( obj_trans );
1500                     } else {
1501                         //cerr << "BIG FAT WARNING: k is here : " << pos << endl;
1502                     }
1503                 }
1504                 dx += 0.2;
1505             }
1506         }
1507         globals->get_scenery()->get_scene_graph()->addChild(group);
1508     }
1509 }
1510
1511 string FGStartupController::getName() {
1512     return string(parent->getId() + "-startup");
1513 }
1514
1515 void FGStartupController::update(double dt)
1516 {
1517
1518 }
1519
1520
1521
1522 /***************************************************************************
1523  * class FGApproachController
1524  *
1525  **************************************************************************/
1526 FGApproachController::FGApproachController(FGAirportDynamics *par):
1527         FGATCController()
1528 {
1529     parent = par;
1530 }
1531
1532 //
1533 void FGApproachController::announcePosition(int id,
1534         FGAIFlightPlan * intendedRoute,
1535         int currentPosition,
1536         double lat, double lon,
1537         double heading, double speed,
1538         double alt, double radius,
1539         int leg, FGAIAircraft * ref)
1540 {
1541     init();
1542     TrafficVectorIterator i = activeTraffic.begin();
1543     // Search whether the current id alread has an entry
1544     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1545     if (! activeTraffic.empty()) {
1546         //while ((i->getId() != id) && i != activeTraffic.end()) {
1547         while (i != activeTraffic.end()) {
1548             if (i->getId() == id) {
1549                 break;
1550             }
1551             i++;
1552         }
1553     }
1554     // Add a new TrafficRecord if no one exsists for this aircraft.
1555     if (i == activeTraffic.end() || activeTraffic.empty()) {
1556         FGTrafficRecord rec;
1557         rec.setId(id);
1558
1559         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1560         rec.setRunway(intendedRoute->getRunway());
1561         rec.setLeg(leg);
1562         //rec.setCallSign(callsign);
1563         rec.setAircraft(ref);
1564         activeTraffic.push_back(rec);
1565     } else {
1566         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1567     }
1568 }
1569
1570 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1571         double heading, double speed, double alt,
1572         double dt)
1573 {
1574     TrafficVectorIterator i = activeTraffic.begin();
1575     // Search search if the current id has an entry
1576     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1577     TrafficVectorIterator current, closest;
1578     if (! activeTraffic.empty()) {
1579         //while ((i->getId() != id) && i != activeTraffic.end()) {
1580         while (i != activeTraffic.end()) {
1581             if (i->getId() == id) {
1582                 break;
1583             }
1584             i++;
1585         }
1586     }
1587 //    // update position of the current aircraft
1588     if (i == activeTraffic.end() || activeTraffic.empty()) {
1589         SG_LOG(SG_ATC, SG_ALERT,
1590                "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
1591     } else {
1592         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1593         current = i;
1594         //cerr << "ApproachController: checking for speed" << endl;
1595         time_t time_diff =
1596             current->getAircraft()->
1597             checkForArrivalTime(string("final001"));
1598         if (time_diff > 15) {
1599             current->setSpeedAdjustment(current->getAircraft()->
1600                                         getPerformance()->vDescent() *
1601                                         1.35);
1602         } else if (time_diff > 5) {
1603             current->setSpeedAdjustment(current->getAircraft()->
1604                                         getPerformance()->vDescent() *
1605                                         1.2);
1606         } else if (time_diff < -15) {
1607             current->setSpeedAdjustment(current->getAircraft()->
1608                                         getPerformance()->vDescent() *
1609                                         0.65);
1610         } else if (time_diff < -5) {
1611             current->setSpeedAdjustment(current->getAircraft()->
1612                                         getPerformance()->vDescent() *
1613                                         0.8);
1614         } else {
1615             current->clearSpeedAdjustment();
1616         }
1617         //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1618     }
1619     setDt(getDt() + dt);
1620 }
1621
1622 void FGApproachController::signOff(int id)
1623 {
1624     TrafficVectorIterator i = activeTraffic.begin();
1625     // Search search if the current id alread has an entry
1626     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1627     if (! activeTraffic.empty()) {
1628         //while ((i->getId() != id) && i != activeTraffic.end()) {
1629         while (i != activeTraffic.end()) {
1630             if (i->getId() == id) {
1631                 break;
1632             }
1633             i++;
1634         }
1635     }
1636     if (i == activeTraffic.end() || activeTraffic.empty()) {
1637         SG_LOG(SG_ATC, SG_ALERT,
1638                "AI error: Aircraft without traffic record is signing off from approach at " << SG_ORIGIN);
1639     } else {
1640         i = activeTraffic.erase(i);
1641     }
1642 }
1643
1644 void FGApproachController::update(double dt)
1645 {
1646
1647 }
1648
1649
1650
1651 bool FGApproachController::hasInstruction(int id)
1652 {
1653     TrafficVectorIterator i = activeTraffic.begin();
1654     // Search search if the current id has an entry
1655     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1656     if (! activeTraffic.empty()) {
1657         //while ((i->getId() != id) && i != activeTraffic.end()) {
1658         while (i != activeTraffic.end()) {
1659             if (i->getId() == id) {
1660                 break;
1661             }
1662             i++;
1663         }
1664     }
1665     if (i == activeTraffic.end() || activeTraffic.empty()) {
1666         SG_LOG(SG_ATC, SG_ALERT,
1667                "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1668     } else {
1669         return i->hasInstruction();
1670     }
1671     return false;
1672 }
1673
1674
1675 FGATCInstruction FGApproachController::getInstruction(int id)
1676 {
1677     TrafficVectorIterator i = activeTraffic.begin();
1678     // Search search if the current id has an entry
1679     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1680     if (activeTraffic.size()) {
1681         //while ((i->getId() != id) && i != activeTraffic.end()) {
1682         while (i != activeTraffic.end()) {
1683             if (i->getId() == id) {
1684                 break;
1685             }
1686             i++;
1687         }
1688     }
1689     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1690         SG_LOG(SG_ATC, SG_ALERT,
1691                "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1692     } else {
1693         return i->getInstruction();
1694     }
1695     return FGATCInstruction();
1696 }
1697
1698
1699 ActiveRunway *FGApproachController::getRunway(const string& name)
1700 {
1701     ActiveRunwayVecIterator rwy = activeRunways.begin();
1702     if (activeRunways.size()) {
1703         while (rwy != activeRunways.end()) {
1704             if (rwy->getRunwayName() == name) {
1705                 break;
1706             }
1707             rwy++;
1708         }
1709     }
1710     if (rwy == activeRunways.end()) {
1711         ActiveRunway aRwy(name, 0);
1712         activeRunways.push_back(aRwy);
1713         rwy = activeRunways.end() - 1;
1714     }
1715     return &(*rwy);
1716 }
1717
1718 void FGApproachController::render(bool visible) {
1719     //std::cerr << "FGApproachController::render function not yet implemented" << std::endl;
1720 }
1721
1722
1723
1724 string FGApproachController::getName() {
1725     return string(parent->getId() + "-approach");
1726 }