1 // trafficrecord.cxx - Implementation of AIModels ATC code.
3 // Written by Durk Talsma, started September 2006.
5 // Copyright (C) 2006 Durk Talsma.
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.
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.
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.
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>
34 /***************************************************************************
36 **************************************************************************/
37 FGTrafficRecord::FGTrafficRecord() :
49 allowTransmission(true) {
52 void FGTrafficRecord::setPositionAndIntentions(int pos, FGAIFlightPlan *route)
56 if (intentions.size()) {
57 intVecIterator i = intentions.begin();
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) << " ";
68 //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
69 int size = route->getNrOfWayPoints();
70 //cerr << "Setting pos" << pos << " ";
71 //cerr << "setting intentions ";
72 for (int i = 0; i < size; i++) {
73 int val = route->getRouteIndex(i);
75 if ((val) && (val != pos))
77 intentions.push_back(val);
82 //while (route->next(&legNr, &routeNr)) {
83 //intentions.push_back(routeNr);
85 //route->rewind(currentPos);
90 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord &other)
93 //cerr << "Start check 1" << endl;
94 if (currentPos == other.currentPos)
96 //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
99 // else if (other.intentions.size())
101 // cerr << "Start check 2" << endl;
102 // intVecIterator i = other.intentions.begin();
103 // while (!((i == other.intentions.end()) || ((*i) == currentPos)))
105 // if (i != other.intentions.end()) {
106 // cerr << "Check Position and intentions: current matches other.intentions" << endl;
109 else if (intentions.size()) {
110 //cerr << "Start check 3" << endl;
111 intVecIterator i = intentions.begin();
112 //while (!((i == intentions.end()) || ((*i) == other.currentPos)))
113 while (i != intentions.end()) {
114 if ((*i) == other.currentPos) {
119 if (i != intentions.end()) {
120 //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
124 //cerr << "Done !!" << endl;
128 void FGTrafficRecord::setPositionAndHeading(double lat, double lon, double hdg,
129 double spd, double alt)
138 int FGTrafficRecord::crosses(FGGroundNetwork *net, FGTrafficRecord &other)
140 if (checkPositionAndIntentions(other) || (other.checkPositionAndIntentions(*this)))
143 int currentTargetNode = 0, otherTargetNode = 0;
145 currentTargetNode = net->findSegment(currentPos )->getEnd()->getIndex(); // OKAY,...
146 if (other.currentPos > 0)
147 otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,...
148 if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
149 return currentTargetNode;
150 if (intentions.size())
152 for (i = intentions.begin(); i != intentions.end(); i++)
155 if ((currentTargetNode == net->findSegment(*i)->getEnd()->getIndex()))
157 //cerr << "Current crosses at " << currentTargetNode <<endl;
158 return currentTargetNode;
163 if (other.intentions.size())
165 for (i = other.intentions.begin(); i != other.intentions.end(); i++)
168 if (otherTargetNode == net->findSegment(*i)->getEnd()->getIndex())
170 //cerr << "Other crosses at " << currentTargetNode <<endl;
171 return otherTargetNode;
176 if (intentions.size() && other.intentions.size())
178 for (i = intentions.begin(); i != intentions.end(); i++)
180 for (j = other.intentions.begin(); j != other.intentions.end(); j++)
182 //cerr << "finding segment " << *i << " and " << *j << endl;
183 if (((*i) > 0) && ((*j) > 0)) {
184 currentTargetNode = net->findSegment(*i)->getEnd()->getIndex();
185 otherTargetNode = net->findSegment(*j)->getEnd()->getIndex();
186 if (currentTargetNode == otherTargetNode)
188 //cerr << "Routes will cross at " << currentTargetNode << endl;
189 return currentTargetNode;
198 bool FGTrafficRecord::onRoute(FGGroundNetwork *net, FGTrafficRecord &other)
200 int node = -1, othernode = -1;
202 node = net->findSegment(currentPos)->getEnd()->getIndex();
203 if (other.currentPos > 0)
204 othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
205 if ((node == othernode) && (node != -1))
207 if (other.intentions.size())
209 for (intVecIterator i = other.intentions.begin(); i != other.intentions.end(); i++)
213 othernode = net->findSegment(*i)->getEnd()->getIndex();
214 if ((node == othernode) && (node > -1))
219 //if (other.currentPos > 0)
220 // othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
221 //if (intentions.size())
223 // for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
227 // node = net->findSegment(*i)->getEnd()->getIndex();
228 // if ((node == othernode) && (node > -1))
237 bool FGTrafficRecord::isOpposing (FGGroundNetwork *net, FGTrafficRecord &other, int node)
239 // Check if current segment is the reverse segment for the other aircraft
241 //cerr << "Current segment " << currentPos << endl;
242 if ((currentPos > 0) && (other.currentPos > 0))
244 opp = net->findSegment(currentPos)->opposite();
246 if (opp->getIndex() == other.currentPos)
250 for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
252 if ((opp = net->findSegment(other.currentPos)->opposite()))
255 if (opp->getIndex() == net->findSegment(*i)->getIndex())
257 if (net->findSegment(*i)->getStart()->getIndex() == node) {
259 //cerr << "Found the node " << node << endl;
265 if (other.intentions.size())
267 for (intVecIterator j = other.intentions.begin(); j != other.intentions.end(); j++)
269 // cerr << "Current segment 1 " << (*i) << endl;
271 if ((opp = net->findSegment(*i)->opposite()))
273 if (opp->getIndex() ==
274 net->findSegment(*j)->getIndex())
276 //cerr << "Nodes " << net->findSegment(*i)->getIndex()
277 // << " and " << net->findSegment(*j)->getIndex()
278 // << " are opposites " << endl;
279 if (net->findSegment(*i)->getStart()->getIndex() == node) {
281 //cerr << "Found the node " << node << endl;
295 void FGTrafficRecord::setSpeedAdjustment(double spd)
297 instruction.setChangeSpeed(true);
298 instruction.setSpeed(spd);
301 void FGTrafficRecord::setHeadingAdjustment(double heading)
303 instruction.setChangeHeading(true);
304 instruction.setHeading(heading);
307 bool FGTrafficRecord::pushBackAllowed() {
308 double course, az2,dist;
309 SGGeod curr(SGGeod::fromDegM(getLongitude(),
313 double userLatitude = fgGetDouble("/position/latitude-deg");
314 double userLongitude = fgGetDouble("/position/longitude-deg");
315 SGGeod user(SGGeod::fromDeg(userLongitude,userLatitude));
316 SGGeodesy::inverse(curr, user, course, az2, dist);
317 //cerr << "Distance to user : " << dist << endl;
324 /***************************************************************************
327 **************************************************************************/
328 FGATCInstruction::FGATCInstruction()
331 holdPosition = false;
333 changeHeading = false;
334 changeAltitude = false;
335 resolveCircularWait = false;
343 bool FGATCInstruction::hasInstruction()
345 return (holdPattern || holdPosition || changeSpeed || changeHeading || changeAltitude || resolveCircularWait);
348 /***************************************************************************
351 **************************************************************************/
356 FGATCController::FGATCController()
360 lastTransmission = 0;
363 string FGATCController::getGateName(FGAIAircraft *ref)
365 return ref->atGate();
368 void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir msgDir)
370 string sender, receiver;
374 string atisInformation;
382 string transponderCode;
387 sender = rec->getAircraft()->getTrafficRef()->getCallSign();
388 //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
389 switch (rec->getLeg()) {
392 freqId = rec->getNextFrequency();
394 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(rec->getLeg()+freqId);
396 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(3);
397 receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
398 atisInformation = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getAtisInformation();
401 receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
404 // Swap sender and receiver value in case of a ground to air transmission
405 if (msgDir == ATC_GROUND_TO_AIR) {
411 case MSG_ANNOUNCE_ENGINE_START:
412 text = sender + ". Ready to Start up";
414 case MSG_REQUEST_ENGINE_START:
415 text = receiver + ", This is " + sender + ". Position " +getGateName(rec->getAircraft()) +
416 ". Information " + atisInformation + ". " +
417 rec->getAircraft()->getTrafficRef()->getFlightRules() + " to " +
418 rec->getAircraft()->getTrafficRef()->getArrivalAirport()->getName() + ". Request start-up";
420 // Acknowledge engine startup permission
421 // Assign departure runway
422 // Assign SID, if necessery (TODO)
423 case MSG_PERMIT_ENGINE_START:
424 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
426 heading = rec->getAircraft()->getTrafficRef()->getCourse();
427 fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
428 rwyClass= rec->getAircraft()->GetFlightPlan()->getRunwayClassFromTrafficType(fltType);
430 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, heading);
431 rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
432 fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getSID(activeRunway, heading);
433 rec->getAircraft()->GetFlightPlan()->setSID(fp);
435 SID = fp->getName() + " departure";
437 SID = "fly runway heading ";
439 //snprintf(buffer, 7, "%3.2f", heading);
440 fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
441 transponderCode = genTransponderCode(fltRules);
442 rec->getAircraft()->SetTransponderCode(transponderCode);
443 text = receiver + ". Start-up approved. " + atisInformation + " correct, runway " + activeRunway
444 + ", " + SID + ", squawk " + transponderCode + ". " +
445 "For push-back and taxi clearance call " + taxiFreqStr + ". " + sender + " control.";
447 case MSG_DENY_ENGINE_START:
448 text = receiver + ". Standby";
450 case MSG_ACKNOWLEDGE_ENGINE_START:
451 fp = rec->getAircraft()->GetFlightPlan()->getSID();
453 SID = rec->getAircraft()->GetFlightPlan()->getSID()->getName() + " departure";
455 SID = "fly runway heading ";
457 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
458 activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
459 transponderCode = rec->getAircraft()->GetTransponderCode();
460 text = receiver + ". Start-up approved. " + atisInformation + " correct, runway " +
461 activeRunway + ", " + SID + ", squawk " + transponderCode + ". " +
462 "For push-back and taxi clearance call " + taxiFreqStr + ". " + sender;
464 case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
465 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
466 text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
468 case MSG_INITIATE_CONTACT:
469 text = receiver + ". With you. " + sender;
471 case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
472 text = receiver + ". Roger. " + sender;
474 case MSG_REQUEST_PUSHBACK_CLEARANCE:
475 text = receiver + ". Request push-back. " + sender;
477 case MSG_PERMIT_PUSHBACK_CLEARANCE:
478 text = receiver + ". Push-back approved. " + sender;
480 case MSG_HOLD_PUSHBACK_CLEARANCE:
481 text = receiver + ". Standby. " + sender;
483 case MSG_REQUEST_TAXI_CLEARANCE:
484 text = receiver + ". Ready to Taxi. " + sender;
486 case MSG_ISSUE_TAXI_CLEARANCE:
487 text = receiver + ". Cleared to taxi. " + sender;
489 case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
490 text = receiver + ". Cleared to taxi. " + sender;
492 case MSG_HOLD_POSITION:
493 text = receiver + ". Hold Position. " + sender;
495 case MSG_ACKNOWLEDGE_HOLD_POSITION:
496 text = receiver + ". Holding Position. " + sender;
498 case MSG_RESUME_TAXI:
499 text = receiver + ". Resume Taxiing. " + sender;
501 case MSG_ACKNOWLEDGE_RESUME_TAXI:
502 text = receiver + ". Continuing Taxi. " + sender;
505 text = text + sender + ". Transmitting unknown Message";
508 double onBoardRadioFreq0 = fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
509 double onBoardRadioFreq1 = fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
510 int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
511 int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
512 //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << endl;
514 // Display ATC message only when one of the radios is tuned
515 // the relevant frequency.
516 // Note that distance attenuation is currently not yet implemented
517 if ((onBoardRadioFreqI0 == stationFreq) || (onBoardRadioFreqI1 == stationFreq)) {
518 if (rec->allowTransmissions()) {
519 fgSetString("/sim/messages/atc", text.c_str());
524 string FGATCController::formatATCFrequency3_2(int freq) {
526 snprintf(buffer, 7, "%3.2f", ( (float) freq / 100.0) );
527 return string(buffer);
530 // TODO: Set transponder codes according to real-world routes.
531 // The current version just returns a random string of four octal numbers.
532 string FGATCController::genTransponderCode(string fltRules) {
533 if (fltRules == "VFR") {
534 return string("1200");
537 snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8,rand() % 8, rand() % 8);
538 return string(buffer);
542 /***************************************************************************
543 * class FGTowerController
545 **************************************************************************/
546 FGTowerController::FGTowerController() :
552 void FGTowerController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
553 double lat, double lon, double heading,
554 double speed, double alt, double radius, int leg,
557 TrafficVectorIterator i = activeTraffic.begin();
558 // Search whether the current id alread has an entry
559 // This might be faster using a map instead of a vector, but let's start by taking a safe route
560 if (activeTraffic.size()) {
561 //while ((i->getId() != id) && i != activeTraffic.end()) {
562 while (i != activeTraffic.end()) {
563 if (i->getId() == id) {
570 // Add a new TrafficRecord if no one exsists for this aircraft.
571 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
575 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
576 rec.setRunway(intendedRoute->getRunway());
578 //rec.setCallSign(callsign);
579 rec.setAircraft(ref);
580 activeTraffic.push_back(rec);
582 i->setPositionAndHeading(lat, lon, heading, speed, alt);
586 void FGTowerController::update(int id, double lat, double lon, double heading, double speed, double alt,
589 TrafficVectorIterator i = activeTraffic.begin();
590 // Search search if the current id has an entry
591 // This might be faster using a map instead of a vector, but let's start by taking a safe route
592 TrafficVectorIterator current, closest;
593 if (activeTraffic.size()) {
594 //while ((i->getId() != id) && i != activeTraffic.end()) {
595 while (i != activeTraffic.end()) {
596 if (i->getId() == id) {
603 // // update position of the current aircraft
604 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
605 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
607 i->setPositionAndHeading(lat, lon, heading, speed, alt);
612 // // see if we already have a clearance record for the currently active runway
613 ActiveRunwayVecIterator rwy = activeRunways.begin();
614 // again, a map might be more efficient here
615 if (activeRunways.size()) {
616 //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
617 while (rwy != activeRunways.end()) {
618 if (rwy->getRunwayName() == current->getRunway()) {
624 if (rwy == activeRunways.end()) {
625 ActiveRunway aRwy(current->getRunway(), id);
626 activeRunways.push_back(aRwy); // Since there are no clearance records for this runway yet
627 current->setHoldPosition(false); // Clear the current aircraft to continue
630 // Okay, we have a clearance record for this runway, so check
631 // whether the clearence ID matches that of the current aircraft
632 if (id == rwy->getCleared()) {
633 current->setHoldPosition(false);
635 current->setHoldPosition(true);
641 void FGTowerController::signOff(int id)
643 TrafficVectorIterator i = activeTraffic.begin();
644 // Search search if the current id alread 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 //while ((i->getId() != id) && i != activeTraffic.end()) {
648 while (i != activeTraffic.end()) {
649 if (i->getId() == id) {
655 // If this aircraft has left the runway, we can clear the departure record for this runway
656 ActiveRunwayVecIterator rwy = activeRunways.begin();
657 if (activeRunways.size()) {
658 //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
659 while (rwy != activeRunways.end()) {
660 if (rwy->getRunwayName() == i->getRunway()) {
665 if (rwy != activeRunways.end()) {
666 rwy = activeRunways.erase(rwy);
668 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
671 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
672 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower");
674 i = activeTraffic.erase(i);
679 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
680 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
681 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
682 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
683 // Note that this function is probably obsolete
684 bool FGTowerController::hasInstruction(int id)
686 TrafficVectorIterator i = activeTraffic.begin();
687 // Search search if the current id has an entry
688 // This might be faster using a map instead of a vector, but let's start by taking a safe route
689 if (activeTraffic.size())
691 //while ((i->getId() != id) && i != activeTraffic.end()) {
692 while (i != activeTraffic.end()) {
693 if (i->getId() == id) {
699 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
700 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
702 return i->hasInstruction();
708 FGATCInstruction FGTowerController::getInstruction(int id)
710 TrafficVectorIterator i = activeTraffic.begin();
711 // Search search if the current id has an entry
712 // This might be faster using a map instead of a vector, but let's start by taking a safe route
713 if (activeTraffic.size()) {
714 //while ((i->getId() != id) && i != activeTraffic.end()) {
715 while (i != activeTraffic.end()) {
716 if (i->getId() == id) {
722 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
723 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
725 return i->getInstruction();
727 return FGATCInstruction();
730 /***************************************************************************
731 * class FGStartupController
733 **************************************************************************/
734 FGStartupController::FGStartupController() :
739 void FGStartupController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
740 double lat, double lon, double heading,
741 double speed, double alt, double radius, int leg,
744 TrafficVectorIterator i = activeTraffic.begin();
745 // Search whether the current id alread has an entry
746 // This might be faster using a map instead of a vector, but let's start by taking a safe route
747 if (activeTraffic.size()) {
748 //while ((i->getId() != id) && i != activeTraffic.end()) {
749 while (i != activeTraffic.end()) {
750 if (i->getId() == id) {
757 // Add a new TrafficRecord if no one exsists for this aircraft.
758 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
762 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
763 rec.setRunway(intendedRoute->getRunway());
765 //rec.setCallSign(callsign);
766 rec.setAircraft(ref);
767 rec.setHoldPosition(true);
768 activeTraffic.push_back(rec);
770 i->setPositionAndHeading(lat, lon, heading, speed, alt);
776 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
777 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
778 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
779 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
780 // Note that this function is probably obsolete
781 bool FGStartupController::hasInstruction(int id)
783 TrafficVectorIterator i = activeTraffic.begin();
784 // Search search if the current id has an entry
785 // This might be faster using a map instead of a vector, but let's start by taking a safe route
786 if (activeTraffic.size())
788 //while ((i->getId() != id) && i != activeTraffic.end()) {
789 while (i != activeTraffic.end()) {
790 if (i->getId() == id) {
796 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
797 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
799 return i->hasInstruction();
805 FGATCInstruction FGStartupController::getInstruction(int id)
807 TrafficVectorIterator i = activeTraffic.begin();
808 // Search search if the current id has an entry
809 // This might be faster using a map instead of a vector, but let's start by taking a safe route
810 if (activeTraffic.size()) {
811 //while ((i->getId() != id) && i != activeTraffic.end()) {
812 while (i != activeTraffic.end()) {
813 if (i->getId() == id) {
819 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
820 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
822 return i->getInstruction();
824 return FGATCInstruction();
827 void FGStartupController::signOff(int id)
829 TrafficVectorIterator i = activeTraffic.begin();
830 // Search search if the current id alread has an entry
831 // This might be faster using a map instead of a vector, but let's start by taking a safe route
832 if (activeTraffic.size()) {
833 //while ((i->getId() != id) && i != activeTraffic.end()) {
834 while (i != activeTraffic.end()) {
835 if (i->getId() == id) {
841 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
842 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower");
844 i = activeTraffic.erase(i);
848 void FGStartupController::update(int id, double lat, double lon, double heading, double speed, double alt,
851 TrafficVectorIterator i = activeTraffic.begin();
852 // Search search if the current id has an entry
853 // This might be faster using a map instead of a vector, but let's start by taking a safe route
854 TrafficVectorIterator current, closest;
855 if (activeTraffic.size()) {
856 //while ((i->getId() != id) && i != activeTraffic.end()) {
857 while (i != activeTraffic.end()) {
858 if (i->getId() == id) {
865 // // update position of the current aircraft
866 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
867 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
869 i->setPositionAndHeading(lat, lon, heading, speed, alt);
874 int state = i->getState();
875 time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
876 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
877 //cerr << i->getAircraft()->getTrafficRef()->getCallSign()
878 // << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
879 // << " at parking " << getGateName(i->getAircraft()) << endl;
881 if ((now - lastTransmission) > 3 + (rand() % 15)) {
885 if ((state == 0) && available) {
886 if (now > startTime) {
887 //cerr << "Transmitting startup msg" << endl;
888 transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
890 lastTransmission = now;
894 if ((state == 1) && available) {
895 if (now > startTime+60) {
896 transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
898 lastTransmission = now;
902 if ((state == 2) && available) {
903 if (now > startTime+80) {
904 transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
906 lastTransmission = now;
910 if ((state == 3) && available){
911 if (now > startTime+100) {
912 transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND);
914 lastTransmission = now;
918 // Note: The next four stages are only necessesary when Startup control is
919 // on a different frequency, compared to ground control
920 if ((state == 4) && available){
921 if (now > startTime+130) {
922 transmit(&(*i), MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, ATC_AIR_TO_GROUND);
925 lastTransmission = now;
929 if ((state == 5) && available){
930 if (now > startTime+140) {
931 transmit(&(*i), MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND);
933 lastTransmission = now;
937 if ((state == 6) && available){
938 if (now > startTime+150) {
939 transmit(&(*i), MSG_ACKNOWLEDGE_INITIATE_CONTACT, ATC_GROUND_TO_AIR);
941 lastTransmission = now;
947 // TODO: Switch to APRON control and request pushback Clearance.
948 // Get Push back clearance
949 if ((state == 7) && available){
950 if (now > startTime+180) {
951 transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND);
953 lastTransmission = now;
957 if ((state == 8) && available){
958 if (now > startTime+200) {
959 if (i->pushBackAllowed()) {
960 i->allowRepeatedTransmissions();
961 transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE, ATC_GROUND_TO_AIR);
964 transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE, ATC_GROUND_TO_AIR);
965 i->suppressRepeatedTransmissions();
967 lastTransmission = now;
971 if ((state == 9) && available){
972 i->setHoldPosition(false);