]> 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         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         if(ground_to_air)
892                 double transmitter_power = 49.0;
893         else
894                 double transmitter_power = 43.0;
895         if(ground_to_air)
896                 double antenna_gain = 5.0; //pilot plane's antenna gain + Controller antenna gain
897         else
898                 double antenna_gain = 2.0; //pilot plane's antenna gain + AI aircraft antenna gain
899         double link_budget = transmitter_power - receiver_sensitivity + antenna_gain;   
900         
901         
902         // first Fresnel zone radius
903         // frequency in the middle of the bandplan, more accuracy is not necessary
904         double fz_clr= 8.657 * sqrt(distance_m / 0.125);
905         
906         // TODO: If we clear the first Fresnel zone, we are into line of sight teritory
907
908         // else we need to calculate point to point link loss
909
910         point_to_point(itm_elev, sender_alt, own_alt,
911                 eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate,
912                 pol, conf, rel, dbloss, strmode, errnum);
913
914         cerr << "ITM:: Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum << endl;
915         
916         //if (errnum !=0 && errnum !=1)
917         //      return -1;
918         double snr = link_budget - dbloss;
919         return snr;
920
921 }
922
923 string FGATCController::formatATCFrequency3_2(int freq)
924 {
925     char buffer[7];
926     snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
927     return string(buffer);
928 }
929
930 // TODO: Set transponder codes according to real-world routes.
931 // The current version just returns a random string of four octal numbers. 
932 string FGATCController::genTransponderCode(string fltRules)
933 {
934     if (fltRules == "VFR") {
935         return string("1200");
936     } else {
937         char buffer[5];
938         snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
939                  rand() % 8);
940         return string(buffer);
941     }
942 }
943
944 void FGATCController::init() 
945 {
946    if (!initialized) {
947        FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
948        mgr->addController(this);
949        initialized = true;
950     }
951 }
952
953 /***************************************************************************
954  * class FGTowerController
955  *
956  **************************************************************************/
957 FGTowerController::FGTowerController(FGAirportDynamics *par) :
958 FGATCController()
959 {
960     parent = par;
961 }
962
963 // 
964 void FGTowerController::announcePosition(int id,
965                                          FGAIFlightPlan * intendedRoute,
966                                          int currentPosition, double lat,
967                                          double lon, double heading,
968                                          double speed, double alt,
969                                          double radius, int leg,
970                                          FGAIAircraft * ref)
971 {
972     init();
973     TrafficVectorIterator i = activeTraffic.begin();
974     // Search whether the current id alread has an entry
975     // This might be faster using a map instead of a vector, but let's start by taking a safe route
976     if (activeTraffic.size()) {
977         //while ((i->getId() != id) && i != activeTraffic.end()) {
978         while (i != activeTraffic.end()) {
979             if (i->getId() == id) {
980                 break;
981             }
982             i++;
983         }
984     }
985     // Add a new TrafficRecord if no one exsists for this aircraft.
986     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
987         FGTrafficRecord rec;
988         rec.setId(id);
989
990         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
991         rec.setRunway(intendedRoute->getRunway());
992         rec.setLeg(leg);
993         //rec.setCallSign(callsign);
994         rec.setRadius(radius);
995         rec.setAircraft(ref);
996         activeTraffic.push_back(rec);
997         // Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway. 
998         ActiveRunwayVecIterator rwy = activeRunways.begin();
999         if (activeRunways.size()) {
1000             while (rwy != activeRunways.end()) {
1001                 if (rwy->getRunwayName() == intendedRoute->getRunway()) {
1002                     break;
1003                 }
1004                 rwy++;
1005             }
1006         }
1007         if (rwy == activeRunways.end()) {
1008             ActiveRunway aRwy(intendedRoute->getRunway(), id);
1009             aRwy.addToDepartureCue(ref);
1010             activeRunways.push_back(aRwy);
1011             rwy = (activeRunways.end()-1);
1012         } else {
1013             rwy->addToDepartureCue(ref);
1014         }
1015
1016         //cerr << ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getDepartureCueSize() <<  " for takeoff " << endl;
1017     } else {
1018         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1019     }
1020 }
1021
1022 void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
1023                                                   double heading, double speed, double alt,
1024                                                   double dt)
1025 {
1026     TrafficVectorIterator i = activeTraffic.begin();
1027     // Search whether the current id has an entry
1028     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1029     TrafficVectorIterator current, closest;
1030     if (activeTraffic.size()) {
1031         //while ((i->getId() != id) && i != activeTraffic.end()) {
1032         while (i != activeTraffic.end()) {
1033             if (i->getId() == id) {
1034                 break;
1035             }
1036             i++;
1037         }
1038     }
1039 //    // update position of the current aircraft
1040     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1041         SG_LOG(SG_GENERAL, SG_ALERT,
1042                "AI error: updating aircraft without traffic record");
1043     } else {
1044         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1045         current = i;
1046     }
1047     setDt(getDt() + dt);
1048
1049     // see if we already have a clearance record for the currently active runway
1050     // NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is
1051     // already exists here. So, we can simplify the current code. 
1052     ActiveRunwayVecIterator rwy = activeRunways.begin();
1053     while (rwy != activeRunways.end()) {
1054         if (rwy->getRunwayName() == current->getRunway()) {
1055             break;
1056         }
1057         rwy++;
1058     }
1059
1060     // only bother running the following code if the current aircraft is the
1061     // first in line for depature
1062     /* if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
1063         if (rwy->getCleared()) {
1064             if (id == rwy->getCleared()) {
1065                 current->setHoldPosition(false);
1066             } else {
1067                 current->setHoldPosition(true);
1068             }
1069         } else {
1070             // For now. At later stages, this will probably be the place to check for inbound traffc.
1071             rwy->setCleared(id);
1072         }
1073     } */
1074     // only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control
1075     if (current->getAircraft()->getTakeOffStatus() == 2) {
1076         current->setHoldPosition(true);
1077         int clearanceId = rwy->getCleared();
1078         if (clearanceId) {
1079             if (id == clearanceId) {
1080                 current->setHoldPosition(false);
1081             }
1082         } else {
1083             if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
1084                 rwy->setCleared(id);
1085             }
1086         }
1087     }
1088 }
1089
1090
1091 void FGTowerController::signOff(int id)
1092 {
1093     TrafficVectorIterator i = activeTraffic.begin();
1094     // Search search if the current id alread 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 this aircraft has left the runway, we can clear the departure record for this runway
1106     ActiveRunwayVecIterator rwy = activeRunways.begin();
1107     if (activeRunways.size()) {
1108         //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
1109         while (rwy != activeRunways.end()) {
1110             if (rwy->getRunwayName() == i->getRunway()) {
1111                 break;
1112             }
1113             rwy++;
1114         }
1115         if (rwy != activeRunways.end()) {
1116             rwy->setCleared(0);
1117             rwy->updateDepartureCue();
1118         } else {
1119             SG_LOG(SG_GENERAL, SG_ALERT,
1120                    "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
1121         }
1122     }
1123     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1124         SG_LOG(SG_GENERAL, SG_ALERT,
1125                "AI error: Aircraft without traffic record is signing off from tower");
1126     } else {
1127         i->getAircraft()->resetTakeOffStatus();
1128         i = activeTraffic.erase(i);
1129         //cerr << "Signing off from tower controller" << endl;
1130     }
1131 }
1132
1133 // NOTE:
1134 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
1135 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
1136 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1137 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1138 // Note that this function is probably obsolete
1139 bool FGTowerController::hasInstruction(int id)
1140 {
1141     TrafficVectorIterator i = activeTraffic.begin();
1142     // Search search if the current id has an entry
1143     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1144     if (activeTraffic.size()) {
1145         //while ((i->getId() != id) && i != activeTraffic.end()) {
1146         while (i != activeTraffic.end()) {
1147             if (i->getId() == id) {
1148                 break;
1149             }
1150             i++;
1151         }
1152     }
1153     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1154         SG_LOG(SG_GENERAL, SG_ALERT,
1155                "AI error: checking ATC instruction for aircraft without traffic record");
1156     } else {
1157         return i->hasInstruction();
1158     }
1159     return false;
1160 }
1161
1162
1163 FGATCInstruction FGTowerController::getInstruction(int id)
1164 {
1165     TrafficVectorIterator i = activeTraffic.begin();
1166     // Search search if the current id has an entry
1167     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1168     if (activeTraffic.size()) {
1169         //while ((i->getId() != id) && i != activeTraffic.end()) {
1170         while (i != activeTraffic.end()) {
1171             if (i->getId() == id) {
1172                 break;
1173             }
1174             i++;
1175         }
1176     }
1177     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1178         SG_LOG(SG_GENERAL, SG_ALERT,
1179                "AI error: requesting ATC instruction for aircraft without traffic record");
1180     } else {
1181         return i->getInstruction();
1182     }
1183     return FGATCInstruction();
1184 }
1185
1186 void FGTowerController::render(bool visible) {
1187     //cerr << "FGTowerController::render function not yet implemented" << endl;
1188 }
1189
1190 string FGTowerController::getName() {
1191     return string(parent->getId() + "-tower");
1192 }
1193
1194
1195
1196 /***************************************************************************
1197  * class FGStartupController
1198  *
1199  **************************************************************************/
1200 FGStartupController::FGStartupController(FGAirportDynamics *par):
1201     FGATCController()
1202 {
1203     parent = par;
1204 }
1205
1206 void FGStartupController::announcePosition(int id,
1207                                            FGAIFlightPlan * intendedRoute,
1208                                            int currentPosition, double lat,
1209                                            double lon, double heading,
1210                                            double speed, double alt,
1211                                            double radius, int leg,
1212                                            FGAIAircraft * ref)
1213 {
1214     init();
1215     TrafficVectorIterator i = activeTraffic.begin();
1216     // Search whether the current id alread has an entry
1217     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1218     if (activeTraffic.size()) {
1219         //while ((i->getId() != id) && i != activeTraffic.end()) {
1220         while (i != activeTraffic.end()) {
1221             if (i->getId() == id) {
1222                 break;
1223             }
1224             i++;
1225         }
1226     }
1227     // Add a new TrafficRecord if no one exsists for this aircraft.
1228     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1229         FGTrafficRecord rec;
1230         rec.setId(id);
1231
1232         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1233         rec.setRunway(intendedRoute->getRunway());
1234         rec.setLeg(leg);
1235         rec.setPositionAndIntentions(currentPosition, intendedRoute);
1236         //rec.setCallSign(callsign);
1237         rec.setAircraft(ref);
1238         rec.setHoldPosition(true);
1239         activeTraffic.push_back(rec);
1240     } else {
1241         i->setPositionAndIntentions(currentPosition, intendedRoute);
1242         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1243
1244     }
1245 }
1246
1247 // NOTE:
1248 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
1249 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
1250 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1251 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1252 // Note that this function is probably obsolete
1253 bool FGStartupController::hasInstruction(int id)
1254 {
1255     TrafficVectorIterator i = activeTraffic.begin();
1256     // Search search if the current id has an entry
1257     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1258     if (activeTraffic.size()) {
1259         //while ((i->getId() != id) && i != activeTraffic.end()) {
1260         while (i != activeTraffic.end()) {
1261             if (i->getId() == id) {
1262                 break;
1263             }
1264             i++;
1265         }
1266     }
1267     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1268         SG_LOG(SG_GENERAL, SG_ALERT,
1269                "AI error: checking ATC instruction for aircraft without traffic record");
1270     } else {
1271         return i->hasInstruction();
1272     }
1273     return false;
1274 }
1275
1276
1277 FGATCInstruction FGStartupController::getInstruction(int id)
1278 {
1279     TrafficVectorIterator i = activeTraffic.begin();
1280     // Search search if the current id has an entry
1281     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1282     if (activeTraffic.size()) {
1283         //while ((i->getId() != id) && i != activeTraffic.end()) {
1284         while (i != activeTraffic.end()) {
1285             if (i->getId() == id) {
1286                 break;
1287             }
1288             i++;
1289         }
1290     }
1291     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1292         SG_LOG(SG_GENERAL, SG_ALERT,
1293                "AI error: requesting ATC instruction for aircraft without traffic record");
1294     } else {
1295         return i->getInstruction();
1296     }
1297     return FGATCInstruction();
1298 }
1299
1300 void FGStartupController::signOff(int id)
1301 {
1302     TrafficVectorIterator i = activeTraffic.begin();
1303     // Search search if the current id alread has an entry
1304     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1305     if (activeTraffic.size()) {
1306         //while ((i->getId() != id) && i != activeTraffic.end()) {
1307         while (i != activeTraffic.end()) {
1308             if (i->getId() == id) {
1309                 break;
1310             }
1311             i++;
1312         }
1313     }
1314     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1315         SG_LOG(SG_GENERAL, SG_ALERT,
1316                "AI error: Aircraft without traffic record is signing off from tower");
1317     } else {
1318         //cerr << i->getAircraft()->getCallSign() << " signing off from startupcontroller" << endl;
1319         i = activeTraffic.erase(i);
1320     }
1321 }
1322
1323 bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId,
1324                                AtcMsgDir msgDir)
1325 {
1326     int state = i->getState();
1327     if ((state == st) && available) {
1328         if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
1329             
1330             //cerr << "Checking state " << st << " for " << i->getAircraft()->getCallSign() << endl;
1331             static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
1332             int n = trans_num->getIntValue();
1333             if (n == 0) {
1334                 trans_num->setIntValue(-1);
1335                  // PopupCallback(n);
1336                  //cerr << "Selected transmission message " << n << endl;
1337                  FGATCManager *atc = (FGATCManager*) globals->get_subsystem("atc");
1338                  atc->getATCDialog()->removeEntry(1);
1339             } else {
1340                 //cerr << "creading message for " << i->getAircraft()->getCallSign() << endl;
1341                 transmit(&(*i), &(*parent), msgId, msgDir, false);
1342                 return false;
1343             }
1344         }
1345         if (now > startTime) {
1346             //cerr << "Transmitting startup msg" << endl;
1347             transmit(&(*i), &(*parent), msgId, msgDir, true);
1348             i->updateState();
1349             lastTransmission = now;
1350             available = false;
1351             return true;
1352         }
1353     }
1354     return false;
1355 }
1356
1357 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
1358                                                     double heading, double speed, double alt,
1359                                                     double dt)
1360 {
1361     TrafficVectorIterator i = activeTraffic.begin();
1362     // Search search if the current id has an entry
1363     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1364     TrafficVectorIterator current, closest;
1365     if (activeTraffic.size()) {
1366         //while ((i->getId() != id) && i != activeTraffic.end()) {
1367         while (i != activeTraffic.end()) {
1368             if (i->getId() == id) {
1369                 break;
1370             }
1371             i++;
1372         }
1373     }
1374 //    // update position of the current aircraft
1375
1376     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1377         SG_LOG(SG_GENERAL, SG_ALERT,
1378                "AI error: updating aircraft without traffic record");
1379     } else {
1380         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1381         current = i;
1382     }
1383     setDt(getDt() + dt);
1384
1385     int state = i->getState();
1386
1387     // The user controlled aircraft should have crased here, because it doesn't have a traffic reference. 
1388     // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
1389     time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
1390     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1391     //cerr << i->getAircraft()->getTrafficRef()->getCallSign() 
1392     //     << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1393     //     << " at parking " << getGateName(i->getAircraft()) << endl;
1394
1395     if ((now - lastTransmission) > 3 + (rand() % 15)) {
1396         available = true;
1397     }
1398
1399     checkTransmissionState(0, now, (startTime + 0  ), i, MSG_ANNOUNCE_ENGINE_START,                     ATC_AIR_TO_GROUND);
1400     checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START,                      ATC_AIR_TO_GROUND);
1401     checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START,                       ATC_GROUND_TO_AIR);
1402     checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START,                  ATC_AIR_TO_GROUND);
1403     if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,       ATC_AIR_TO_GROUND)) {
1404         i->nextFrequency();
1405     }
1406     checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT,                          ATC_AIR_TO_GROUND);
1407     checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT,              ATC_GROUND_TO_AIR);
1408     checkTransmissionState(7, now, (startTime + 180), i, MSG_REQUEST_PUSHBACK_CLEARANCE,                ATC_AIR_TO_GROUND);
1409
1410
1411    
1412     if ((state == 8) && available) {
1413         if (now > startTime + 200) {
1414             if (i->pushBackAllowed()) {
1415                 i->allowRepeatedTransmissions();
1416                 transmit(&(*i), &(*parent), MSG_PERMIT_PUSHBACK_CLEARANCE,
1417                          ATC_GROUND_TO_AIR, true);
1418                 i->updateState();
1419             } else {
1420                 transmit(&(*i), &(*parent), MSG_HOLD_PUSHBACK_CLEARANCE,
1421                          ATC_GROUND_TO_AIR, true);
1422                 i->suppressRepeatedTransmissions();
1423             }
1424             lastTransmission = now;
1425             available = false;
1426         }
1427     }
1428     if ((state == 9) && available) {
1429         i->setHoldPosition(false);
1430     }
1431 }
1432
1433 // Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
1434 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
1435                             double lon, double elev, double hdg, double slope)
1436 {
1437     SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
1438     obj_pos = geod.makeZUpFrame();
1439     // hdg is not a compass heading, but a counter-clockwise rotation
1440     // around the Z axis
1441     obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
1442                                         0.0, 0.0, 1.0));
1443     obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
1444                                         0.0, 1.0, 0.0));
1445 }
1446
1447
1448 void FGStartupController::render(bool visible)
1449 {
1450
1451     SGMaterialLib *matlib = globals->get_matlib();
1452     if (group) {
1453         //int nr = ;
1454         globals->get_scenery()->get_scene_graph()->removeChild(group);
1455         //while (group->getNumChildren()) {
1456         //  cerr << "Number of children: " << group->getNumChildren() << endl;
1457         //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
1458           //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
1459            //geode->releaseGLObjects();
1460            //group->removeChild(geode);
1461            //delete geode;
1462         group = 0;
1463     }
1464     if (visible) {
1465         group = new osg::Group;
1466         FGScenery * local_scenery = globals->get_scenery();
1467         double elevation_meters = 0.0;
1468         double elevation_feet = 0.0;
1469
1470
1471         //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
1472         double dx = 0;
1473         for   (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1474             // Handle start point
1475             int pos = i->getCurrentPosition();
1476             //cerr << "rendering for " << i->getAircraft()->getCallSign() << "pos = " << pos << endl;
1477             if (pos > 0) {
1478                 FGTaxiSegment *segment  = parent->getGroundNetwork()->findSegment(pos);
1479                 SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1480                 SGGeod end  (SGGeod::fromDeg(segment->getEnd()->getLongitude(), segment->getEnd()->getLatitude()));
1481
1482                 double length = SGGeodesy::distanceM(start, end);
1483                 //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
1484
1485                 double az2, heading; //, distanceM;
1486                 SGGeodesy::inverse(start, end, heading, az2, length);
1487                 double coveredDistance = length * 0.5;
1488                 SGGeod center;
1489                 SGGeodesy::direct(start, heading, coveredDistance, center, az2);
1490                 //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
1491                 ///////////////////////////////////////////////////////////////////////////////
1492                 // Make a helper function out of this
1493                 osg::Matrix obj_pos;
1494                 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1495                 obj_trans->setDataVariance(osg::Object::STATIC);
1496                 // Experimental: Calculate slope here, based on length, and the individual elevations
1497                 double elevationStart;
1498                 if (isUserAircraft((i)->getAircraft())) {
1499                     elevationStart = fgGetDouble("/position/ground-elev-m");
1500                 } else {
1501                     elevationStart = ((i)->getAircraft()->_getAltitude()); 
1502                 }
1503                 double elevationEnd   = segment->getEnd()->getElevation();
1504                 if (elevationEnd == 0) {
1505                     SGGeod center2 = end;
1506                     center2.setElevationM(SG_MAX_ELEVATION_M);
1507                     if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1508                         elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1509                             //elevation_meters += 0.5;
1510                     }
1511                     else { 
1512                         elevationEnd = parent->getElevation()+8+dx;
1513                     }
1514                     segment->getEnd()->setElevation(elevationEnd);
1515                 }
1516
1517                 double elevationMean  = (elevationStart + elevationEnd) / 2.0;
1518                 double elevDiff       = elevationEnd - elevationStart;
1519                
1520                double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1521                 
1522                //cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
1523
1524                 WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean + 0.5, -(heading), slope );
1525 ;
1526
1527                 obj_trans->setMatrix( obj_pos );
1528                 //osg::Vec3 center(0, 0, 0)
1529
1530                 float width = length /2.0;
1531                 osg::Vec3 corner(-width, 0, 0.25f);
1532                 osg::Vec3 widthVec(2*width + 1, 0, 0);
1533                 osg::Vec3 heightVec(0, 1, 0);
1534                 osg::Geometry* geometry;
1535                 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1536                 simgear::EffectGeode* geode = new simgear::EffectGeode;
1537                 geode->setName("test");
1538                 geode->addDrawable(geometry);
1539                 //osg::Node *custom_obj;
1540                 SGMaterial *mat = matlib->find("UnidirectionalTaper");
1541                 if (mat)
1542                     geode->setEffect(mat->get_effect());
1543                 obj_trans->addChild(geode);
1544                 // wire as much of the scene graph together as we can
1545                 //->addChild( obj_trans );
1546                 group->addChild( obj_trans );
1547                 /////////////////////////////////////////////////////////////////////
1548             } else {
1549                 //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
1550             }
1551             for(intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
1552                 osg::Matrix obj_pos;
1553                 int k = (*j);
1554                 if (k > 0) {
1555                     //cerr << "rendering for " << i->getAircraft()->getCallSign() << "intention = " << k << endl;
1556                     osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1557                     obj_trans->setDataVariance(osg::Object::STATIC);
1558                     FGTaxiSegment *segment  = parent->getGroundNetwork()->findSegment(k);
1559
1560                     double elevationStart = segment->getStart()->getElevation();
1561                     double elevationEnd   = segment->getEnd  ()->getElevation();
1562                     if (elevationStart == 0) {
1563                         SGGeod center2 = segment->getStart()->getGeod();
1564                         center2.setElevationM(SG_MAX_ELEVATION_M);
1565                         if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
1566                             elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
1567                             //elevation_meters += 0.5;
1568                         }
1569                         else { 
1570                             elevationStart = parent->getElevation()+8+dx;
1571                         }
1572                         segment->getStart()->setElevation(elevationStart);
1573                     }
1574                     if (elevationEnd == 0) {
1575                         SGGeod center2 = segment->getEnd()->getGeod();
1576                         center2.setElevationM(SG_MAX_ELEVATION_M);
1577                         if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1578                             elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1579                             //elevation_meters += 0.5;
1580                         }
1581                         else { 
1582                             elevationEnd = parent->getElevation()+8+dx;
1583                         }
1584                         segment->getEnd()->setElevation(elevationEnd);
1585                     }
1586  
1587                     double elevationMean  = (elevationStart + elevationEnd) / 2.0;
1588                     double elevDiff       = elevationEnd - elevationStart;
1589                     double length         = segment->getLength();
1590                     double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1591                 
1592                     //cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
1593
1594
1595                     WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), elevationMean + 0.5, -(segment->getHeading()), slope );
1596
1597                     //WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), parent->getElevation()+8+dx, -(segment->getHeading()) );
1598
1599                     obj_trans->setMatrix( obj_pos );
1600                     //osg::Vec3 center(0, 0, 0)
1601
1602                     float width = segment->getLength() /2.0;
1603                     osg::Vec3 corner(-width, 0, 0.25f);
1604                     osg::Vec3 widthVec(2*width + 1, 0, 0);
1605                     osg::Vec3 heightVec(0, 1, 0);
1606                     osg::Geometry* geometry;
1607                     geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1608                     simgear::EffectGeode* geode = new simgear::EffectGeode;
1609                     geode->setName("test");
1610                     geode->addDrawable(geometry);
1611                     //osg::Node *custom_obj;
1612                     SGMaterial *mat = matlib->find("UnidirectionalTaper");
1613                     if (mat)
1614                         geode->setEffect(mat->get_effect());
1615                     obj_trans->addChild(geode);
1616                     // wire as much of the scene graph together as we can
1617                     //->addChild( obj_trans );
1618                     group->addChild( obj_trans );
1619                 } else {
1620                     //cerr << "BIG FAT WARNING: k is here : " << pos << endl;
1621                 }
1622             }
1623             //dx += 0.1;
1624         }
1625         globals->get_scenery()->get_scene_graph()->addChild(group);
1626     }
1627 }
1628
1629 string FGStartupController::getName() {
1630     return string(parent->getId() + "-startup");
1631 }
1632
1633
1634 /***************************************************************************
1635  * class FGApproachController
1636  *
1637  **************************************************************************/
1638 FGApproachController::FGApproachController(FGAirportDynamics *par):
1639 FGATCController()
1640 {
1641     parent = par;
1642 }
1643
1644 // 
1645 void FGApproachController::announcePosition(int id,
1646                                             FGAIFlightPlan * intendedRoute,
1647                                             int currentPosition,
1648                                             double lat, double lon,
1649                                             double heading, double speed,
1650                                             double alt, double radius,
1651                                             int leg, FGAIAircraft * ref)
1652 {
1653     init();
1654     TrafficVectorIterator i = activeTraffic.begin();
1655     // Search whether the current id alread has an entry
1656     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1657     if (activeTraffic.size()) {
1658         //while ((i->getId() != id) && i != activeTraffic.end()) {
1659         while (i != activeTraffic.end()) {
1660             if (i->getId() == id) {
1661                 break;
1662             }
1663             i++;
1664         }
1665     }
1666     // Add a new TrafficRecord if no one exsists for this aircraft.
1667     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1668         FGTrafficRecord rec;
1669         rec.setId(id);
1670
1671         rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1672         rec.setRunway(intendedRoute->getRunway());
1673         rec.setLeg(leg);
1674         //rec.setCallSign(callsign);
1675         rec.setAircraft(ref);
1676         activeTraffic.push_back(rec);
1677     } else {
1678         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1679     }
1680 }
1681
1682 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1683                                                      double heading, double speed, double alt,
1684                                                      double dt)
1685 {
1686     TrafficVectorIterator i = activeTraffic.begin();
1687     // Search search if the current id has an entry
1688     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1689     TrafficVectorIterator current, closest;
1690     if (activeTraffic.size()) {
1691         //while ((i->getId() != id) && i != activeTraffic.end()) {
1692         while (i != activeTraffic.end()) {
1693             if (i->getId() == id) {
1694                 break;
1695             }
1696             i++;
1697         }
1698     }
1699 //    // update position of the current aircraft
1700     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1701         SG_LOG(SG_GENERAL, SG_ALERT,
1702                "AI error: updating aircraft without traffic record");
1703     } else {
1704         i->setPositionAndHeading(lat, lon, heading, speed, alt);
1705         current = i;
1706         //cerr << "ApproachController: checking for speed" << endl;
1707         time_t time_diff =
1708             current->getAircraft()->
1709             checkForArrivalTime(string("final001"));
1710         if (time_diff > 15) {
1711             current->setSpeedAdjustment(current->getAircraft()->
1712                                         getPerformance()->vDescent() *
1713                                         1.35);
1714         } else if (time_diff > 5) {
1715             current->setSpeedAdjustment(current->getAircraft()->
1716                                         getPerformance()->vDescent() *
1717                                         1.2);
1718         } else if (time_diff < -15) {
1719             current->setSpeedAdjustment(current->getAircraft()->
1720                                         getPerformance()->vDescent() *
1721                                         0.65);
1722         } else if (time_diff < -5) {
1723             current->setSpeedAdjustment(current->getAircraft()->
1724                                         getPerformance()->vDescent() *
1725                                         0.8);
1726         } else {
1727             current->clearSpeedAdjustment();
1728         }
1729         //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1730     }
1731     setDt(getDt() + dt);
1732 }
1733
1734 void FGApproachController::signOff(int id)
1735 {
1736     TrafficVectorIterator i = activeTraffic.begin();
1737     // Search search if the current id alread has an entry
1738     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1739     if (activeTraffic.size()) {
1740         //while ((i->getId() != id) && i != activeTraffic.end()) {
1741         while (i != activeTraffic.end()) {
1742             if (i->getId() == id) {
1743                 break;
1744             }
1745             i++;
1746         }
1747     }
1748     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1749         SG_LOG(SG_GENERAL, SG_ALERT,
1750                "AI error: Aircraft without traffic record is signing off from approach");
1751     } else {
1752         i = activeTraffic.erase(i);
1753     }
1754 }
1755
1756
1757
1758
1759 bool FGApproachController::hasInstruction(int id)
1760 {
1761     TrafficVectorIterator i = activeTraffic.begin();
1762     // Search search if the current id has an entry
1763     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1764     if (activeTraffic.size()) {
1765         //while ((i->getId() != id) && i != activeTraffic.end()) {
1766         while (i != activeTraffic.end()) {
1767             if (i->getId() == id) {
1768                 break;
1769             }
1770             i++;
1771         }
1772     }
1773     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1774         SG_LOG(SG_GENERAL, SG_ALERT,
1775                "AI error: checking ATC instruction for aircraft without traffic record");
1776     } else {
1777         return i->hasInstruction();
1778     }
1779     return false;
1780 }
1781
1782
1783 FGATCInstruction FGApproachController::getInstruction(int id)
1784 {
1785     TrafficVectorIterator i = activeTraffic.begin();
1786     // Search search if the current id has an entry
1787     // This might be faster using a map instead of a vector, but let's start by taking a safe route
1788     if (activeTraffic.size()) {
1789         //while ((i->getId() != id) && i != activeTraffic.end()) {
1790         while (i != activeTraffic.end()) {
1791             if (i->getId() == id) {
1792                 break;
1793             }
1794             i++;
1795         }
1796     }
1797     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1798         SG_LOG(SG_GENERAL, SG_ALERT,
1799                "AI error: requesting ATC instruction for aircraft without traffic record");
1800     } else {
1801         return i->getInstruction();
1802     }
1803     return FGATCInstruction();
1804 }
1805
1806
1807 ActiveRunway *FGApproachController::getRunway(string name)
1808 {
1809     ActiveRunwayVecIterator rwy = activeRunways.begin();
1810     if (activeRunways.size()) {
1811         while (rwy != activeRunways.end()) {
1812             if (rwy->getRunwayName() == name) {
1813                 break;
1814             }
1815             rwy++;
1816         }
1817     }
1818     if (rwy == activeRunways.end()) {
1819         ActiveRunway aRwy(name, 0);
1820         activeRunways.push_back(aRwy);
1821         rwy = activeRunways.end() - 1;
1822     }
1823     return &(*rwy);
1824 }
1825
1826 void FGApproachController::render(bool visible) {
1827     //cerr << "FGApproachController::render function not yet implemented" << endl;
1828 }
1829
1830
1831
1832 string FGApproachController::getName() {
1833     return string(parent->getId() + "-approach");
1834 }