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