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