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