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