]> git.mxchange.org Git - flightgear.git/blob - src/ATC/trafficcontrol.cxx
Merge branch 'durk/traffic'
[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 "trafficcontrol.hxx"
28 #include <AIModel/AIAircraft.hxx>
29 #include <AIModel/AIFlightPlan.hxx>
30 #include <Traffic/TrafficMgr.hxx>
31 #include <Airports/groundnetwork.hxx>
32 #include <Airports/dynamics.hxx>
33
34 /***************************************************************************
35  * FGTrafficRecord
36  **************************************************************************/
37 FGTrafficRecord::FGTrafficRecord() :
38   id(0), waitsForId(0),
39   currentPos(0),
40   leg(0),
41   state(0),
42   latitude(0),
43   longitude(0), 
44    heading(0), 
45    speed(0), 
46    altitude(0), 
47    radius(0),
48    frequencyId(0),
49    allowTransmission(true) {
50 }
51
52 void FGTrafficRecord::setPositionAndIntentions(int pos, FGAIFlightPlan *route)
53 {
54  
55    currentPos = pos;
56    if (intentions.size()) {
57      intVecIterator i = intentions.begin();
58      if ((*i) != pos) {
59        SG_LOG(SG_GENERAL, SG_ALERT, "Error in FGTrafficRecord::setPositionAndIntentions");
60        //cerr << "Pos : " << pos << " Curr " << *(intentions.begin())  << endl;
61        for (intVecIterator i = intentions.begin(); i != intentions.end() ; i++) {
62         //cerr << (*i) << " ";
63        }
64        //cerr << endl;
65      }
66      intentions.erase(i);
67    } else {
68      //int legNr, routeNr;
69      //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
70      int size = route->getNrOfWayPoints();
71      //cerr << "Setting pos" << pos << " ";
72      //cerr << "setting intentions ";
73      for (int i = 0; i < size; i++) {
74        int val = route->getRouteIndex(i);
75        //cerr << val<< " ";
76        if ((val) && (val != pos))
77         {
78           intentions.push_back(val); 
79           //cerr << "[set] ";
80         }
81      }
82      //cerr << endl;
83      //while (route->next(&legNr, &routeNr)) {
84      //intentions.push_back(routeNr);
85      //}
86      //route->rewind(currentPos);
87    }
88    //exit(1);
89 }
90
91 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord &other)
92 {
93    bool result = false;
94    //cerr << "Start check 1" << endl;
95    if (currentPos == other.currentPos) 
96      {
97        //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
98        result = true;
99      }
100   //  else if (other.intentions.size()) 
101  //     {
102  //       cerr << "Start check 2" << endl;
103  //       intVecIterator i = other.intentions.begin(); 
104  //       while (!((i == other.intentions.end()) || ((*i) == currentPos)))
105  //     i++;
106  //       if (i != other.intentions.end()) {
107  //     cerr << "Check Position and intentions: current matches other.intentions" << endl;
108  //     result = true;
109  //       }
110    else if (intentions.size()) {
111      //cerr << "Start check 3" << endl;
112      intVecIterator i = intentions.begin(); 
113      //while (!((i == intentions.end()) || ((*i) == other.currentPos)))
114      while (i != intentions.end()) {
115        if ((*i) == other.currentPos) {
116          break;
117        }
118        i++;
119      }
120      if (i != intentions.end()) {
121        //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
122        result = true;
123      }
124    }
125    //cerr << "Done !!" << endl;
126    return result;
127 }
128
129 void FGTrafficRecord::setPositionAndHeading(double lat, double lon, double hdg, 
130                                             double spd, double alt)
131 {
132   latitude = lat;
133   longitude = lon;
134   heading = hdg;
135   speed = spd;
136   altitude = alt;
137 }
138
139 int FGTrafficRecord::crosses(FGGroundNetwork *net, FGTrafficRecord &other)
140 {
141    if (checkPositionAndIntentions(other) || (other.checkPositionAndIntentions(*this)))
142      return -1;
143    intVecIterator i, j;
144    int currentTargetNode = 0, otherTargetNode = 0;
145    if (currentPos > 0)
146      currentTargetNode = net->findSegment(currentPos      )->getEnd()->getIndex(); // OKAY,... 
147    if (other.currentPos > 0)
148      otherTargetNode   = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,...
149    if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
150      return currentTargetNode;
151    if (intentions.size())
152      {
153        for (i = intentions.begin(); i != intentions.end(); i++)
154         {
155           if ((*i) > 0) {
156             if ((currentTargetNode == net->findSegment(*i)->getEnd()->getIndex()))
157               {
158                 //cerr << "Current crosses at " << currentTargetNode <<endl;
159                 return currentTargetNode;
160               }
161           }
162         }
163      }
164    if (other.intentions.size())
165      {
166        for (i = other.intentions.begin(); i != other.intentions.end(); i++)
167         {
168           if ((*i) > 0) {
169             if (otherTargetNode == net->findSegment(*i)->getEnd()->getIndex())
170               {
171                 //cerr << "Other crosses at " << currentTargetNode <<endl;
172                 return otherTargetNode;
173               }
174           }
175         }
176      }
177    if (intentions.size() && other.intentions.size())
178      {
179        for (i = intentions.begin(); i != intentions.end(); i++) 
180         {
181           for (j = other.intentions.begin(); j != other.intentions.end(); j++)
182             {
183               //cerr << "finding segment " << *i << " and " << *j << endl;
184               if (((*i) > 0) && ((*j) > 0)) {
185                 currentTargetNode = net->findSegment(*i)->getEnd()->getIndex();
186                 otherTargetNode   = net->findSegment(*j)->getEnd()->getIndex();
187                 if (currentTargetNode == otherTargetNode) 
188                   {
189                     //cerr << "Routes will cross at " << currentTargetNode << endl;
190                     return currentTargetNode;
191                   }
192               }
193             }
194         }
195      }
196   return -1;
197 }
198
199 bool FGTrafficRecord::onRoute(FGGroundNetwork *net, FGTrafficRecord &other)
200 {
201   int node = -1, othernode = -1;
202   if (currentPos >0)
203     node = net->findSegment(currentPos)->getEnd()->getIndex();
204   if (other.currentPos > 0)
205     othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
206   if ((node == othernode) && (node != -1))
207     return true;
208   if (other.intentions.size())
209     {
210       for (intVecIterator i = other.intentions.begin(); i != other.intentions.end(); i++)
211         {
212           if (*i > 0) 
213             {
214               othernode = net->findSegment(*i)->getEnd()->getIndex();
215               if ((node == othernode) && (node > -1))
216                 return true;
217             }
218         }
219     }
220   //if (other.currentPos > 0)
221   //  othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
222   //if (intentions.size())
223   //  {
224   //    for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
225   //    {
226   //      if (*i > 0) 
227   //        {
228   //          node = net->findSegment(*i)->getEnd()->getIndex();
229   //          if ((node == othernode) && (node > -1))
230   //            return true;
231   //        }
232   //    }
233   //  }
234   return false;
235 }
236
237
238 bool FGTrafficRecord::isOpposing (FGGroundNetwork *net, FGTrafficRecord &other, int node)
239 {
240    // Check if current segment is the reverse segment for the other aircraft
241    FGTaxiSegment *opp;
242    //cerr << "Current segment " << currentPos << endl;
243    if ((currentPos > 0) && (other.currentPos > 0))
244      {
245        opp = net->findSegment(currentPos)->opposite();
246        if (opp) {
247         if (opp->getIndex() == other.currentPos)
248           return true;
249        }
250       
251        for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
252         {
253          if ((opp = net->findSegment(other.currentPos)->opposite()))
254             {
255               if ((*i) > 0)
256                 if (opp->getIndex() == net->findSegment(*i)->getIndex())
257                   {
258                     if (net->findSegment(*i)->getStart()->getIndex() == node) {
259                       {
260                         //cerr << "Found the node " << node << endl;
261                         return true;
262                       }
263                     }
264                   }
265             }
266           if (other.intentions.size())
267             {
268               for (intVecIterator j = other.intentions.begin(); j != other.intentions.end(); j++)
269                 {
270                   // cerr << "Current segment 1 " << (*i) << endl;
271                   if ((*i) > 0) {
272                     if ((opp = net->findSegment(*i)->opposite()))
273                       {
274                         if (opp->getIndex() == 
275                             net->findSegment(*j)->getIndex())
276                           {
277                             //cerr << "Nodes " << net->findSegment(*i)->getIndex()
278                             //   << " and  " << net->findSegment(*j)->getIndex()
279                             //   << " are opposites " << endl;
280                             if (net->findSegment(*i)->getStart()->getIndex() == node) {
281                               {
282                                 //cerr << "Found the node " << node << endl;
283                                 return true;
284                               }
285                             }
286                           }
287                       }
288                   }
289                 }
290             }
291         }
292      }
293    return false;
294 }
295
296 void FGTrafficRecord::setSpeedAdjustment(double spd) 
297
298   instruction.setChangeSpeed(true); 
299   instruction.setSpeed(spd); 
300 }
301
302 void FGTrafficRecord::setHeadingAdjustment(double heading) 
303
304   instruction.setChangeHeading(true);
305   instruction.setHeading(heading); 
306 }
307
308 bool FGTrafficRecord::pushBackAllowed() {
309       double course, az2,dist;
310        SGGeod curr(SGGeod::fromDegM(getLongitude(),
311                                        getLatitude(), 
312                                        getAltitude()));
313
314       double userLatitude  = fgGetDouble("/position/latitude-deg");
315       double userLongitude = fgGetDouble("/position/longitude-deg");
316       SGGeod user(SGGeod::fromDeg(userLongitude,userLatitude));
317       SGGeodesy::inverse(curr, user, course, az2, dist);
318       //cerr << "Distance to user : " << dist << endl;
319       return (dist > 250);
320
321 }
322
323
324
325 /***************************************************************************
326  * FGATCInstruction
327  *
328  **************************************************************************/
329 FGATCInstruction::FGATCInstruction()
330 {
331   holdPattern    = false; 
332   holdPosition   = false;
333   changeSpeed    = false;
334   changeHeading  = false;
335   changeAltitude = false;
336   resolveCircularWait = false;
337
338   speed   = 0;
339   heading = 0;
340   alt     = 0;
341 }
342
343 bool FGATCInstruction::hasInstruction()
344 {
345   return (holdPattern || holdPosition || changeSpeed || changeHeading || changeAltitude || resolveCircularWait);
346 }
347
348 string FGATCController::getGateName(FGAIAircraft *ref) 
349 {
350     return ref->atGate();
351 }
352
353 void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir msgDir)
354 {
355     string sender, receiver;
356     int stationFreq = 0;
357     int taxiFreq    = 0;
358     int freqId      = 0;
359     string atisInformation;
360     string text;
361     string taxiFreqStr;
362     double heading = 0;
363     string activeRunway;
364     string fltType;
365     string rwyClass;
366     string SID;
367     string transponderCode;
368     FGAIFlightPlan *fp;
369     string fltRules;
370
371     //double commFreqD;
372     sender = rec->getAircraft()->getTrafficRef()->getCallSign();
373     switch (rec->getLeg()) {
374         case 2:
375         case 3:
376             freqId = rec->getNextFrequency();
377             stationFreq =
378             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(rec->getLeg()+freqId);
379             taxiFreq =
380             rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(3);
381             receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
382             atisInformation = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getAtisInformation();
383             break;
384         case 4: 
385             receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
386             break;
387     }
388     // Swap sender and receiver value in case of a ground to air transmission
389     if (msgDir == ATC_GROUND_TO_AIR) {
390        string tmp = sender;
391        sender = receiver;
392        receiver = tmp;
393     }
394     switch (msgId) {
395           case MSG_ANNOUNCE_ENGINE_START:
396                text = sender + ". Ready to Start up";
397                break;
398           case MSG_REQUEST_ENGINE_START:
399                text = receiver + ", This is " + sender + ". Position " +getGateName(rec->getAircraft()) +
400                        ". Information " + atisInformation + ". " +
401                        rec->getAircraft()->getTrafficRef()->getFlightRules() + " to " + 
402                        rec->getAircraft()->getTrafficRef()->getArrivalAirport()->getName() + ". Request start-up";
403                break;
404           // Acknowledge engine startup permission
405           // Assign departure runway
406           // Assign SID, if necessery (TODO)
407           case MSG_PERMIT_ENGINE_START:
408                taxiFreqStr = formatATCFrequency3_2(taxiFreq);
409
410                heading = rec->getAircraft()->getTrafficRef()->getCourse();
411                fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
412                rwyClass= rec->getAircraft()->GetFlightPlan()->getRunwayClassFromTrafficType(fltType);
413
414                rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, heading);
415                rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
416                fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getSID(activeRunway, heading);
417                rec->getAircraft()->GetFlightPlan()->setSID(fp);
418                if (fp) {
419                    SID = fp->getName() + " departure";
420                } else {
421                    SID = "fly runway heading ";
422                }
423                //snprintf(buffer, 7, "%3.2f", heading);
424                fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
425                transponderCode = genTransponderCode(fltRules);
426                rec->getAircraft()->SetTransponderCode(transponderCode);
427                text = receiver + ". Start-up approved. " + atisInformation + " correct, runway " + activeRunway
428                        + ", " + SID + ", squawk " + transponderCode + ". " +
429                       "For push-back and taxi clearance call " + taxiFreqStr + ". " + sender + " control.";
430                break;
431           case MSG_DENY_ENGINE_START:
432                text = receiver + ". Standby";
433                break;
434          case MSG_ACKNOWLEDGE_ENGINE_START:
435                fp = rec->getAircraft()->GetFlightPlan()->getSID();
436                if (fp) {
437                   SID = rec->getAircraft()->GetFlightPlan()->getSID()->getName() + " departure";
438                } else {
439                   SID = "fly runway heading ";
440                }
441                taxiFreqStr = formatATCFrequency3_2(taxiFreq);
442                activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
443                transponderCode = rec->getAircraft()->GetTransponderCode();
444                text = receiver + ". Start-up approved. " + atisInformation + " correct, runway " +
445                       activeRunway + ", " + SID + ", squawk " + transponderCode + ". " +
446                       "For push-back and taxi clearance call " + taxiFreqStr + ". " + sender;
447                break;
448            case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
449                taxiFreqStr = formatATCFrequency3_2(taxiFreq);
450                text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
451                break;
452            case MSG_REQUEST_PUSHBACK_CLEARANCE:
453                text = receiver + ". Request push-back. " + sender;
454                break;
455            case MSG_PERMIT_PUSHBACK_CLEARANCE:
456                text = receiver + ". Push-back approved. " + sender;
457                break;
458            case MSG_HOLD_PUSHBACK_CLEARANCE:
459                 text = receiver + ". Standby. " + sender;
460                  break;
461             default:
462                  text = sender + ". Transmitting unknown Message";
463                   break;
464     }
465     double onBoardRadioFreq0 = fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
466     double onBoardRadioFreq1 = fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
467     int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
468     int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
469     //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << endl;
470
471     // Display ATC message only when one of the radios is tuned
472     // the relevant frequency.
473     // Note that distance attenuation is currently not yet implemented
474     if ((onBoardRadioFreqI0 == stationFreq) || (onBoardRadioFreqI1 == stationFreq)) {
475         if (rec->allowTransmissions()) {
476             fgSetString("/sim/messages/atc", text.c_str());
477         }
478     }
479 }
480
481 string FGATCController::formatATCFrequency3_2(int freq) {
482     char buffer[7];
483     snprintf(buffer, 7, "%3.2f", ( (float) freq / 100.0) );
484     return string(buffer);
485 }
486
487 // TODO: Set transponder codes according to real-world routes.
488 // The current version just returns a random string of four octal numbers. 
489 string FGATCController::genTransponderCode(string fltRules) {
490     if (fltRules == "VFR") {
491         return string("1200");
492     } else {
493         char buffer[5];
494         snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8,rand() % 8, rand() % 8);
495         return string(buffer);
496     }
497 }
498
499 /***************************************************************************
500  * class FGTowerController
501  *
502  **************************************************************************/
503 FGTowerController::FGTowerController() :
504   FGATCController()
505 {
506 }
507
508 // 
509 void FGTowerController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
510                                          double lat, double lon, double heading, 
511                                          double speed, double alt, double radius, int leg,
512                                          FGAIAircraft *ref)
513 {
514   TrafficVectorIterator i = activeTraffic.begin();
515   // Search whether the current id alread has an entry
516   // This might be faster using a map instead of a vector, but let's start by taking a safe route
517   if (activeTraffic.size()) {
518     //while ((i->getId() != id) && i != activeTraffic.end()) {
519     while (i != activeTraffic.end()) {
520       if (i->getId() == id) {
521         break;
522       }
523       i++;
524     }
525   }
526   
527   // Add a new TrafficRecord if no one exsists for this aircraft.
528   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
529     FGTrafficRecord rec;
530     rec.setId(id);
531
532     rec.setPositionAndHeading(lat, lon, heading, speed, alt);
533     rec.setRunway(intendedRoute->getRunway());
534     rec.setLeg(leg);
535     //rec.setCallSign(callsign);
536     rec.setAircraft(ref);
537     activeTraffic.push_back(rec);
538   } else {
539     i->setPositionAndHeading(lat, lon, heading, speed, alt);
540   }
541 }
542
543 void FGTowerController::update(int id, double lat, double lon, double heading, double speed, double alt, 
544                              double dt)
545 {
546     TrafficVectorIterator i = activeTraffic.begin();
547     // Search search if the current id has an entry
548     // This might be faster using a map instead of a vector, but let's start by taking a safe route
549     TrafficVectorIterator current, closest;
550     if (activeTraffic.size()) {
551       //while ((i->getId() != id) && i != activeTraffic.end()) {
552       while (i != activeTraffic.end()) {
553         if (i->getId() == id) {
554           break;
555         }
556         i++;
557       }
558     }
559
560 //    // update position of the current aircraft
561     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
562       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
563     } else {
564       i->setPositionAndHeading(lat, lon, heading, speed, alt);
565       current = i;
566     }
567     setDt(getDt() + dt);
568
569 //    // see if we already have a clearance record for the currently active runway
570     ActiveRunwayVecIterator rwy = activeRunways.begin();
571     // again, a map might be more efficient here
572     if (activeRunways.size()) {
573       //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
574       while (rwy != activeRunways.end()) {
575         if (rwy->getRunwayName() == current->getRunway()) {
576           break;
577         }
578         rwy++;
579       }
580     }
581     if (rwy == activeRunways.end()) {
582       ActiveRunway aRwy(current->getRunway(), id);
583       activeRunways.push_back(aRwy);   // Since there are no clearance records for this runway yet
584       current->setHoldPosition(false); // Clear the current aircraft to continue
585     }
586     else {
587       // Okay, we have a clearance record for this runway, so check
588       // whether the clearence ID matches that of the current aircraft
589       if (id == rwy->getCleared()) {
590         current->setHoldPosition(false);
591       } else {
592         current->setHoldPosition(true);
593       }
594     }
595 }
596
597
598 void FGTowerController::signOff(int id) 
599 {
600   TrafficVectorIterator i = activeTraffic.begin();
601   // Search search if the current id alread has an entry
602   // This might be faster using a map instead of a vector, but let's start by taking a safe route
603     if (activeTraffic.size()) {
604       //while ((i->getId() != id) && i != activeTraffic.end()) {
605       while (i != activeTraffic.end()) {
606         if (i->getId() == id) {
607           break;
608         }
609         i++;
610       }
611     }
612     // If this aircraft has left the runway, we can clear the departure record for this runway
613     ActiveRunwayVecIterator rwy = activeRunways.begin();
614     if (activeRunways.size()) {
615       //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
616       while (rwy != activeRunways.end()) {
617         if (rwy->getRunwayName() == i->getRunway()) {
618           break;
619         }
620         rwy++;
621       }
622       if (rwy != activeRunways.end()) {
623         rwy = activeRunways.erase(rwy);
624       } else {
625         SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
626       }
627     }
628     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
629       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower");
630     } else {
631       i = activeTraffic.erase(i);
632     }
633 }
634
635 // NOTE:
636 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
637 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
638 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
639 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
640 // Note that this function is probably obsolete
641 bool FGTowerController::hasInstruction(int id)
642 {
643     TrafficVectorIterator i = activeTraffic.begin();
644     // Search search if the current id has an entry
645     // This might be faster using a map instead of a vector, but let's start by taking a safe route
646     if (activeTraffic.size()) 
647       {
648         //while ((i->getId() != id) && i != activeTraffic.end()) {
649         while (i != activeTraffic.end()) {
650           if (i->getId() == id) {
651             break;
652           }
653         i++;
654       }
655     }
656     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
657       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
658     } else {
659       return i->hasInstruction();
660     }
661   return false;
662 }
663
664
665 FGATCInstruction FGTowerController::getInstruction(int id)
666 {
667   TrafficVectorIterator i = activeTraffic.begin();
668   // Search search if the current id has an entry
669   // This might be faster using a map instead of a vector, but let's start by taking a safe route
670   if (activeTraffic.size()) {
671     //while ((i->getId() != id) && i != activeTraffic.end()) {
672     while (i != activeTraffic.end()) {
673       if (i->getId() == id) {
674         break;
675       }
676       i++;
677     }
678   }
679   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
680     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
681   } else {
682     return i->getInstruction();
683   }
684   return FGATCInstruction();
685 }
686
687 /***************************************************************************
688  * class FGStartupController
689  *
690  **************************************************************************/
691 FGStartupController::FGStartupController() :
692   FGATCController()
693 {
694     available        = false;
695     lastTransmission = 0;
696 }
697
698 void FGStartupController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
699                                          double lat, double lon, double heading, 
700                                          double speed, double alt, double radius, int leg,
701                                          FGAIAircraft *ref)
702 {
703   TrafficVectorIterator i = activeTraffic.begin();
704   // Search whether the current id alread has an entry
705   // This might be faster using a map instead of a vector, but let's start by taking a safe route
706   if (activeTraffic.size()) {
707     //while ((i->getId() != id) && i != activeTraffic.end()) {
708     while (i != activeTraffic.end()) {
709       if (i->getId() == id) {
710         break;
711       }
712       i++;
713     }
714   }
715   
716   // Add a new TrafficRecord if no one exsists for this aircraft.
717   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
718     FGTrafficRecord rec;
719     rec.setId(id);
720
721     rec.setPositionAndHeading(lat, lon, heading, speed, alt);
722     rec.setRunway(intendedRoute->getRunway());
723     rec.setLeg(leg);
724     //rec.setCallSign(callsign);
725     rec.setAircraft(ref);
726     rec.setHoldPosition(true);
727     activeTraffic.push_back(rec);
728     } else {
729         i->setPositionAndHeading(lat, lon, heading, speed, alt);
730         
731   }
732 }
733
734 // NOTE:
735 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
736 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
737 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
738 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
739 // Note that this function is probably obsolete
740 bool FGStartupController::hasInstruction(int id)
741 {
742     TrafficVectorIterator i = activeTraffic.begin();
743     // Search search if the current id has an entry
744     // This might be faster using a map instead of a vector, but let's start by taking a safe route
745     if (activeTraffic.size()) 
746       {
747         //while ((i->getId() != id) && i != activeTraffic.end()) {
748         while (i != activeTraffic.end()) {
749           if (i->getId() == id) {
750             break;
751           }
752         i++;
753       }
754     }
755     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
756       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
757     } else {
758       return i->hasInstruction();
759     }
760   return false;
761 }
762
763
764 FGATCInstruction FGStartupController::getInstruction(int id)
765 {
766   TrafficVectorIterator i = activeTraffic.begin();
767   // Search search if the current id has an entry
768   // This might be faster using a map instead of a vector, but let's start by taking a safe route
769   if (activeTraffic.size()) {
770     //while ((i->getId() != id) && i != activeTraffic.end()) {
771     while (i != activeTraffic.end()) {
772       if (i->getId() == id) {
773         break;
774       }
775       i++;
776     }
777   }
778   if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
779     SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
780   } else {
781     return i->getInstruction();
782   }
783   return FGATCInstruction();
784 }
785
786 void FGStartupController::signOff(int id) 
787 {
788   TrafficVectorIterator i = activeTraffic.begin();
789   // Search search if the current id alread has an entry
790   // This might be faster using a map instead of a vector, but let's start by taking a safe route
791     if (activeTraffic.size()) {
792       //while ((i->getId() != id) && i != activeTraffic.end()) {
793       while (i != activeTraffic.end()) {
794         if (i->getId() == id) {
795           break;
796         }
797         i++;
798       }
799     }
800     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
801       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower");
802     } else {
803       i = activeTraffic.erase(i);
804     }
805 }
806
807 void FGStartupController::update(int id, double lat, double lon, double heading, double speed, double alt, 
808                              double dt)
809 {
810     TrafficVectorIterator i = activeTraffic.begin();
811     // Search search if the current id has an entry
812     // This might be faster using a map instead of a vector, but let's start by taking a safe route
813     TrafficVectorIterator current, closest;
814     if (activeTraffic.size()) {
815       //while ((i->getId() != id) && i != activeTraffic.end()) {
816       while (i != activeTraffic.end()) {
817         if (i->getId() == id) {
818           break;
819         }
820         i++;
821       }
822     }
823
824 //    // update position of the current aircraft
825     if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
826       SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
827     } else {
828       i->setPositionAndHeading(lat, lon, heading, speed, alt);
829       current = i;
830     }
831     setDt(getDt() + dt);
832
833     int state = i->getState();
834     time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
835     time_t now       = time(NULL) + fgGetLong("/sim/time/warp");
836     //cerr << i->getAircraft()->getTrafficRef()->getCallSign() 
837     //     << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
838     //     << " at parking " << getGateName(i->getAircraft()) << endl;
839
840     if ((now - lastTransmission) > 3 + (rand() % 15)) {
841         available = true;
842     }
843
844     if ((state == 0) && available) {
845         if (now > startTime) {
846              //cerr << "Transmitting startup msg" << endl;
847              transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
848              i->updateState();
849              lastTransmission = now;
850              available = false;
851         }
852     }
853     if ((state == 1) && available) {
854         if (now > startTime+60) {
855             transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
856             i->updateState();
857             lastTransmission = now;
858              available = false;
859         }
860     }
861     if ((state == 2) && available) {
862         if (now > startTime+80) {
863             transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
864             i->updateState();
865             lastTransmission = now;
866              available = false;
867         }
868     }
869     if ((state == 3) && available){
870         if (now > startTime+100) {
871             transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND);
872             i->updateState();
873             lastTransmission = now;
874              available = false;
875         }
876      }
877      // Note: The next two stages are only necessesary when Startup control is
878      //  on a different frequency, compared to ground control
879      if ((state == 4) && available){
880         if (now > startTime+130) {
881             transmit(&(*i), MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, ATC_AIR_TO_GROUND);
882             i->updateState();
883             i->nextFrequency();
884             lastTransmission = now;
885              available = false;
886         }
887      }
888
889
890      // TODO: Switch to APRON control and request pushback Clearance.
891      // Get Push back clearance
892      if ((state == 5) && available){
893         if (now > startTime+160) {
894             transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND);
895             i->updateState();
896             lastTransmission = now;
897              available = false;
898         }
899      }
900      if ((state == 6) && available){
901         if (now > startTime+180) {
902             if (i->pushBackAllowed()) {
903                  i->allowRepeatedTransmissions();
904                  transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE, ATC_GROUND_TO_AIR);
905                  i->updateState();
906             } else {
907                  transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE, ATC_GROUND_TO_AIR);
908                  i->suppressRepeatedTransmissions();
909             }
910             lastTransmission = now;
911              available = false;
912         }
913      }
914      if ((state == 6) && available){
915           i->setHoldPosition(false);
916      }
917 }