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