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