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.
29 #include "trafficcontrol.hxx"
30 #include "atc_mgr.hxx"
31 #include <AIModel/AIAircraft.hxx>
32 #include <AIModel/AIFlightPlan.hxx>
33 #include <AIModel/performancedata.hxx>
34 #include <AIModel/performancedb.hxx>
35 #include <Traffic/TrafficMgr.hxx>
36 #include <Airports/groundnetwork.hxx>
37 #include <Airports/dynamics.hxx>
38 #include <Airports/simple.hxx>
42 /***************************************************************************
44 **************************************************************************/
45 time_t ActiveRunway::requestTimeSlot(time_t eta)
48 time_t separation = 90;
50 if (estimatedArrivalTimes.size() == 0) {
51 estimatedArrivalTimes.push_back(eta);
54 TimeVectorIterator i = estimatedArrivalTimes.begin();
55 //cerr << "Checking eta slots " << eta << ": " << endl;
56 for (i = estimatedArrivalTimes.begin();
57 i != estimatedArrivalTimes.end(); i++) {
58 //cerr << "Stored time : " << (*i) << endl;
60 i = estimatedArrivalTimes.begin();
61 if ((eta + separation) < (*i)) {
64 //cerr << "Storing at beginning" << endl;
66 while ((i != estimatedArrivalTimes.end()) && (!found)) {
67 TimeVectorIterator j = i + 1;
68 if (j == estimatedArrivalTimes.end()) {
69 if (((*i) + separation) < eta) {
70 //cerr << "Storing at end" << endl;
73 newEta = (*i) + separation;
74 //cerr << "Storing at end + separation" << endl;
77 if ((((*j) - (*i)) > (separation * 2))) { // found a potential slot
78 // now check whether this slow is usable:
79 // 1) eta should fall between the two points
80 // i.e. eta > i AND eta < j
82 //cerr << "Found potential slot after " << (*i) << endl;
83 if (eta > (*i) && (eta < (*j))) {
85 if (eta < ((*i) + separation)) {
86 newEta = (*i) + separation;
87 //cerr << "Using original" << (*i) << " + separation " << endl;
90 //cerr << "Using original after " << (*i) << endl;
92 } else if (eta < (*i)) {
94 newEta = (*i) + separation;
95 //cerr << "Using delayed slot after " << (*i) << endl;
98 if (((*j) - separation) < eta) {
100 if (((*i) + separation) < eta) {
102 cerr << "Using original after " << (*i) << endl;
104 newEta = (*i) + separation;
105 cerr << "Using " << (*i) << " + separation " << endl;
113 //cerr << ". done. New ETA : " << newEta << endl;
115 estimatedArrivalTimes.push_back(newEta);
116 sort(estimatedArrivalTimes.begin(), estimatedArrivalTimes.end());
117 // do some housekeeping : remove any timestamps that are past
118 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
119 TimeVectorIterator i = estimatedArrivalTimes.begin();
120 while (i != estimatedArrivalTimes.end()) {
122 //cerr << "Deleting timestamp " << (*i) << " (now = " << now << "). " << endl;
123 estimatedArrivalTimes.erase(i);
124 i = estimatedArrivalTimes.begin();
132 /***************************************************************************
134 **************************************************************************/
135 FGTrafficRecord::FGTrafficRecord():
136 id(0), waitsForId(0),
141 allowTransmission(true),
142 latitude(0), longitude(0), heading(0), speed(0), altitude(0), radius(0)
146 void FGTrafficRecord::setPositionAndIntentions(int pos,
147 FGAIFlightPlan * route)
151 if (intentions.size()) {
152 intVecIterator i = intentions.begin();
154 SG_LOG(SG_GENERAL, SG_ALERT,
155 "Error in FGTrafficRecord::setPositionAndIntentions");
156 //cerr << "Pos : " << pos << " Curr " << *(intentions.begin()) << endl;
157 for (intVecIterator i = intentions.begin();
158 i != intentions.end(); i++) {
159 //cerr << (*i) << " ";
165 //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
166 int size = route->getNrOfWayPoints();
167 //cerr << "Setting pos" << pos << " ";
168 //cerr << "setting intentions ";
169 for (int i = 0; i < size; i++) {
170 int val = route->getRouteIndex(i);
172 if ((val) && (val != pos)) {
173 intentions.push_back(val);
178 //while (route->next(&legNr, &routeNr)) {
179 //intentions.push_back(routeNr);
181 //route->rewind(currentPos);
186 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other)
189 //cerr << "Start check 1" << endl;
190 if (currentPos == other.currentPos) {
191 //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
194 // else if (other.intentions.size())
196 // cerr << "Start check 2" << endl;
197 // intVecIterator i = other.intentions.begin();
198 // while (!((i == other.intentions.end()) || ((*i) == currentPos)))
200 // if (i != other.intentions.end()) {
201 // cerr << "Check Position and intentions: current matches other.intentions" << endl;
204 else if (intentions.size()) {
205 //cerr << "Start check 3" << endl;
206 intVecIterator i = intentions.begin();
207 //while (!((i == intentions.end()) || ((*i) == other.currentPos)))
208 while (i != intentions.end()) {
209 if ((*i) == other.currentPos) {
214 if (i != intentions.end()) {
215 //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
219 //cerr << "Done !!" << endl;
223 void FGTrafficRecord::setPositionAndHeading(double lat, double lon,
224 double hdg, double spd,
234 int FGTrafficRecord::crosses(FGGroundNetwork * net,
235 FGTrafficRecord & other)
237 if (checkPositionAndIntentions(other)
238 || (other.checkPositionAndIntentions(*this)))
241 int currentTargetNode = 0, otherTargetNode = 0;
243 currentTargetNode = net->findSegment(currentPos)->getEnd()->getIndex(); // OKAY,...
244 if (other.currentPos > 0)
245 otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,...
246 if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
247 return currentTargetNode;
248 if (intentions.size()) {
249 for (i = intentions.begin(); i != intentions.end(); i++) {
251 if ((currentTargetNode ==
252 net->findSegment(*i)->getEnd()->getIndex())) {
253 //cerr << "Current crosses at " << currentTargetNode <<endl;
254 return currentTargetNode;
259 if (other.intentions.size()) {
260 for (i = other.intentions.begin(); i != other.intentions.end();
263 if (otherTargetNode ==
264 net->findSegment(*i)->getEnd()->getIndex()) {
265 //cerr << "Other crosses at " << currentTargetNode <<endl;
266 return otherTargetNode;
271 if (intentions.size() && other.intentions.size()) {
272 for (i = intentions.begin(); i != intentions.end(); i++) {
273 for (j = other.intentions.begin(); j != other.intentions.end();
275 //cerr << "finding segment " << *i << " and " << *j << endl;
276 if (((*i) > 0) && ((*j) > 0)) {
278 net->findSegment(*i)->getEnd()->getIndex();
280 net->findSegment(*j)->getEnd()->getIndex();
281 if (currentTargetNode == otherTargetNode) {
282 //cerr << "Routes will cross at " << currentTargetNode << endl;
283 return currentTargetNode;
292 bool FGTrafficRecord::onRoute(FGGroundNetwork * net,
293 FGTrafficRecord & other)
295 int node = -1, othernode = -1;
297 node = net->findSegment(currentPos)->getEnd()->getIndex();
298 if (other.currentPos > 0)
300 net->findSegment(other.currentPos)->getEnd()->getIndex();
301 if ((node == othernode) && (node != -1))
303 if (other.intentions.size()) {
304 for (intVecIterator i = other.intentions.begin();
305 i != other.intentions.end(); i++) {
307 othernode = net->findSegment(*i)->getEnd()->getIndex();
308 if ((node == othernode) && (node > -1))
313 //if (other.currentPos > 0)
314 // othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
315 //if (intentions.size())
317 // for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
321 // node = net->findSegment(*i)->getEnd()->getIndex();
322 // if ((node == othernode) && (node > -1))
331 bool FGTrafficRecord::isOpposing(FGGroundNetwork * net,
332 FGTrafficRecord & other, int node)
334 // Check if current segment is the reverse segment for the other aircraft
336 //cerr << "Current segment " << currentPos << endl;
337 if ((currentPos > 0) && (other.currentPos > 0)) {
338 opp = net->findSegment(currentPos)->opposite();
340 if (opp->getIndex() == other.currentPos)
344 for (intVecIterator i = intentions.begin(); i != intentions.end();
346 if ((opp = net->findSegment(other.currentPos)->opposite())) {
348 if (opp->getIndex() ==
349 net->findSegment(*i)->getIndex()) {
350 if (net->findSegment(*i)->getStart()->getIndex() ==
353 //cerr << "Found the node " << node << endl;
359 if (other.intentions.size()) {
360 for (intVecIterator j = other.intentions.begin();
361 j != other.intentions.end(); j++) {
362 // cerr << "Current segment 1 " << (*i) << endl;
364 if ((opp = net->findSegment(*i)->opposite())) {
365 if (opp->getIndex() ==
366 net->findSegment(*j)->getIndex()) {
367 //cerr << "Nodes " << net->findSegment(*i)->getIndex()
368 // << " and " << net->findSegment(*j)->getIndex()
369 // << " are opposites " << endl;
370 if (net->findSegment(*i)->getStart()->
371 getIndex() == node) {
373 //cerr << "Found the node " << node << endl;
387 void FGTrafficRecord::setSpeedAdjustment(double spd)
389 instruction.setChangeSpeed(true);
390 instruction.setSpeed(spd);
393 void FGTrafficRecord::setHeadingAdjustment(double heading)
395 instruction.setChangeHeading(true);
396 instruction.setHeading(heading);
399 bool FGTrafficRecord::pushBackAllowed()
401 double course, az2, dist;
402 SGGeod curr(SGGeod::fromDegM(getLongitude(),
403 getLatitude(), getAltitude()));
405 double userLatitude = fgGetDouble("/position/latitude-deg");
406 double userLongitude = fgGetDouble("/position/longitude-deg");
407 SGGeod user(SGGeod::fromDeg(userLongitude, userLatitude));
408 SGGeodesy::inverse(curr, user, course, az2, dist);
409 //cerr << "Distance to user : " << dist << endl;
416 /***************************************************************************
419 **************************************************************************/
420 FGATCInstruction::FGATCInstruction()
423 holdPosition = false;
425 changeHeading = false;
426 changeAltitude = false;
427 resolveCircularWait = false;
435 bool FGATCInstruction::hasInstruction()
437 return (holdPattern || holdPosition || changeSpeed || changeHeading
438 || changeAltitude || resolveCircularWait);
441 /***************************************************************************
444 **************************************************************************/
449 FGATCController::FGATCController()
451 cerr << "running FGATController constructor" << endl;
454 lastTransmission = 0;
458 FGATCController::~FGATCController()
460 cerr << "running FGATController destructor" << endl;
463 string FGATCController::getGateName(FGAIAircraft * ref)
465 return ref->atGate();
468 bool FGATCController::isUserAircraft(FGAIAircraft* ac)
470 return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false;
473 void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId,
476 string sender, receiver;
480 string atisInformation;
488 string transponderCode;
493 sender = rec->getAircraft()->getTrafficRef()->getCallSign();
494 //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
495 switch (rec->getLeg()) {
498 freqId = rec->getNextFrequency();
500 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
501 getDynamics()->getGroundFrequency(rec->getLeg() + freqId);
503 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
504 getDynamics()->getGroundFrequency(3);
506 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
507 getName() + "-Ground";
509 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
510 getDynamics()->getAtisInformation();
514 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
515 getName() + "-Tower";
518 // Swap sender and receiver value in case of a ground to air transmission
519 if (msgDir == ATC_GROUND_TO_AIR) {
525 case MSG_ANNOUNCE_ENGINE_START:
526 text = sender + ". Ready to Start up";
528 case MSG_REQUEST_ENGINE_START:
530 receiver + ", This is " + sender + ". Position " +
531 getGateName(rec->getAircraft()) + ". Information " +
532 atisInformation + ". " +
533 rec->getAircraft()->getTrafficRef()->getFlightRules() +
535 rec->getAircraft()->getTrafficRef()->getArrivalAirport()->
536 getName() + ". Request start-up";
538 // Acknowledge engine startup permission
539 // Assign departure runway
540 // Assign SID, if necessery (TODO)
541 case MSG_PERMIT_ENGINE_START:
542 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
544 heading = rec->getAircraft()->getTrafficRef()->getCourse();
545 fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
547 rec->getAircraft()->GetFlightPlan()->
548 getRunwayClassFromTrafficType(fltType);
550 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
551 getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
553 rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
554 fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
555 getDynamics()->getSID(activeRunway, heading);
556 rec->getAircraft()->GetFlightPlan()->setSID(fp);
558 SID = fp->getName() + " departure";
560 SID = "fly runway heading ";
562 //snprintf(buffer, 7, "%3.2f", heading);
563 fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
564 transponderCode = genTransponderCode(fltRules);
565 rec->getAircraft()->SetTransponderCode(transponderCode);
567 receiver + ". Start-up approved. " + atisInformation +
568 " correct, runway " + activeRunway + ", " + SID + ", squawk " +
569 transponderCode + ". " +
570 "For push-back and taxi clearance call " + taxiFreqStr + ". " +
571 sender + " control.";
573 case MSG_DENY_ENGINE_START:
574 text = receiver + ". Standby";
576 case MSG_ACKNOWLEDGE_ENGINE_START:
577 fp = rec->getAircraft()->GetFlightPlan()->getSID();
580 rec->getAircraft()->GetFlightPlan()->getSID()->getName() +
583 SID = "fly runway heading ";
585 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
586 activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
587 transponderCode = rec->getAircraft()->GetTransponderCode();
589 receiver + ". Start-up approved. " + atisInformation +
590 " correct, runway " + activeRunway + ", " + SID + ", squawk " +
591 transponderCode + ". " +
592 "For push-back and taxi clearance call " + taxiFreqStr + ". " +
595 case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
596 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
597 text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
599 case MSG_INITIATE_CONTACT:
600 text = receiver + ". With you. " + sender;
602 case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
603 text = receiver + ". Roger. " + sender;
605 case MSG_REQUEST_PUSHBACK_CLEARANCE:
606 text = receiver + ". Request push-back. " + sender;
608 case MSG_PERMIT_PUSHBACK_CLEARANCE:
609 text = receiver + ". Push-back approved. " + sender;
611 case MSG_HOLD_PUSHBACK_CLEARANCE:
612 text = receiver + ". Standby. " + sender;
614 case MSG_REQUEST_TAXI_CLEARANCE:
615 text = receiver + ". Ready to Taxi. " + sender;
617 case MSG_ISSUE_TAXI_CLEARANCE:
618 text = receiver + ". Cleared to taxi. " + sender;
620 case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
621 text = receiver + ". Cleared to taxi. " + sender;
623 case MSG_HOLD_POSITION:
624 text = receiver + ". Hold Position. " + sender;
626 case MSG_ACKNOWLEDGE_HOLD_POSITION:
627 text = receiver + ". Holding Position. " + sender;
629 case MSG_RESUME_TAXI:
630 text = receiver + ". Resume Taxiing. " + sender;
632 case MSG_ACKNOWLEDGE_RESUME_TAXI:
633 text = receiver + ". Continuing Taxi. " + sender;
636 text = text + sender + ". Transmitting unknown Message";
639 double onBoardRadioFreq0 =
640 fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
641 double onBoardRadioFreq1 =
642 fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
643 int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
644 int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
645 //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl;
647 // Display ATC message only when one of the radios is tuned
648 // the relevant frequency.
649 // Note that distance attenuation is currently not yet implemented
650 if ((onBoardRadioFreqI0 == stationFreq)
651 || (onBoardRadioFreqI1 == stationFreq)) {
652 if (rec->allowTransmissions()) {
653 fgSetString("/sim/messages/atc", text.c_str());
658 string FGATCController::formatATCFrequency3_2(int freq)
661 snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
662 return string(buffer);
665 // TODO: Set transponder codes according to real-world routes.
666 // The current version just returns a random string of four octal numbers.
667 string FGATCController::genTransponderCode(string fltRules)
669 if (fltRules == "VFR") {
670 return string("1200");
673 snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
675 return string(buffer);
679 void FGATCController::init()
682 FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
683 mgr->addController(this);
688 /***************************************************************************
689 * class FGTowerController
691 **************************************************************************/
692 FGTowerController::FGTowerController():
698 void FGTowerController::announcePosition(int id,
699 FGAIFlightPlan * intendedRoute,
700 int currentPosition, double lat,
701 double lon, double heading,
702 double speed, double alt,
703 double radius, int leg,
707 TrafficVectorIterator i = activeTraffic.begin();
708 // Search whether the current id alread has an entry
709 // This might be faster using a map instead of a vector, but let's start by taking a safe route
710 if (activeTraffic.size()) {
711 //while ((i->getId() != id) && i != activeTraffic.end()) {
712 while (i != activeTraffic.end()) {
713 if (i->getId() == id) {
719 // Add a new TrafficRecord if no one exsists for this aircraft.
720 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
724 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
725 rec.setRunway(intendedRoute->getRunway());
727 //rec.setCallSign(callsign);
728 rec.setAircraft(ref);
729 activeTraffic.push_back(rec);
731 i->setPositionAndHeading(lat, lon, heading, speed, alt);
735 void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
736 double heading, double speed, double alt,
739 TrafficVectorIterator i = activeTraffic.begin();
740 // Search whether the current id has an entry
741 // This might be faster using a map instead of a vector, but let's start by taking a safe route
742 TrafficVectorIterator current, closest;
743 if (activeTraffic.size()) {
744 //while ((i->getId() != id) && i != activeTraffic.end()) {
745 while (i != activeTraffic.end()) {
746 if (i->getId() == id) {
752 // // update position of the current aircraft
753 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
754 SG_LOG(SG_GENERAL, SG_ALERT,
755 "AI error: updating aircraft without traffic record");
757 i->setPositionAndHeading(lat, lon, heading, speed, alt);
762 // // see if we already have a clearance record for the currently active runway
763 ActiveRunwayVecIterator rwy = activeRunways.begin();
764 // again, a map might be more efficient here
765 if (activeRunways.size()) {
766 //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
767 while (rwy != activeRunways.end()) {
768 if (rwy->getRunwayName() == current->getRunway()) {
774 if (rwy == activeRunways.end()) {
775 ActiveRunway aRwy(current->getRunway(), id);
776 activeRunways.push_back(aRwy); // Since there are no clearance records for this runway yet
777 current->setHoldPosition(false); // Clear the current aircraft to continue
779 // Okay, we have a clearance record for this runway, so check
780 // whether the clearence ID matches that of the current aircraft
781 if (id == rwy->getCleared()) {
782 current->setHoldPosition(false);
784 current->setHoldPosition(true);
790 void FGTowerController::signOff(int id)
792 TrafficVectorIterator i = activeTraffic.begin();
793 // Search search if the current id alread has an entry
794 // This might be faster using a map instead of a vector, but let's start by taking a safe route
795 if (activeTraffic.size()) {
796 //while ((i->getId() != id) && i != activeTraffic.end()) {
797 while (i != activeTraffic.end()) {
798 if (i->getId() == id) {
804 // If this aircraft has left the runway, we can clear the departure record for this runway
805 ActiveRunwayVecIterator rwy = activeRunways.begin();
806 if (activeRunways.size()) {
807 //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
808 while (rwy != activeRunways.end()) {
809 if (rwy->getRunwayName() == i->getRunway()) {
814 if (rwy != activeRunways.end()) {
815 rwy = activeRunways.erase(rwy);
817 SG_LOG(SG_GENERAL, SG_ALERT,
818 "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
821 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
822 SG_LOG(SG_GENERAL, SG_ALERT,
823 "AI error: Aircraft without traffic record is signing off from tower");
825 i = activeTraffic.erase(i);
830 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
831 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
832 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
833 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
834 // Note that this function is probably obsolete
835 bool FGTowerController::hasInstruction(int id)
837 TrafficVectorIterator i = activeTraffic.begin();
838 // Search search if the current id has an entry
839 // This might be faster using a map instead of a vector, but let's start by taking a safe route
840 if (activeTraffic.size()) {
841 //while ((i->getId() != id) && i != activeTraffic.end()) {
842 while (i != activeTraffic.end()) {
843 if (i->getId() == id) {
849 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
850 SG_LOG(SG_GENERAL, SG_ALERT,
851 "AI error: checking ATC instruction for aircraft without traffic record");
853 return i->hasInstruction();
859 FGATCInstruction FGTowerController::getInstruction(int id)
861 TrafficVectorIterator i = activeTraffic.begin();
862 // Search search if the current id has an entry
863 // This might be faster using a map instead of a vector, but let's start by taking a safe route
864 if (activeTraffic.size()) {
865 //while ((i->getId() != id) && i != activeTraffic.end()) {
866 while (i != activeTraffic.end()) {
867 if (i->getId() == id) {
873 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
874 SG_LOG(SG_GENERAL, SG_ALERT,
875 "AI error: requesting ATC instruction for aircraft without traffic record");
877 return i->getInstruction();
879 return FGATCInstruction();
882 /***************************************************************************
883 * class FGStartupController
885 **************************************************************************/
886 FGStartupController::FGStartupController():
891 void FGStartupController::announcePosition(int id,
892 FGAIFlightPlan * intendedRoute,
893 int currentPosition, double lat,
894 double lon, double heading,
895 double speed, double alt,
896 double radius, int leg,
900 TrafficVectorIterator i = activeTraffic.begin();
901 // Search whether the current id alread has an entry
902 // This might be faster using a map instead of a vector, but let's start by taking a safe route
903 if (activeTraffic.size()) {
904 //while ((i->getId() != id) && i != activeTraffic.end()) {
905 while (i != activeTraffic.end()) {
906 if (i->getId() == id) {
912 // Add a new TrafficRecord if no one exsists for this aircraft.
913 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
917 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
918 rec.setRunway(intendedRoute->getRunway());
920 //rec.setCallSign(callsign);
921 rec.setAircraft(ref);
922 rec.setHoldPosition(true);
923 activeTraffic.push_back(rec);
925 i->setPositionAndHeading(lat, lon, heading, speed, alt);
931 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
932 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
933 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
934 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
935 // Note that this function is probably obsolete
936 bool FGStartupController::hasInstruction(int id)
938 TrafficVectorIterator i = activeTraffic.begin();
939 // Search search if the current id has an entry
940 // This might be faster using a map instead of a vector, but let's start by taking a safe route
941 if (activeTraffic.size()) {
942 //while ((i->getId() != id) && i != activeTraffic.end()) {
943 while (i != activeTraffic.end()) {
944 if (i->getId() == id) {
950 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
951 SG_LOG(SG_GENERAL, SG_ALERT,
952 "AI error: checking ATC instruction for aircraft without traffic record");
954 return i->hasInstruction();
960 FGATCInstruction FGStartupController::getInstruction(int id)
962 TrafficVectorIterator i = activeTraffic.begin();
963 // Search search if the current id has an entry
964 // This might be faster using a map instead of a vector, but let's start by taking a safe route
965 if (activeTraffic.size()) {
966 //while ((i->getId() != id) && i != activeTraffic.end()) {
967 while (i != activeTraffic.end()) {
968 if (i->getId() == id) {
974 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
975 SG_LOG(SG_GENERAL, SG_ALERT,
976 "AI error: requesting ATC instruction for aircraft without traffic record");
978 return i->getInstruction();
980 return FGATCInstruction();
983 void FGStartupController::signOff(int id)
985 TrafficVectorIterator i = activeTraffic.begin();
986 // Search search if the current id alread has an entry
987 // This might be faster using a map instead of a vector, but let's start by taking a safe route
988 if (activeTraffic.size()) {
989 //while ((i->getId() != id) && i != activeTraffic.end()) {
990 while (i != activeTraffic.end()) {
991 if (i->getId() == id) {
997 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
998 SG_LOG(SG_GENERAL, SG_ALERT,
999 "AI error: Aircraft without traffic record is signing off from tower");
1001 i = activeTraffic.erase(i);
1005 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
1006 double heading, double speed, double alt,
1009 TrafficVectorIterator i = activeTraffic.begin();
1010 // Search search if the current id has an entry
1011 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1012 TrafficVectorIterator current, closest;
1013 if (activeTraffic.size()) {
1014 //while ((i->getId() != id) && i != activeTraffic.end()) {
1015 while (i != activeTraffic.end()) {
1016 if (i->getId() == id) {
1022 // // update position of the current aircraft
1024 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1025 SG_LOG(SG_GENERAL, SG_ALERT,
1026 "AI error: updating aircraft without traffic record");
1028 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1031 setDt(getDt() + dt);
1033 int state = i->getState();
1035 // The user controlled aircraft should have crased here, because it doesn't have a traffic reference.
1036 // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
1037 time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
1038 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1039 //cerr << i->getAircraft()->getTrafficRef()->getCallSign()
1040 // << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1041 // << " at parking " << getGateName(i->getAircraft()) << endl;
1043 if ((now - lastTransmission) > 3 + (rand() % 15)) {
1047 if ((state == 0) && available) {
1048 if (now > startTime) {
1049 //cerr << "Transmitting startup msg" << endl;
1050 transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
1052 lastTransmission = now;
1056 if ((state == 1) && available) {
1057 if (now > startTime + 60) {
1058 transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
1060 lastTransmission = now;
1064 if ((state == 2) && available) {
1065 if (now > startTime + 80) {
1066 transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
1068 lastTransmission = now;
1072 if ((state == 3) && available) {
1073 if (now > startTime + 100) {
1074 transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START,
1077 lastTransmission = now;
1081 // Note: The next four stages are only necessesary when Startup control is
1082 // on a different frequency, compared to ground control
1083 if ((state == 4) && available) {
1084 if (now > startTime + 130) {
1085 transmit(&(*i), MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,
1089 lastTransmission = now;
1093 if ((state == 5) && available) {
1094 if (now > startTime + 140) {
1095 transmit(&(*i), MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND);
1097 lastTransmission = now;
1101 if ((state == 6) && available) {
1102 if (now > startTime + 150) {
1103 transmit(&(*i), MSG_ACKNOWLEDGE_INITIATE_CONTACT,
1106 lastTransmission = now;
1111 // TODO: Switch to APRON control and request pushback Clearance.
1112 // Get Push back clearance
1113 if ((state == 7) && available) {
1114 if (now > startTime + 180) {
1115 transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE,
1118 lastTransmission = now;
1122 if ((state == 8) && available) {
1123 if (now > startTime + 200) {
1124 if (i->pushBackAllowed()) {
1125 i->allowRepeatedTransmissions();
1126 transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE,
1130 transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE,
1132 i->suppressRepeatedTransmissions();
1134 lastTransmission = now;
1138 if ((state == 9) && available) {
1139 i->setHoldPosition(false);
1144 /***************************************************************************
1145 * class FGApproachController
1147 **************************************************************************/
1148 FGApproachController::FGApproachController():
1154 void FGApproachController::announcePosition(int id,
1155 FGAIFlightPlan * intendedRoute,
1156 int currentPosition,
1157 double lat, double lon,
1158 double heading, double speed,
1159 double alt, double radius,
1160 int leg, FGAIAircraft * ref)
1163 TrafficVectorIterator i = activeTraffic.begin();
1164 // Search whether the current id alread has an entry
1165 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1166 if (activeTraffic.size()) {
1167 //while ((i->getId() != id) && i != activeTraffic.end()) {
1168 while (i != activeTraffic.end()) {
1169 if (i->getId() == id) {
1175 // Add a new TrafficRecord if no one exsists for this aircraft.
1176 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1177 FGTrafficRecord rec;
1180 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1181 rec.setRunway(intendedRoute->getRunway());
1183 //rec.setCallSign(callsign);
1184 rec.setAircraft(ref);
1185 activeTraffic.push_back(rec);
1187 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1191 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1192 double heading, double speed, double alt,
1195 TrafficVectorIterator i = activeTraffic.begin();
1196 // Search search if the current id has an entry
1197 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1198 TrafficVectorIterator current, closest;
1199 if (activeTraffic.size()) {
1200 //while ((i->getId() != id) && i != activeTraffic.end()) {
1201 while (i != activeTraffic.end()) {
1202 if (i->getId() == id) {
1208 // // update position of the current aircraft
1209 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1210 SG_LOG(SG_GENERAL, SG_ALERT,
1211 "AI error: updating aircraft without traffic record");
1213 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1215 //cerr << "ApproachController: checking for speed" << endl;
1217 current->getAircraft()->
1218 checkForArrivalTime(string("final001"));
1219 if (time_diff > 15) {
1220 current->setSpeedAdjustment(current->getAircraft()->
1221 getPerformance()->vDescent() *
1223 } else if (time_diff > 5) {
1224 current->setSpeedAdjustment(current->getAircraft()->
1225 getPerformance()->vDescent() *
1227 } else if (time_diff < -15) {
1228 current->setSpeedAdjustment(current->getAircraft()->
1229 getPerformance()->vDescent() *
1231 } else if (time_diff < -5) {
1232 current->setSpeedAdjustment(current->getAircraft()->
1233 getPerformance()->vDescent() *
1236 current->clearSpeedAdjustment();
1238 //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1240 setDt(getDt() + dt);
1243 void FGApproachController::signOff(int id)
1245 TrafficVectorIterator i = activeTraffic.begin();
1246 // Search search if the current id alread has an entry
1247 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1248 if (activeTraffic.size()) {
1249 //while ((i->getId() != id) && i != activeTraffic.end()) {
1250 while (i != activeTraffic.end()) {
1251 if (i->getId() == id) {
1257 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1258 SG_LOG(SG_GENERAL, SG_ALERT,
1259 "AI error: Aircraft without traffic record is signing off from approach");
1261 i = activeTraffic.erase(i);
1268 bool FGApproachController::hasInstruction(int id)
1270 TrafficVectorIterator i = activeTraffic.begin();
1271 // Search search if the current id has an entry
1272 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1273 if (activeTraffic.size()) {
1274 //while ((i->getId() != id) && i != activeTraffic.end()) {
1275 while (i != activeTraffic.end()) {
1276 if (i->getId() == id) {
1282 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1283 SG_LOG(SG_GENERAL, SG_ALERT,
1284 "AI error: checking ATC instruction for aircraft without traffic record");
1286 return i->hasInstruction();
1292 FGATCInstruction FGApproachController::getInstruction(int id)
1294 TrafficVectorIterator i = activeTraffic.begin();
1295 // Search search if the current id has an entry
1296 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1297 if (activeTraffic.size()) {
1298 //while ((i->getId() != id) && i != activeTraffic.end()) {
1299 while (i != activeTraffic.end()) {
1300 if (i->getId() == id) {
1306 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1307 SG_LOG(SG_GENERAL, SG_ALERT,
1308 "AI error: requesting ATC instruction for aircraft without traffic record");
1310 return i->getInstruction();
1312 return FGATCInstruction();
1316 ActiveRunway *FGApproachController::getRunway(string name)
1318 ActiveRunwayVecIterator rwy = activeRunways.begin();
1319 if (activeRunways.size()) {
1320 while (rwy != activeRunways.end()) {
1321 if (rwy->getRunwayName() == name) {
1327 if (rwy == activeRunways.end()) {
1328 ActiveRunway aRwy(name, 0);
1329 activeRunways.push_back(aRwy);
1330 rwy = activeRunways.end() - 1;