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