]> git.mxchange.org Git - flightgear.git/blob - src/ATC/trafficcontrol.cxx
cleanup
[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( "/instrumentation/use-itm-attenuation", false ) ) {
748                         FGRadio* radio = new FGRadio();
749                         SGGeod sender_pos;
750                         double sender_alt_ft, sender_alt;
751                         if(ground_to_air) {
752                                       sender_alt_ft = parent->getElevation();
753                                       sender_alt = sender_alt_ft * SG_FEET_TO_METER;
754                                       sender_pos= SGGeod::fromDegM( parent->getLongitude(),
755                                       parent->getLatitude(), sender_alt );
756                                 }
757                                 else {
758                                       sender_alt_ft = rec->getAltitude();
759                                       sender_alt = sender_alt_ft * SG_FEET_TO_METER;
760                                       sender_pos= SGGeod::fromDegM( rec->getLongitude(),
761                                              rec->getLatitude(), sender_alt );
762                                 }
763                         radio->receiveText(sender_pos, stationFreq, text, ground_to_air);
764                         delete radio;
765                 }
766                 else {
767                         fgSetString("/sim/messages/atc", text.c_str());
768                 }
769             }
770         }
771     } else {
772         FGATCDialogNew::instance()->addEntry(1, text);
773     }
774 }
775
776
777 string FGATCController::formatATCFrequency3_2(int freq)
778 {
779     char buffer[7];
780     snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
781     return string(buffer);
782 }
783
784 // TODO: Set transponder codes according to real-world routes.
785 // The current version just returns a random string of four octal numbers.
786 string FGATCController::genTransponderCode(string fltRules)
787 {
788     if (fltRules == "VFR") {
789         return string("1200");
790     } else {
791         char buffer[5];
792         snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
793                  rand() % 8);
794         return string(buffer);
795     }
796 }
797
798 void FGATCController::init()
799 {
800     if (!initialized) {
801         FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
802         mgr->addController(this);
803         initialized = true;
804     }
805 }
806
807 /***************************************************************************
808  * class FGTowerController
809  *
810  **************************************************************************/
811 FGTowerController::FGTowerController(FGAirportDynamics *par) :
812         FGATCController()
813 {
814     parent = par;
815 }
816
817 //
818 void FGTowerController::announcePosition(int id,
819         FGAIFlightPlan * intendedRoute,
820         int currentPosition, double lat,
821         double lon, double heading,
822         double speed, double alt,
823         double radius, int leg,
824         FGAIAircraft * ref)
825 {
826     init();
827     TrafficVectorIterator i = activeTraffic.begin();
828     // Search whether the current id alread has an entry
829     // This might be faster using a map instead of a vector, but let's start by taking a safe route
830     if (activeTraffic.size()) {
831         //while ((i->getId() != id) && i != activeTraffic.end()) {
832         while (i != activeTraffic.end()) {
833             if (i->getId() == id) {
834                 break;
835             }
836             i++;
837         }
838     }
839     // Add a new TrafficRecord if no one exsists for this aircraft.
840     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
841         FGTrafficRecord rec;
842         rec.setId(id);
843
844         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
845         rec.setRunway(intendedRoute->getRunway());
846         rec.setLeg(leg);
847         //rec.setCallSign(callsign);
848         rec.setRadius(radius);
849         rec.setAircraft(ref);
850         activeTraffic.push_back(rec);
851         // Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway.
852         ActiveRunwayVecIterator rwy = activeRunways.begin();
853         if (activeRunways.size()) {
854             while (rwy != activeRunways.end()) {
855                 if (rwy->getRunwayName() == intendedRoute->getRunway()) {
856                     break;
857                 }
858                 rwy++;
859             }
860         }
861         if (rwy == activeRunways.end()) {
862             ActiveRunway aRwy(intendedRoute->getRunway(), id);
863             aRwy.addToDepartureCue(ref);
864             activeRunways.push_back(aRwy);
865             rwy = (activeRunways.end()-1);
866         } else {
867             rwy->addToDepartureCue(ref);
868         }
869
870         //cerr << ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getDepartureCueSize() <<  " for takeoff " << endl;
871     } else {
872         i->setPositionAndHeading(lat, lon, heading, speed, alt);
873     }
874 }
875
876 void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
877         double heading, double speed, double alt,
878         double dt)
879 {
880     TrafficVectorIterator i = activeTraffic.begin();
881     // Search whether the current id has an entry
882     // This might be faster using a map instead of a vector, but let's start by taking a safe route
883     TrafficVectorIterator current, closest;
884     if (activeTraffic.size()) {
885         //while ((i->getId() != id) && i != activeTraffic.end()) {
886         while (i != activeTraffic.end()) {
887             if (i->getId() == id) {
888                 break;
889             }
890             i++;
891         }
892     }
893 //    // update position of the current aircraft
894     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
895         SG_LOG(SG_GENERAL, SG_ALERT,
896                "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
897     } else {
898         i->setPositionAndHeading(lat, lon, heading, speed, alt);
899         current = i;
900     }
901     setDt(getDt() + dt);
902
903     // see if we already have a clearance record for the currently active runway
904     // NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is
905     // already exists here. So, we can simplify the current code.
906     
907     ActiveRunwayVecIterator rwy = activeRunways.begin();
908     //if (parent->getId() == fgGetString("/sim/presets/airport-id")) {
909     //    for (rwy = activeRunways.begin(); rwy != activeRunways.end(); rwy++) {
910     //        rwy->printDepartureCue();
911     //    }
912     //}
913     
914     rwy = activeRunways.begin();
915     while (rwy != activeRunways.end()) {
916         if (rwy->getRunwayName() == current->getRunway()) {
917             break;
918         }
919         rwy++;
920     }
921
922     // only bother running the following code if the current aircraft is the
923     // first in line for depature
924     /* if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
925         if (rwy->getCleared()) {
926             if (id == rwy->getCleared()) {
927                 current->setHoldPosition(false);
928             } else {
929                 current->setHoldPosition(true);
930             }
931         } else {
932             // For now. At later stages, this will probably be the place to check for inbound traffc.
933             rwy->setCleared(id);
934         }
935     } */
936     // only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control
937     FGAIAircraft* ac= rwy->getFirstAircraftInDepartureCue();
938     if (ac->getTakeOffStatus() == 1) {
939         ac->setTakeOffStatus(2);
940     }
941     if (current->getAircraft()->getTakeOffStatus() == 2) {
942         current -> setHoldPosition(false);
943     } else {
944         current->setHoldPosition(true);
945     }
946     int clearanceId = rwy->getCleared();
947     if (clearanceId) {
948         if (id == clearanceId) {
949             current->setHoldPosition(false);
950         }
951     } else {
952         if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
953             rwy->setCleared(id);
954             FGAIAircraft *ac = rwy->getFirstOfStatus(1);
955             if (ac)
956                 ac->setTakeOffStatus(2);
957         }
958     }
959
960
961
962 void FGTowerController::signOff(int id)
963 {
964     TrafficVectorIterator i = activeTraffic.begin();
965     // Search search if the current id alread has an entry
966     // This might be faster using a map instead of a vector, but let's start by taking a safe route
967     if (activeTraffic.size()) {
968         //while ((i->getId() != id) && i != activeTraffic.end()) {
969         while (i != activeTraffic.end()) {
970             if (i->getId() == id) {
971                 break;
972             }
973             i++;
974         }
975     }
976     // If this aircraft has left the runway, we can clear the departure record for this runway
977     ActiveRunwayVecIterator rwy = activeRunways.begin();
978     if (activeRunways.size()) {
979         //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
980         while (rwy != activeRunways.end()) {
981             if (rwy->getRunwayName() == i->getRunway()) {
982                 break;
983             }
984             rwy++;
985         }
986         if (rwy != activeRunways.end()) {
987             rwy->setCleared(0);
988             rwy->updateDepartureCue();
989         } else {
990             SG_LOG(SG_GENERAL, SG_ALERT,
991                    "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff at " << SG_ORIGIN);
992         }
993     }
994     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
995         SG_LOG(SG_GENERAL, SG_ALERT,
996                "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN);
997     } else {
998         i->getAircraft()->resetTakeOffStatus();
999         i = activeTraffic.erase(i);
1000         //cerr << "Signing off from tower controller" << endl;
1001     }
1002 }
1003
1004 // NOTE:
1005 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
1006 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
1007 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1008 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1009 // Note that this function is probably obsolete
1010 bool FGTowerController::hasInstruction(int id)
1011 {
1012     TrafficVectorIterator i = activeTraffic.begin();
1013     // Search search if the current id has an entry
1014     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1015     if (activeTraffic.size()) {
1016         //while ((i->getId() != id) && i != activeTraffic.end()) {
1017         while (i != activeTraffic.end()) {
1018             if (i->getId() == id) {
1019                 break;
1020             }
1021             i++;
1022         }
1023     }
1024     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1025         SG_LOG(SG_GENERAL, SG_ALERT,
1026                "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1027     } else {
1028         return i->hasInstruction();
1029     }
1030     return false;
1031 }
1032
1033
1034 FGATCInstruction FGTowerController::getInstruction(int id)
1035 {
1036     TrafficVectorIterator i = activeTraffic.begin();
1037     // Search search if the current id has an entry
1038     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1039     if (activeTraffic.size()) {
1040         //while ((i->getId() != id) && i != activeTraffic.end()) {
1041         while (i != activeTraffic.end()) {
1042             if (i->getId() == id) {
1043                 break;
1044             }
1045             i++;
1046         }
1047     }
1048     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1049         SG_LOG(SG_GENERAL, SG_ALERT,
1050                "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1051     } else {
1052         return i->getInstruction();
1053     }
1054     return FGATCInstruction();
1055 }
1056
1057 void FGTowerController::render(bool visible) {
1058     //cerr << "FGTowerController::render function not yet implemented" << endl;
1059 }
1060
1061 string FGTowerController::getName() {
1062     return string(parent->getId() + "-tower");
1063 }
1064
1065 void FGTowerController::update(double dt)
1066 {
1067
1068 }
1069
1070
1071
1072 /***************************************************************************
1073  * class FGStartupController
1074  *
1075  **************************************************************************/
1076 FGStartupController::FGStartupController(FGAirportDynamics *par):
1077         FGATCController()
1078 {
1079     parent = par;
1080 }
1081
1082 void FGStartupController::announcePosition(int id,
1083         FGAIFlightPlan * intendedRoute,
1084         int currentPosition, double lat,
1085         double lon, double heading,
1086         double speed, double alt,
1087         double radius, int leg,
1088         FGAIAircraft * ref)
1089 {
1090     init();
1091     TrafficVectorIterator i = activeTraffic.begin();
1092     // Search whether the current id alread has an entry
1093     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1094     if (activeTraffic.size()) {
1095         //while ((i->getId() != id) && i != activeTraffic.end()) {
1096         while (i != activeTraffic.end()) {
1097             if (i->getId() == id) {
1098                 break;
1099             }
1100             i++;
1101         }
1102     }
1103     // Add a new TrafficRecord if no one exsists for this aircraft.
1104     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1105         FGTrafficRecord rec;
1106         rec.setId(id);
1107
1108         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1109         rec.setRunway(intendedRoute->getRunway());
1110         rec.setLeg(leg);
1111         rec.setPositionAndIntentions(currentPosition, intendedRoute);
1112         //rec.setCallSign(callsign);
1113         rec.setAircraft(ref);
1114         rec.setHoldPosition(true);
1115         activeTraffic.push_back(rec);
1116     } else {
1117         i->setPositionAndIntentions(currentPosition, intendedRoute);
1118         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1119
1120     }
1121 }
1122
1123 // NOTE:
1124 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
1125 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
1126 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1127 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1128 // Note that this function is probably obsolete
1129 bool FGStartupController::hasInstruction(int id)
1130 {
1131     TrafficVectorIterator i = activeTraffic.begin();
1132     // Search search if the current id has an entry
1133     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1134     if (activeTraffic.size()) {
1135         //while ((i->getId() != id) && i != activeTraffic.end()) {
1136         while (i != activeTraffic.end()) {
1137             if (i->getId() == id) {
1138                 break;
1139             }
1140             i++;
1141         }
1142     }
1143     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1144         SG_LOG(SG_GENERAL, SG_ALERT,
1145                "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1146     } else {
1147         return i->hasInstruction();
1148     }
1149     return false;
1150 }
1151
1152
1153 FGATCInstruction FGStartupController::getInstruction(int id)
1154 {
1155     TrafficVectorIterator i = activeTraffic.begin();
1156     // Search search if the current id has an entry
1157     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1158     if (activeTraffic.size()) {
1159         //while ((i->getId() != id) && i != activeTraffic.end()) {
1160         while (i != activeTraffic.end()) {
1161             if (i->getId() == id) {
1162                 break;
1163             }
1164             i++;
1165         }
1166     }
1167     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1168         SG_LOG(SG_GENERAL, SG_ALERT,
1169                "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1170     } else {
1171         return i->getInstruction();
1172     }
1173     return FGATCInstruction();
1174 }
1175
1176 void FGStartupController::signOff(int id)
1177 {
1178     TrafficVectorIterator i = activeTraffic.begin();
1179     // Search search if the current id alread has an entry
1180     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1181     if (activeTraffic.size()) {
1182         //while ((i->getId() != id) && i != activeTraffic.end()) {
1183         while (i != activeTraffic.end()) {
1184             if (i->getId() == id) {
1185                 break;
1186             }
1187             i++;
1188         }
1189     }
1190     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1191         SG_LOG(SG_GENERAL, SG_ALERT,
1192                "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN);
1193     } else {
1194         //cerr << i->getAircraft()->getCallSign() << " signing off from startupcontroller" << endl;
1195         i = activeTraffic.erase(i);
1196     }
1197 }
1198
1199 bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId,
1200         AtcMsgDir msgDir)
1201 {
1202     int state = i->getState();
1203     if ((state == st) && available) {
1204         if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
1205
1206             //cerr << "Checking state " << st << " for " << i->getAircraft()->getCallSign() << endl;
1207             static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
1208             int n = trans_num->getIntValue();
1209             if (n == 0) {
1210                 trans_num->setIntValue(-1);
1211                 // PopupCallback(n);
1212                 //cerr << "Selected transmission message " << n << endl;
1213                 FGATCDialogNew::instance()->removeEntry(1);
1214             } else {
1215                 //cerr << "creading message for " << i->getAircraft()->getCallSign() << endl;
1216                 transmit(&(*i), &(*parent), msgId, msgDir, false);
1217                 return false;
1218             }
1219         }
1220         if (now > startTime) {
1221             //cerr << "Transmitting startup msg" << endl;
1222             transmit(&(*i), &(*parent), msgId, msgDir, true);
1223             i->updateState();
1224             lastTransmission = now;
1225             available = false;
1226             return true;
1227         }
1228     }
1229     return false;
1230 }
1231
1232 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
1233         double heading, double speed, double alt,
1234         double dt)
1235 {
1236     TrafficVectorIterator i = activeTraffic.begin();
1237     // Search search if the current id has an entry
1238     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1239     TrafficVectorIterator current, closest;
1240     if (activeTraffic.size()) {
1241         //while ((i->getId() != id) && i != activeTraffic.end()) {
1242         while (i != activeTraffic.end()) {
1243             if (i->getId() == id) {
1244                 break;
1245             }
1246             i++;
1247         }
1248     }
1249 //    // update position of the current aircraft
1250
1251     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1252         SG_LOG(SG_GENERAL, SG_ALERT,
1253                "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
1254     } else {
1255         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1256         current = i;
1257     }
1258     setDt(getDt() + dt);
1259
1260     int state = i->getState();
1261
1262     // The user controlled aircraft should have crased here, because it doesn't have a traffic reference.
1263     // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
1264     time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
1265     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1266     //cerr << i->getAircraft()->getTrafficRef()->getCallSign()
1267     //     << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1268     //     << " at parking " << getGateName(i->getAircraft()) << endl;
1269
1270     if ((now - lastTransmission) > 3 + (rand() % 15)) {
1271         available = true;
1272     }
1273
1274     checkTransmissionState(0, now, (startTime + 0  ), i, MSG_ANNOUNCE_ENGINE_START,                     ATC_AIR_TO_GROUND);
1275     checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START,                      ATC_AIR_TO_GROUND);
1276     checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START,                       ATC_GROUND_TO_AIR);
1277     checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START,                  ATC_AIR_TO_GROUND);
1278     if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,       ATC_AIR_TO_GROUND)) {
1279         i->nextFrequency();
1280     }
1281     checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT,                          ATC_AIR_TO_GROUND);
1282     checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT,              ATC_GROUND_TO_AIR);
1283     checkTransmissionState(7, now, (startTime + 180), i, MSG_REQUEST_PUSHBACK_CLEARANCE,                ATC_AIR_TO_GROUND);
1284
1285
1286
1287     if ((state == 8) && available) {
1288         if (now > startTime + 200) {
1289             if (i->pushBackAllowed()) {
1290                 i->allowRepeatedTransmissions();
1291                 transmit(&(*i), &(*parent), MSG_PERMIT_PUSHBACK_CLEARANCE,
1292                          ATC_GROUND_TO_AIR, true);
1293                 i->updateState();
1294             } else {
1295                 transmit(&(*i), &(*parent), MSG_HOLD_PUSHBACK_CLEARANCE,
1296                          ATC_GROUND_TO_AIR, true);
1297                 i->suppressRepeatedTransmissions();
1298             }
1299             lastTransmission = now;
1300             available = false;
1301         }
1302     }
1303     if ((state == 9) && available) {
1304         i->setHoldPosition(false);
1305     }
1306 }
1307
1308 // Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
1309 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
1310                             double lon, double elev, double hdg, double slope)
1311 {
1312     SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
1313     obj_pos = geod.makeZUpFrame();
1314     // hdg is not a compass heading, but a counter-clockwise rotation
1315     // around the Z axis
1316     obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
1317                                         0.0, 0.0, 1.0));
1318     obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
1319                                         0.0, 1.0, 0.0));
1320 }
1321
1322
1323 void FGStartupController::render(bool visible)
1324 {
1325
1326     SGMaterialLib *matlib = globals->get_matlib();
1327     if (group) {
1328         //int nr = ;
1329         globals->get_scenery()->get_scene_graph()->removeChild(group);
1330         //while (group->getNumChildren()) {
1331         //  cerr << "Number of children: " << group->getNumChildren() << endl;
1332         //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
1333         //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
1334         //geode->releaseGLObjects();
1335         //group->removeChild(geode);
1336         //delete geode;
1337         group = 0;
1338     }
1339     if (visible) {
1340         group = new osg::Group;
1341         FGScenery * local_scenery = globals->get_scenery();
1342         double elevation_meters = 0.0;
1343         double elevation_feet = 0.0;
1344
1345
1346         //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
1347         double dx = 0;
1348         time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1349         for   (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1350             if (i->isActive(300)) {
1351                 // Handle start point
1352                 int pos = i->getCurrentPosition();
1353                 //cerr << "rendering for " << i->getAircraft()->getCallSign() << "pos = " << pos << endl;
1354                 if (pos > 0) {
1355                     FGTaxiSegment *segment  = parent->getGroundNetwork()->findSegment(pos);
1356                     SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1357                     SGGeod end  (SGGeod::fromDeg(segment->getEnd()->getLongitude(), segment->getEnd()->getLatitude()));
1358
1359                     double length = SGGeodesy::distanceM(start, end);
1360                     //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
1361
1362                     double az2, heading; //, distanceM;
1363                     SGGeodesy::inverse(start, end, heading, az2, length);
1364                     double coveredDistance = length * 0.5;
1365                     SGGeod center;
1366                     SGGeodesy::direct(start, heading, coveredDistance, center, az2);
1367                     //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
1368                     ///////////////////////////////////////////////////////////////////////////////
1369                     // Make a helper function out of this
1370                     osg::Matrix obj_pos;
1371                     osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1372                     obj_trans->setDataVariance(osg::Object::STATIC);
1373                     // Experimental: Calculate slope here, based on length, and the individual elevations
1374                     double elevationStart;
1375                     if (isUserAircraft((i)->getAircraft())) {
1376                         elevationStart = fgGetDouble("/position/ground-elev-m");
1377                     } else {
1378                         elevationStart = ((i)->getAircraft()->_getAltitude() * SG_FEET_TO_METER);
1379                     }
1380                     double elevationEnd   = segment->getEnd()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
1381                     if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1382                         SGGeod center2 = end;
1383                         center2.setElevationM(SG_MAX_ELEVATION_M);
1384                         if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1385                             elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1386                             //elevation_meters += 0.5;
1387                         }
1388                         else {
1389                             elevationEnd = parent->getElevation();
1390                         }
1391                         segment->getEnd()->setElevation(elevationEnd);
1392                     }
1393
1394                     double elevationMean  = (elevationStart + elevationEnd) / 2.0;
1395                     double elevDiff       = elevationEnd - elevationStart;
1396
1397                     double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1398
1399                     //cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
1400
1401                     WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean + 0.5 + dx, -(heading), slope );
1402                     ;
1403
1404                     obj_trans->setMatrix( obj_pos );
1405                     //osg::Vec3 center(0, 0, 0)
1406
1407                     float width = length /2.0;
1408                     osg::Vec3 corner(-width, 0, 0.25f);
1409                     osg::Vec3 widthVec(2*width + 1, 0, 0);
1410                     osg::Vec3 heightVec(0, 1, 0);
1411                     osg::Geometry* geometry;
1412                     geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1413                     simgear::EffectGeode* geode = new simgear::EffectGeode;
1414                     geode->setName("test");
1415                     geode->addDrawable(geometry);
1416                     //osg::Node *custom_obj;
1417                     SGMaterial *mat;
1418                     if (segment->hasBlock(now)) {
1419                         mat = matlib->find("UnidirectionalTaperRed");
1420                     } else {
1421                         mat = matlib->find("UnidirectionalTaperGreen");
1422                     }
1423                     if (mat)
1424                         geode->setEffect(mat->get_effect());
1425                     obj_trans->addChild(geode);
1426                     // wire as much of the scene graph together as we can
1427                     //->addChild( obj_trans );
1428                     group->addChild( obj_trans );
1429                     /////////////////////////////////////////////////////////////////////
1430                 } else {
1431                     //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
1432                 }
1433                 for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
1434                     osg::Matrix obj_pos;
1435                     int k = (*j);
1436                     if (k > 0) {
1437                         //cerr << "rendering for " << i->getAircraft()->getCallSign() << "intention = " << k << endl;
1438                         osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1439                         obj_trans->setDataVariance(osg::Object::STATIC);
1440                         FGTaxiSegment *segment  = parent->getGroundNetwork()->findSegment(k);
1441
1442                         double elevationStart = segment->getStart()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
1443                         double elevationEnd   = segment->getEnd  ()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
1444                         if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
1445                             SGGeod center2 = segment->getStart()->getGeod();
1446                             center2.setElevationM(SG_MAX_ELEVATION_M);
1447                             if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
1448                                 elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
1449                                 //elevation_meters += 0.5;
1450                             }
1451                             else {
1452                                 elevationStart = parent->getElevation();
1453                             }
1454                             segment->getStart()->setElevation(elevationStart);
1455                         }
1456                         if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1457                             SGGeod center2 = segment->getEnd()->getGeod();
1458                             center2.setElevationM(SG_MAX_ELEVATION_M);
1459                             if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1460                                 elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1461                                 //elevation_meters += 0.5;
1462                             }
1463                             else {
1464                                 elevationEnd = parent->getElevation();
1465                             }
1466                             segment->getEnd()->setElevation(elevationEnd);
1467                         }
1468
1469                         double elevationMean  = (elevationStart + elevationEnd) / 2.0;
1470                         double elevDiff       = elevationEnd - elevationStart;
1471                         double length         = segment->getLength();
1472                         double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1473
1474                         //cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
1475
1476
1477                         WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), elevationMean + 0.5 + dx, -(segment->getHeading()), slope );
1478
1479                         //WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), parent->getElevation()+8+dx, -(segment->getHeading()) );
1480
1481                         obj_trans->setMatrix( obj_pos );
1482                         //osg::Vec3 center(0, 0, 0)
1483
1484                         float width = segment->getLength() /2.0;
1485                         osg::Vec3 corner(-width, 0, 0.25f);
1486                         osg::Vec3 widthVec(2*width + 1, 0, 0);
1487                         osg::Vec3 heightVec(0, 1, 0);
1488                         osg::Geometry* geometry;
1489                         geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1490                         simgear::EffectGeode* geode = new simgear::EffectGeode;
1491                         geode->setName("test");
1492                         geode->addDrawable(geometry);
1493                         //osg::Node *custom_obj;
1494                         SGMaterial *mat;
1495                         if (segment->hasBlock(now)) {
1496                             mat = matlib->find("UnidirectionalTaperRed");
1497                         } else {
1498                             mat = matlib->find("UnidirectionalTaperGreen");
1499                         }
1500                         if (mat)
1501                             geode->setEffect(mat->get_effect());
1502                         obj_trans->addChild(geode);
1503                         // wire as much of the scene graph together as we can
1504                         //->addChild( obj_trans );
1505                         group->addChild( obj_trans );
1506                     } else {
1507                         //cerr << "BIG FAT WARNING: k is here : " << pos << endl;
1508                     }
1509                 }
1510                 dx += 0.2;
1511             }
1512         }
1513         globals->get_scenery()->get_scene_graph()->addChild(group);
1514     }
1515 }
1516
1517 string FGStartupController::getName() {
1518     return string(parent->getId() + "-startup");
1519 }
1520
1521 void FGStartupController::update(double dt)
1522 {
1523
1524 }
1525
1526
1527
1528 /***************************************************************************
1529  * class FGApproachController
1530  *
1531  **************************************************************************/
1532 FGApproachController::FGApproachController(FGAirportDynamics *par):
1533         FGATCController()
1534 {
1535     parent = par;
1536 }
1537
1538 //
1539 void FGApproachController::announcePosition(int id,
1540         FGAIFlightPlan * intendedRoute,
1541         int currentPosition,
1542         double lat, double lon,
1543         double heading, double speed,
1544         double alt, double radius,
1545         int leg, FGAIAircraft * ref)
1546 {
1547     init();
1548     TrafficVectorIterator i = activeTraffic.begin();
1549     // Search whether the current id alread has an entry
1550     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1551     if (activeTraffic.size()) {
1552         //while ((i->getId() != id) && i != activeTraffic.end()) {
1553         while (i != activeTraffic.end()) {
1554             if (i->getId() == id) {
1555                 break;
1556             }
1557             i++;
1558         }
1559     }
1560     // Add a new TrafficRecord if no one exsists for this aircraft.
1561     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1562         FGTrafficRecord rec;
1563         rec.setId(id);
1564
1565         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1566         rec.setRunway(intendedRoute->getRunway());
1567         rec.setLeg(leg);
1568         //rec.setCallSign(callsign);
1569         rec.setAircraft(ref);
1570         activeTraffic.push_back(rec);
1571     } else {
1572         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1573     }
1574 }
1575
1576 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1577         double heading, double speed, double alt,
1578         double dt)
1579 {
1580     TrafficVectorIterator i = activeTraffic.begin();
1581     // Search search if the current id has an entry
1582     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1583     TrafficVectorIterator current, closest;
1584     if (activeTraffic.size()) {
1585         //while ((i->getId() != id) && i != activeTraffic.end()) {
1586         while (i != activeTraffic.end()) {
1587             if (i->getId() == id) {
1588                 break;
1589             }
1590             i++;
1591         }
1592     }
1593 //    // update position of the current aircraft
1594     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1595         SG_LOG(SG_GENERAL, SG_ALERT,
1596                "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
1597     } else {
1598         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1599         current = i;
1600         //cerr << "ApproachController: checking for speed" << endl;
1601         time_t time_diff =
1602             current->getAircraft()->
1603             checkForArrivalTime(string("final001"));
1604         if (time_diff > 15) {
1605             current->setSpeedAdjustment(current->getAircraft()->
1606                                         getPerformance()->vDescent() *
1607                                         1.35);
1608         } else if (time_diff > 5) {
1609             current->setSpeedAdjustment(current->getAircraft()->
1610                                         getPerformance()->vDescent() *
1611                                         1.2);
1612         } else if (time_diff < -15) {
1613             current->setSpeedAdjustment(current->getAircraft()->
1614                                         getPerformance()->vDescent() *
1615                                         0.65);
1616         } else if (time_diff < -5) {
1617             current->setSpeedAdjustment(current->getAircraft()->
1618                                         getPerformance()->vDescent() *
1619                                         0.8);
1620         } else {
1621             current->clearSpeedAdjustment();
1622         }
1623         //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1624     }
1625     setDt(getDt() + dt);
1626 }
1627
1628 void FGApproachController::signOff(int id)
1629 {
1630     TrafficVectorIterator i = activeTraffic.begin();
1631     // Search search if the current id alread has an entry
1632     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1633     if (activeTraffic.size()) {
1634         //while ((i->getId() != id) && i != activeTraffic.end()) {
1635         while (i != activeTraffic.end()) {
1636             if (i->getId() == id) {
1637                 break;
1638             }
1639             i++;
1640         }
1641     }
1642     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1643         SG_LOG(SG_GENERAL, SG_ALERT,
1644                "AI error: Aircraft without traffic record is signing off from approach at " << SG_ORIGIN);
1645     } else {
1646         i = activeTraffic.erase(i);
1647     }
1648 }
1649
1650 void FGApproachController::update(double dt)
1651 {
1652
1653 }
1654
1655
1656
1657 bool FGApproachController::hasInstruction(int id)
1658 {
1659     TrafficVectorIterator i = activeTraffic.begin();
1660     // Search search if the current id has an entry
1661     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1662     if (activeTraffic.size()) {
1663         //while ((i->getId() != id) && i != activeTraffic.end()) {
1664         while (i != activeTraffic.end()) {
1665             if (i->getId() == id) {
1666                 break;
1667             }
1668             i++;
1669         }
1670     }
1671     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1672         SG_LOG(SG_GENERAL, SG_ALERT,
1673                "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1674     } else {
1675         return i->hasInstruction();
1676     }
1677     return false;
1678 }
1679
1680
1681 FGATCInstruction FGApproachController::getInstruction(int id)
1682 {
1683     TrafficVectorIterator i = activeTraffic.begin();
1684     // Search search if the current id has an entry
1685     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1686     if (activeTraffic.size()) {
1687         //while ((i->getId() != id) && i != activeTraffic.end()) {
1688         while (i != activeTraffic.end()) {
1689             if (i->getId() == id) {
1690                 break;
1691             }
1692             i++;
1693         }
1694     }
1695     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1696         SG_LOG(SG_GENERAL, SG_ALERT,
1697                "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1698     } else {
1699         return i->getInstruction();
1700     }
1701     return FGATCInstruction();
1702 }
1703
1704
1705 ActiveRunway *FGApproachController::getRunway(string name)
1706 {
1707     ActiveRunwayVecIterator rwy = activeRunways.begin();
1708     if (activeRunways.size()) {
1709         while (rwy != activeRunways.end()) {
1710             if (rwy->getRunwayName() == name) {
1711                 break;
1712             }
1713             rwy++;
1714         }
1715     }
1716     if (rwy == activeRunways.end()) {
1717         ActiveRunway aRwy(name, 0);
1718         activeRunways.push_back(aRwy);
1719         rwy = activeRunways.end() - 1;
1720     }
1721     return &(*rwy);
1722 }
1723
1724 void FGApproachController::render(bool visible) {
1725     //cerr << "FGApproachController::render function not yet implemented" << endl;
1726 }
1727
1728
1729
1730 string FGApproachController::getName() {
1731     return string(parent->getId() + "-approach");
1732 }