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 bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId,
1008 int state = i->getState();
1009 if ((state == st) && available) {
1010 if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
1011 cerr << "Checking state " << st << " for " << i->getAircraft()->getCallSign() << endl;
1012 static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
1013 int n = trans_num->getIntValue();
1015 trans_num->setIntValue(-1);
1016 // PopupCallback(n);
1017 cerr << "Selected transmission message" << n << endl;
1022 if (now > startTime) {
1023 //cerr << "Transmitting startup msg" << endl;
1024 transmit(&(*i), msgId, msgDir);
1026 lastTransmission = now;
1034 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
1035 double heading, double speed, double alt,
1038 TrafficVectorIterator i = activeTraffic.begin();
1039 // Search search if the current id has an entry
1040 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1041 TrafficVectorIterator current, closest;
1042 if (activeTraffic.size()) {
1043 //while ((i->getId() != id) && i != activeTraffic.end()) {
1044 while (i != activeTraffic.end()) {
1045 if (i->getId() == id) {
1051 // // update position of the current aircraft
1053 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1054 SG_LOG(SG_GENERAL, SG_ALERT,
1055 "AI error: updating aircraft without traffic record");
1057 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1060 setDt(getDt() + dt);
1062 int state = i->getState();
1064 // The user controlled aircraft should have crased here, because it doesn't have a traffic reference.
1065 // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
1066 time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
1067 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1068 //cerr << i->getAircraft()->getTrafficRef()->getCallSign()
1069 // << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1070 // << " at parking " << getGateName(i->getAircraft()) << endl;
1072 if ((now - lastTransmission) > 3 + (rand() % 15)) {
1076 checkTransmissionState(0, now, (startTime + 0 ), i, MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
1077 checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
1078 checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
1079 checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND);
1080 if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, ATC_AIR_TO_GROUND)) {
1083 checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND);
1084 checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT, ATC_GROUND_TO_AIR);
1088 if ((state == 0) && available) {
1089 if (now > startTime) {
1090 //cerr << "Transmitting startup msg" << endl;
1091 transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
1093 lastTransmission = now;
1097 if ((state == 1) && available) {
1098 if (now > startTime + 60) {
1099 transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
1101 lastTransmission = now;
1105 if ((state == 2) && available) {
1106 if (now > startTime + 80) {
1107 transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
1109 lastTransmission = now;
1113 if ((state == 3) && available) {
1114 if (now > startTime + 100) {
1115 transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START,
1118 lastTransmission = now;
1122 // Note: The next four stages are only necessesary when Startup control is
1123 // on a different frequency, compared to ground control
1124 if ((state == 4) && available) {
1125 if (now > startTime + 130) {
1126 transmit(&(*i), MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,
1130 lastTransmission = now;
1134 if ((state == 5) && available) {
1135 if (now > startTime + 140) {
1136 transmit(&(*i), MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND);
1138 lastTransmission = now;
1142 if ((state == 6) && available) {
1143 if (now > startTime + 150) {
1144 transmit(&(*i), MSG_ACKNOWLEDGE_INITIATE_CONTACT,
1147 lastTransmission = now;
1152 // TODO: Switch to APRON control and request pushback Clearance.
1153 // Get Push back clearance
1154 if ((state == 7) && available) {
1155 if (now > startTime + 180) {
1156 transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE,
1159 lastTransmission = now;
1163 if ((state == 8) && available) {
1164 if (now > startTime + 200) {
1165 if (i->pushBackAllowed()) {
1166 i->allowRepeatedTransmissions();
1167 transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE,
1171 transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE,
1173 i->suppressRepeatedTransmissions();
1175 lastTransmission = now;
1179 if ((state == 9) && available) {
1180 i->setHoldPosition(false);
1185 /***************************************************************************
1186 * class FGApproachController
1188 **************************************************************************/
1189 FGApproachController::FGApproachController():
1195 void FGApproachController::announcePosition(int id,
1196 FGAIFlightPlan * intendedRoute,
1197 int currentPosition,
1198 double lat, double lon,
1199 double heading, double speed,
1200 double alt, double radius,
1201 int leg, FGAIAircraft * ref)
1204 TrafficVectorIterator i = activeTraffic.begin();
1205 // Search whether the current id alread has an entry
1206 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1207 if (activeTraffic.size()) {
1208 //while ((i->getId() != id) && i != activeTraffic.end()) {
1209 while (i != activeTraffic.end()) {
1210 if (i->getId() == id) {
1216 // Add a new TrafficRecord if no one exsists for this aircraft.
1217 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1218 FGTrafficRecord rec;
1221 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1222 rec.setRunway(intendedRoute->getRunway());
1224 //rec.setCallSign(callsign);
1225 rec.setAircraft(ref);
1226 activeTraffic.push_back(rec);
1228 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1232 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1233 double heading, double speed, double alt,
1236 TrafficVectorIterator i = activeTraffic.begin();
1237 // Search search if the current id has an entry
1238 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1239 TrafficVectorIterator current, closest;
1240 if (activeTraffic.size()) {
1241 //while ((i->getId() != id) && i != activeTraffic.end()) {
1242 while (i != activeTraffic.end()) {
1243 if (i->getId() == id) {
1249 // // update position of the current aircraft
1250 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1251 SG_LOG(SG_GENERAL, SG_ALERT,
1252 "AI error: updating aircraft without traffic record");
1254 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1256 //cerr << "ApproachController: checking for speed" << endl;
1258 current->getAircraft()->
1259 checkForArrivalTime(string("final001"));
1260 if (time_diff > 15) {
1261 current->setSpeedAdjustment(current->getAircraft()->
1262 getPerformance()->vDescent() *
1264 } else if (time_diff > 5) {
1265 current->setSpeedAdjustment(current->getAircraft()->
1266 getPerformance()->vDescent() *
1268 } else if (time_diff < -15) {
1269 current->setSpeedAdjustment(current->getAircraft()->
1270 getPerformance()->vDescent() *
1272 } else if (time_diff < -5) {
1273 current->setSpeedAdjustment(current->getAircraft()->
1274 getPerformance()->vDescent() *
1277 current->clearSpeedAdjustment();
1279 //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1281 setDt(getDt() + dt);
1284 void FGApproachController::signOff(int id)
1286 TrafficVectorIterator i = activeTraffic.begin();
1287 // Search search if the current id alread has an entry
1288 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1289 if (activeTraffic.size()) {
1290 //while ((i->getId() != id) && i != activeTraffic.end()) {
1291 while (i != activeTraffic.end()) {
1292 if (i->getId() == id) {
1298 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1299 SG_LOG(SG_GENERAL, SG_ALERT,
1300 "AI error: Aircraft without traffic record is signing off from approach");
1302 i = activeTraffic.erase(i);
1309 bool FGApproachController::hasInstruction(int id)
1311 TrafficVectorIterator i = activeTraffic.begin();
1312 // Search search if the current id has an entry
1313 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1314 if (activeTraffic.size()) {
1315 //while ((i->getId() != id) && i != activeTraffic.end()) {
1316 while (i != activeTraffic.end()) {
1317 if (i->getId() == id) {
1323 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1324 SG_LOG(SG_GENERAL, SG_ALERT,
1325 "AI error: checking ATC instruction for aircraft without traffic record");
1327 return i->hasInstruction();
1333 FGATCInstruction FGApproachController::getInstruction(int id)
1335 TrafficVectorIterator i = activeTraffic.begin();
1336 // Search search if the current id has an entry
1337 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1338 if (activeTraffic.size()) {
1339 //while ((i->getId() != id) && i != activeTraffic.end()) {
1340 while (i != activeTraffic.end()) {
1341 if (i->getId() == id) {
1347 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1348 SG_LOG(SG_GENERAL, SG_ALERT,
1349 "AI error: requesting ATC instruction for aircraft without traffic record");
1351 return i->getInstruction();
1353 return FGATCInstruction();
1357 ActiveRunway *FGApproachController::getRunway(string name)
1359 ActiveRunwayVecIterator rwy = activeRunways.begin();
1360 if (activeRunways.size()) {
1361 while (rwy != activeRunways.end()) {
1362 if (rwy->getRunwayName() == name) {
1368 if (rwy == activeRunways.end()) {
1369 ActiveRunway aRwy(name, 0);
1370 activeRunways.push_back(aRwy);
1371 rwy = activeRunways.end() - 1;