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>
33 /***************************************************************************
35 **************************************************************************/
36 FGTrafficRecord::FGTrafficRecord() :
49 void FGTrafficRecord::setPositionAndIntentions(int pos, FGAIFlightPlan *route)
53 if (intentions.size()) {
54 intVecIterator i = intentions.begin();
56 SG_LOG(SG_GENERAL, SG_ALERT, "Error in FGTrafficRecord::setPositionAndIntentions");
57 //cerr << "Pos : " << pos << " Curr " << *(intentions.begin()) << endl;
58 for (intVecIterator i = intentions.begin(); i != intentions.end() ; i++) {
59 //cerr << (*i) << " ";
66 //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
67 int size = route->getNrOfWayPoints();
68 //cerr << "Setting pos" << pos << " ";
69 //cerr << "setting intentions ";
70 for (int i = 0; i < size; i++) {
71 int val = route->getRouteIndex(i);
73 if ((val) && (val != pos))
75 intentions.push_back(val);
80 //while (route->next(&legNr, &routeNr)) {
81 //intentions.push_back(routeNr);
83 //route->rewind(currentPos);
88 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord &other)
91 //cerr << "Start check 1" << endl;
92 if (currentPos == other.currentPos)
94 //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
97 // else if (other.intentions.size())
99 // cerr << "Start check 2" << endl;
100 // intVecIterator i = other.intentions.begin();
101 // while (!((i == other.intentions.end()) || ((*i) == currentPos)))
103 // if (i != other.intentions.end()) {
104 // cerr << "Check Position and intentions: current matches other.intentions" << endl;
107 else if (intentions.size()) {
108 //cerr << "Start check 3" << endl;
109 intVecIterator i = intentions.begin();
110 //while (!((i == intentions.end()) || ((*i) == other.currentPos)))
111 while (i != intentions.end()) {
112 if ((*i) == other.currentPos) {
117 if (i != intentions.end()) {
118 //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
122 //cerr << "Done !!" << endl;
126 void FGTrafficRecord::setPositionAndHeading(double lat, double lon, double hdg,
127 double spd, double alt)
136 int FGTrafficRecord::crosses(FGGroundNetwork *net, FGTrafficRecord &other)
138 if (checkPositionAndIntentions(other) || (other.checkPositionAndIntentions(*this)))
141 int currentTargetNode = 0, otherTargetNode = 0;
143 currentTargetNode = net->findSegment(currentPos )->getEnd()->getIndex(); // OKAY,...
144 if (other.currentPos > 0)
145 otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,...
146 if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
147 return currentTargetNode;
148 if (intentions.size())
150 for (i = intentions.begin(); i != intentions.end(); i++)
153 if ((currentTargetNode == net->findSegment(*i)->getEnd()->getIndex()))
155 //cerr << "Current crosses at " << currentTargetNode <<endl;
156 return currentTargetNode;
161 if (other.intentions.size())
163 for (i = other.intentions.begin(); i != other.intentions.end(); i++)
166 if (otherTargetNode == net->findSegment(*i)->getEnd()->getIndex())
168 //cerr << "Other crosses at " << currentTargetNode <<endl;
169 return otherTargetNode;
174 if (intentions.size() && other.intentions.size())
176 for (i = intentions.begin(); i != intentions.end(); i++)
178 for (j = other.intentions.begin(); j != other.intentions.end(); j++)
180 //cerr << "finding segment " << *i << " and " << *j << endl;
181 if (((*i) > 0) && ((*j) > 0)) {
182 currentTargetNode = net->findSegment(*i)->getEnd()->getIndex();
183 otherTargetNode = net->findSegment(*j)->getEnd()->getIndex();
184 if (currentTargetNode == otherTargetNode)
186 //cerr << "Routes will cross at " << currentTargetNode << endl;
187 return currentTargetNode;
196 bool FGTrafficRecord::onRoute(FGGroundNetwork *net, FGTrafficRecord &other)
198 int node = -1, othernode = -1;
200 node = net->findSegment(currentPos)->getEnd()->getIndex();
201 if (other.currentPos > 0)
202 othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
203 if ((node == othernode) && (node != -1))
205 if (other.intentions.size())
207 for (intVecIterator i = other.intentions.begin(); i != other.intentions.end(); i++)
211 othernode = net->findSegment(*i)->getEnd()->getIndex();
212 if ((node == othernode) && (node > -1))
217 //if (other.currentPos > 0)
218 // othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
219 //if (intentions.size())
221 // for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
225 // node = net->findSegment(*i)->getEnd()->getIndex();
226 // if ((node == othernode) && (node > -1))
235 bool FGTrafficRecord::isOpposing (FGGroundNetwork *net, FGTrafficRecord &other, int node)
237 // Check if current segment is the reverse segment for the other aircraft
239 //cerr << "Current segment " << currentPos << endl;
240 if ((currentPos > 0) && (other.currentPos > 0))
242 opp = net->findSegment(currentPos)->opposite();
244 if (opp->getIndex() == other.currentPos)
248 for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
250 if (opp = net->findSegment(other.currentPos)->opposite())
253 if (opp->getIndex() == net->findSegment(*i)->getIndex())
255 if (net->findSegment(*i)->getStart()->getIndex() == node) {
257 //cerr << "Found the node " << node << endl;
263 if (other.intentions.size())
265 for (intVecIterator j = other.intentions.begin(); j != other.intentions.end(); j++)
267 // cerr << "Current segment 1 " << (*i) << endl;
269 if (opp = net->findSegment(*i)->opposite())
271 if (opp->getIndex() ==
272 net->findSegment(*j)->getIndex())
274 //cerr << "Nodes " << net->findSegment(*i)->getIndex()
275 // << " and " << net->findSegment(*j)->getIndex()
276 // << " are opposites " << endl;
277 if (net->findSegment(*i)->getStart()->getIndex() == node) {
279 //cerr << "Found the node " << node << endl;
293 void FGTrafficRecord::setSpeedAdjustment(double spd)
295 instruction.setChangeSpeed(true);
296 instruction.setSpeed(spd);
299 void FGTrafficRecord::setHeadingAdjustment(double heading)
301 instruction.setChangeHeading(true);
302 instruction.setHeading(heading);
307 /***************************************************************************
310 **************************************************************************/
311 FGATCInstruction::FGATCInstruction()
314 holdPosition = false;
316 changeHeading = false;
317 changeAltitude = false;
318 resolveCircularWait = false;
325 bool FGATCInstruction::hasInstruction()
327 return (holdPattern || holdPosition || changeSpeed || changeHeading || changeAltitude || resolveCircularWait);
330 string FGATCController::getGateName(FGAIAircraft *ref)
332 return ref->atGate();
335 void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir msgDir)
337 string sender, receiver;
341 case ATC_AIR_TO_GROUND:
342 sender = rec->getAircraft()->getTrafficRef()->getCallSign();
343 switch (rec->getLeg()) {
346 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency();
347 receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
350 receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
353 receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
357 case ATC_GROUND_TO_AIR:
358 receiver = rec->getAircraft()->getTrafficRef()->getCallSign();
359 switch (rec->getLeg()) {
362 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency();
363 sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
366 sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
369 sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
376 case MSG_ANNOUNCE_ENGINE_START:
377 text = sender + ". Ready to Start up";
379 case MSG_REQUEST_ENGINE_START:
380 text = receiver + ", This is " + sender + ". Position " +getGateName(rec->getAircraft()) +
381 ". Information YY." +
382 rec->getAircraft()->getTrafficRef()->getFlightRules() + " to " +
383 rec->getAircraft()->getTrafficRef()->getArrivalAirport()->getName() + ". Request start-up";
385 case MSG_PERMIT_ENGINE_START:
386 text = receiver + ". Start-up approved. YY correct, runway ZZ, AAA departure, squawk BBBB. " +
387 "For push-back and taxi clearance call CCC.CCC. " + sender + " control.";
389 case MSG_DENY_ENGINE_START:
390 text = receiver + ". Standby";
392 case MSG_ACKNOWLEDGE_ENGINE_START:
393 text = receiver + ". Start-up approved. YY correct, runway ZZ, AAA departure, squawk BBBB. " +
394 "For push-back and taxi clearance call CCC.CCC. " + sender;
399 double currFreq = fgGetDouble("/instrumentation/comm/frequencies/selected-mhz");
400 int currFreqI = (int) floor(currFreq * 100 + 0.5);
401 //cerr << "Using " << currFreqI << " and " << commFreq << endl;
402 if (currFreqI == commFreq) {
403 fgSetString("/sim/messages/atc", text.c_str());
404 //cerr << "Printing Message: " << endl;
409 /***************************************************************************
410 * class FGTowerController
412 **************************************************************************/
413 FGTowerController::FGTowerController() :
419 void FGTowerController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
420 double lat, double lon, double heading,
421 double speed, double alt, double radius, int leg,
424 TrafficVectorIterator i = activeTraffic.begin();
425 // Search whether the current id alread has an entry
426 // This might be faster using a map instead of a vector, but let's start by taking a safe route
427 if (activeTraffic.size()) {
428 //while ((i->getId() != id) && i != activeTraffic.end()) {
429 while (i != activeTraffic.end()) {
430 if (i->getId() == id) {
437 // Add a new TrafficRecord if no one exsists for this aircraft.
438 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
442 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
443 rec.setRunway(intendedRoute->getRunway());
445 //rec.setCallSign(callsign);
446 rec.setAircraft(ref);
447 activeTraffic.push_back(rec);
449 i->setPositionAndHeading(lat, lon, heading, speed, alt);
453 void FGTowerController::update(int id, double lat, double lon, double heading, double speed, double alt,
456 TrafficVectorIterator i = activeTraffic.begin();
457 // Search search if the current id has an entry
458 // This might be faster using a map instead of a vector, but let's start by taking a safe route
459 TrafficVectorIterator current, closest;
460 if (activeTraffic.size()) {
461 //while ((i->getId() != id) && i != activeTraffic.end()) {
462 while (i != activeTraffic.end()) {
463 if (i->getId() == id) {
470 // // update position of the current aircraft
471 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
472 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
474 i->setPositionAndHeading(lat, lon, heading, speed, alt);
479 // // see if we already have a clearance record for the currently active runway
480 ActiveRunwayVecIterator rwy = activeRunways.begin();
481 // again, a map might be more efficient here
482 if (activeRunways.size()) {
483 //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
484 while (rwy != activeRunways.end()) {
485 if (rwy->getRunwayName() == current->getRunway()) {
491 if (rwy == activeRunways.end()) {
492 ActiveRunway aRwy(current->getRunway(), id);
493 activeRunways.push_back(aRwy); // Since there are no clearance records for this runway yet
494 current->setHoldPosition(false); // Clear the current aircraft to continue
497 // Okay, we have a clearance record for this runway, so check
498 // whether the clearence ID matches that of the current aircraft
499 if (id == rwy->getCleared()) {
500 current->setHoldPosition(false);
502 current->setHoldPosition(true);
508 void FGTowerController::signOff(int id)
510 TrafficVectorIterator i = activeTraffic.begin();
511 // Search search if the current id alread has an entry
512 // This might be faster using a map instead of a vector, but let's start by taking a safe route
513 if (activeTraffic.size()) {
514 //while ((i->getId() != id) && i != activeTraffic.end()) {
515 while (i != activeTraffic.end()) {
516 if (i->getId() == id) {
522 // If this aircraft has left the runway, we can clear the departure record for this runway
523 ActiveRunwayVecIterator rwy = activeRunways.begin();
524 if (activeRunways.size()) {
525 //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
526 while (rwy != activeRunways.end()) {
527 if (rwy->getRunwayName() == i->getRunway()) {
532 if (rwy != activeRunways.end()) {
533 rwy = activeRunways.erase(rwy);
535 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
538 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
539 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower");
541 i = activeTraffic.erase(i);
546 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
547 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
548 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
549 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
550 // Note that this function is probably obsolete
551 bool FGTowerController::hasInstruction(int id)
553 TrafficVectorIterator i = activeTraffic.begin();
554 // Search search if the current id has an entry
555 // This might be faster using a map instead of a vector, but let's start by taking a safe route
556 if (activeTraffic.size())
558 //while ((i->getId() != id) && i != activeTraffic.end()) {
559 while (i != activeTraffic.end()) {
560 if (i->getId() == id) {
566 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
567 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
569 return i->hasInstruction();
575 FGATCInstruction FGTowerController::getInstruction(int id)
577 TrafficVectorIterator i = activeTraffic.begin();
578 // Search search if the current id has an entry
579 // This might be faster using a map instead of a vector, but let's start by taking a safe route
580 if (activeTraffic.size()) {
581 //while ((i->getId() != id) && i != activeTraffic.end()) {
582 while (i != activeTraffic.end()) {
583 if (i->getId() == id) {
589 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
590 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
592 return i->getInstruction();
594 return FGATCInstruction();
597 /***************************************************************************
598 * class FGStartupController
600 **************************************************************************/
601 FGStartupController::FGStartupController() :
607 void FGStartupController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
608 double lat, double lon, double heading,
609 double speed, double alt, double radius, int leg,
612 TrafficVectorIterator i = activeTraffic.begin();
613 // Search whether the current id alread has an entry
614 // This might be faster using a map instead of a vector, but let's start by taking a safe route
615 if (activeTraffic.size()) {
616 //while ((i->getId() != id) && i != activeTraffic.end()) {
617 while (i != activeTraffic.end()) {
618 if (i->getId() == id) {
625 // Add a new TrafficRecord if no one exsists for this aircraft.
626 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
630 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
631 rec.setRunway(intendedRoute->getRunway());
633 //rec.setCallSign(callsign);
634 rec.setAircraft(ref);
635 rec.setHoldPosition(true);
636 activeTraffic.push_back(rec);
638 i->setPositionAndHeading(lat, lon, heading, speed, alt);
644 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
645 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
646 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
647 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
648 // Note that this function is probably obsolete
649 bool FGStartupController::hasInstruction(int id)
651 TrafficVectorIterator i = activeTraffic.begin();
652 // Search search if the current id has an entry
653 // This might be faster using a map instead of a vector, but let's start by taking a safe route
654 if (activeTraffic.size())
656 //while ((i->getId() != id) && i != activeTraffic.end()) {
657 while (i != activeTraffic.end()) {
658 if (i->getId() == id) {
664 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
665 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
667 return i->hasInstruction();
673 FGATCInstruction FGStartupController::getInstruction(int id)
675 TrafficVectorIterator i = activeTraffic.begin();
676 // Search search if the current id has an entry
677 // This might be faster using a map instead of a vector, but let's start by taking a safe route
678 if (activeTraffic.size()) {
679 //while ((i->getId() != id) && i != activeTraffic.end()) {
680 while (i != activeTraffic.end()) {
681 if (i->getId() == id) {
687 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
688 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
690 return i->getInstruction();
692 return FGATCInstruction();
695 void FGStartupController::signOff(int id)
697 TrafficVectorIterator i = activeTraffic.begin();
698 // Search search if the current id alread has an entry
699 // This might be faster using a map instead of a vector, but let's start by taking a safe route
700 if (activeTraffic.size()) {
701 //while ((i->getId() != id) && i != activeTraffic.end()) {
702 while (i != activeTraffic.end()) {
703 if (i->getId() == id) {
709 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
710 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower");
712 i = activeTraffic.erase(i);
716 void FGStartupController::update(int id, double lat, double lon, double heading, double speed, double alt,
719 TrafficVectorIterator i = activeTraffic.begin();
720 // Search search if the current id has an entry
721 // This might be faster using a map instead of a vector, but let's start by taking a safe route
722 TrafficVectorIterator current, closest;
723 if (activeTraffic.size()) {
724 //while ((i->getId() != id) && i != activeTraffic.end()) {
725 while (i != activeTraffic.end()) {
726 if (i->getId() == id) {
733 // // update position of the current aircraft
734 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
735 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
737 i->setPositionAndHeading(lat, lon, heading, speed, alt);
742 int state = i->getState();
743 time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
744 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
746 if ((now - lastTransmission) > 3 + (rand() % 15)) {
750 if ((state == 0) && available) {
751 if (now > startTime) {
752 transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
754 lastTransmission = now;
758 if ((state == 1) && available) {
759 if (now > startTime+60) {
760 transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
762 lastTransmission = now;
766 if ((state == 2) && available) {
767 if (now > startTime+80) {
768 transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
770 lastTransmission = now;
774 if ((state == 3) && available){
775 if (now > startTime+100) {
776 transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND);
778 lastTransmission = now;
782 // TODO: Switch to APRON control and request pushback Clearance.
783 // Get Push back clearance
784 if ((state == 4) && available){
785 i->setHoldPosition(false);