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