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