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 void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId,
471 string sender, receiver;
475 string atisInformation;
483 string transponderCode;
488 sender = rec->getAircraft()->getTrafficRef()->getCallSign();
489 //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
490 switch (rec->getLeg()) {
493 freqId = rec->getNextFrequency();
495 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
496 getDynamics()->getGroundFrequency(rec->getLeg() + freqId);
498 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
499 getDynamics()->getGroundFrequency(3);
501 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
502 getName() + "-Ground";
504 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
505 getDynamics()->getAtisInformation();
509 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
510 getName() + "-Tower";
513 // Swap sender and receiver value in case of a ground to air transmission
514 if (msgDir == ATC_GROUND_TO_AIR) {
520 case MSG_ANNOUNCE_ENGINE_START:
521 text = sender + ". Ready to Start up";
523 case MSG_REQUEST_ENGINE_START:
525 receiver + ", This is " + sender + ". Position " +
526 getGateName(rec->getAircraft()) + ". Information " +
527 atisInformation + ". " +
528 rec->getAircraft()->getTrafficRef()->getFlightRules() +
530 rec->getAircraft()->getTrafficRef()->getArrivalAirport()->
531 getName() + ". Request start-up";
533 // Acknowledge engine startup permission
534 // Assign departure runway
535 // Assign SID, if necessery (TODO)
536 case MSG_PERMIT_ENGINE_START:
537 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
539 heading = rec->getAircraft()->getTrafficRef()->getCourse();
540 fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
542 rec->getAircraft()->GetFlightPlan()->
543 getRunwayClassFromTrafficType(fltType);
545 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
546 getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
548 rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
549 fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
550 getDynamics()->getSID(activeRunway, heading);
551 rec->getAircraft()->GetFlightPlan()->setSID(fp);
553 SID = fp->getName() + " departure";
555 SID = "fly runway heading ";
557 //snprintf(buffer, 7, "%3.2f", heading);
558 fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
559 transponderCode = genTransponderCode(fltRules);
560 rec->getAircraft()->SetTransponderCode(transponderCode);
562 receiver + ". Start-up approved. " + atisInformation +
563 " correct, runway " + activeRunway + ", " + SID + ", squawk " +
564 transponderCode + ". " +
565 "For push-back and taxi clearance call " + taxiFreqStr + ". " +
566 sender + " control.";
568 case MSG_DENY_ENGINE_START:
569 text = receiver + ". Standby";
571 case MSG_ACKNOWLEDGE_ENGINE_START:
572 fp = rec->getAircraft()->GetFlightPlan()->getSID();
575 rec->getAircraft()->GetFlightPlan()->getSID()->getName() +
578 SID = "fly runway heading ";
580 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
581 activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
582 transponderCode = rec->getAircraft()->GetTransponderCode();
584 receiver + ". Start-up approved. " + atisInformation +
585 " correct, runway " + activeRunway + ", " + SID + ", squawk " +
586 transponderCode + ". " +
587 "For push-back and taxi clearance call " + taxiFreqStr + ". " +
590 case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
591 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
592 text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
594 case MSG_INITIATE_CONTACT:
595 text = receiver + ". With you. " + sender;
597 case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
598 text = receiver + ". Roger. " + sender;
600 case MSG_REQUEST_PUSHBACK_CLEARANCE:
601 text = receiver + ". Request push-back. " + sender;
603 case MSG_PERMIT_PUSHBACK_CLEARANCE:
604 text = receiver + ". Push-back approved. " + sender;
606 case MSG_HOLD_PUSHBACK_CLEARANCE:
607 text = receiver + ". Standby. " + sender;
609 case MSG_REQUEST_TAXI_CLEARANCE:
610 text = receiver + ". Ready to Taxi. " + sender;
612 case MSG_ISSUE_TAXI_CLEARANCE:
613 text = receiver + ". Cleared to taxi. " + sender;
615 case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
616 text = receiver + ". Cleared to taxi. " + sender;
618 case MSG_HOLD_POSITION:
619 text = receiver + ". Hold Position. " + sender;
621 case MSG_ACKNOWLEDGE_HOLD_POSITION:
622 text = receiver + ". Holding Position. " + sender;
624 case MSG_RESUME_TAXI:
625 text = receiver + ". Resume Taxiing. " + sender;
627 case MSG_ACKNOWLEDGE_RESUME_TAXI:
628 text = receiver + ". Continuing Taxi. " + sender;
631 text = text + sender + ". Transmitting unknown Message";
634 double onBoardRadioFreq0 =
635 fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
636 double onBoardRadioFreq1 =
637 fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
638 int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
639 int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
640 //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl;
642 // Display ATC message only when one of the radios is tuned
643 // the relevant frequency.
644 // Note that distance attenuation is currently not yet implemented
645 if ((onBoardRadioFreqI0 == stationFreq)
646 || (onBoardRadioFreqI1 == stationFreq)) {
647 if (rec->allowTransmissions()) {
648 fgSetString("/sim/messages/atc", text.c_str());
653 string FGATCController::formatATCFrequency3_2(int freq)
656 snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
657 return string(buffer);
660 // TODO: Set transponder codes according to real-world routes.
661 // The current version just returns a random string of four octal numbers.
662 string FGATCController::genTransponderCode(string fltRules)
664 if (fltRules == "VFR") {
665 return string("1200");
668 snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
670 return string(buffer);
674 void FGATCController::init()
677 FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
678 mgr->addController(this);
683 /***************************************************************************
684 * class FGTowerController
686 **************************************************************************/
687 FGTowerController::FGTowerController():
693 void FGTowerController::announcePosition(int id,
694 FGAIFlightPlan * intendedRoute,
695 int currentPosition, double lat,
696 double lon, double heading,
697 double speed, double alt,
698 double radius, int leg,
702 TrafficVectorIterator i = activeTraffic.begin();
703 // Search whether the current id alread has an entry
704 // This might be faster using a map instead of a vector, but let's start by taking a safe route
705 if (activeTraffic.size()) {
706 //while ((i->getId() != id) && i != activeTraffic.end()) {
707 while (i != activeTraffic.end()) {
708 if (i->getId() == id) {
714 // Add a new TrafficRecord if no one exsists for this aircraft.
715 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
719 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
720 rec.setRunway(intendedRoute->getRunway());
722 //rec.setCallSign(callsign);
723 rec.setAircraft(ref);
724 activeTraffic.push_back(rec);
726 i->setPositionAndHeading(lat, lon, heading, speed, alt);
730 void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
731 double heading, double speed, double alt,
734 TrafficVectorIterator i = activeTraffic.begin();
735 // Search whether the current id has an entry
736 // This might be faster using a map instead of a vector, but let's start by taking a safe route
737 TrafficVectorIterator current, closest;
738 if (activeTraffic.size()) {
739 //while ((i->getId() != id) && i != activeTraffic.end()) {
740 while (i != activeTraffic.end()) {
741 if (i->getId() == id) {
747 // // update position of the current aircraft
748 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
749 SG_LOG(SG_GENERAL, SG_ALERT,
750 "AI error: updating aircraft without traffic record");
752 i->setPositionAndHeading(lat, lon, heading, speed, alt);
757 // // see if we already have a clearance record for the currently active runway
758 ActiveRunwayVecIterator rwy = activeRunways.begin();
759 // again, a map might be more efficient here
760 if (activeRunways.size()) {
761 //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
762 while (rwy != activeRunways.end()) {
763 if (rwy->getRunwayName() == current->getRunway()) {
769 if (rwy == activeRunways.end()) {
770 ActiveRunway aRwy(current->getRunway(), id);
771 activeRunways.push_back(aRwy); // Since there are no clearance records for this runway yet
772 current->setHoldPosition(false); // Clear the current aircraft to continue
774 // Okay, we have a clearance record for this runway, so check
775 // whether the clearence ID matches that of the current aircraft
776 if (id == rwy->getCleared()) {
777 current->setHoldPosition(false);
779 current->setHoldPosition(true);
785 void FGTowerController::signOff(int id)
787 TrafficVectorIterator i = activeTraffic.begin();
788 // Search search if the current id alread has an entry
789 // This might be faster using a map instead of a vector, but let's start by taking a safe route
790 if (activeTraffic.size()) {
791 //while ((i->getId() != id) && i != activeTraffic.end()) {
792 while (i != activeTraffic.end()) {
793 if (i->getId() == id) {
799 // If this aircraft has left the runway, we can clear the departure record for this runway
800 ActiveRunwayVecIterator rwy = activeRunways.begin();
801 if (activeRunways.size()) {
802 //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
803 while (rwy != activeRunways.end()) {
804 if (rwy->getRunwayName() == i->getRunway()) {
809 if (rwy != activeRunways.end()) {
810 rwy = activeRunways.erase(rwy);
812 SG_LOG(SG_GENERAL, SG_ALERT,
813 "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
816 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
817 SG_LOG(SG_GENERAL, SG_ALERT,
818 "AI error: Aircraft without traffic record is signing off from tower");
820 i = activeTraffic.erase(i);
825 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
826 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
827 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
828 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
829 // Note that this function is probably obsolete
830 bool FGTowerController::hasInstruction(int id)
832 TrafficVectorIterator i = activeTraffic.begin();
833 // Search search if the current id has an entry
834 // This might be faster using a map instead of a vector, but let's start by taking a safe route
835 if (activeTraffic.size()) {
836 //while ((i->getId() != id) && i != activeTraffic.end()) {
837 while (i != activeTraffic.end()) {
838 if (i->getId() == id) {
844 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
845 SG_LOG(SG_GENERAL, SG_ALERT,
846 "AI error: checking ATC instruction for aircraft without traffic record");
848 return i->hasInstruction();
854 FGATCInstruction FGTowerController::getInstruction(int id)
856 TrafficVectorIterator i = activeTraffic.begin();
857 // Search search if the current id has an entry
858 // This might be faster using a map instead of a vector, but let's start by taking a safe route
859 if (activeTraffic.size()) {
860 //while ((i->getId() != id) && i != activeTraffic.end()) {
861 while (i != activeTraffic.end()) {
862 if (i->getId() == id) {
868 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
869 SG_LOG(SG_GENERAL, SG_ALERT,
870 "AI error: requesting ATC instruction for aircraft without traffic record");
872 return i->getInstruction();
874 return FGATCInstruction();
877 /***************************************************************************
878 * class FGStartupController
880 **************************************************************************/
881 FGStartupController::FGStartupController():
886 void FGStartupController::announcePosition(int id,
887 FGAIFlightPlan * intendedRoute,
888 int currentPosition, double lat,
889 double lon, double heading,
890 double speed, double alt,
891 double radius, int leg,
895 TrafficVectorIterator i = activeTraffic.begin();
896 // Search whether the current id alread has an entry
897 // This might be faster using a map instead of a vector, but let's start by taking a safe route
898 if (activeTraffic.size()) {
899 //while ((i->getId() != id) && i != activeTraffic.end()) {
900 while (i != activeTraffic.end()) {
901 if (i->getId() == id) {
907 // Add a new TrafficRecord if no one exsists for this aircraft.
908 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
912 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
913 rec.setRunway(intendedRoute->getRunway());
915 //rec.setCallSign(callsign);
916 rec.setAircraft(ref);
917 rec.setHoldPosition(true);
918 activeTraffic.push_back(rec);
920 i->setPositionAndHeading(lat, lon, heading, speed, alt);
926 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
927 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
928 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
929 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
930 // Note that this function is probably obsolete
931 bool FGStartupController::hasInstruction(int id)
933 TrafficVectorIterator i = activeTraffic.begin();
934 // Search search if the current id has an entry
935 // This might be faster using a map instead of a vector, but let's start by taking a safe route
936 if (activeTraffic.size()) {
937 //while ((i->getId() != id) && i != activeTraffic.end()) {
938 while (i != activeTraffic.end()) {
939 if (i->getId() == id) {
945 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
946 SG_LOG(SG_GENERAL, SG_ALERT,
947 "AI error: checking ATC instruction for aircraft without traffic record");
949 return i->hasInstruction();
955 FGATCInstruction FGStartupController::getInstruction(int id)
957 TrafficVectorIterator i = activeTraffic.begin();
958 // Search search if the current id has an entry
959 // This might be faster using a map instead of a vector, but let's start by taking a safe route
960 if (activeTraffic.size()) {
961 //while ((i->getId() != id) && i != activeTraffic.end()) {
962 while (i != activeTraffic.end()) {
963 if (i->getId() == id) {
969 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
970 SG_LOG(SG_GENERAL, SG_ALERT,
971 "AI error: requesting ATC instruction for aircraft without traffic record");
973 return i->getInstruction();
975 return FGATCInstruction();
978 void FGStartupController::signOff(int id)
980 TrafficVectorIterator i = activeTraffic.begin();
981 // Search search if the current id alread has an entry
982 // This might be faster using a map instead of a vector, but let's start by taking a safe route
983 if (activeTraffic.size()) {
984 //while ((i->getId() != id) && i != activeTraffic.end()) {
985 while (i != activeTraffic.end()) {
986 if (i->getId() == id) {
992 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
993 SG_LOG(SG_GENERAL, SG_ALERT,
994 "AI error: Aircraft without traffic record is signing off from tower");
996 i = activeTraffic.erase(i);
1000 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
1001 double heading, double speed, double alt,
1004 TrafficVectorIterator i = activeTraffic.begin();
1005 // Search search if the current id has an entry
1006 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1007 TrafficVectorIterator current, closest;
1008 if (activeTraffic.size()) {
1009 //while ((i->getId() != id) && i != activeTraffic.end()) {
1010 while (i != activeTraffic.end()) {
1011 if (i->getId() == id) {
1017 // // update position of the current aircraft
1018 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1019 SG_LOG(SG_GENERAL, SG_ALERT,
1020 "AI error: updating aircraft without traffic record");
1022 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1025 setDt(getDt() + dt);
1027 int state = i->getState();
1029 i->getAircraft()->getTrafficRef()->getDepartureTime();
1030 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1031 //cerr << i->getAircraft()->getTrafficRef()->getCallSign()
1032 // << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1033 // << " at parking " << getGateName(i->getAircraft()) << endl;
1035 if ((now - lastTransmission) > 3 + (rand() % 15)) {
1039 if ((state == 0) && available) {
1040 if (now > startTime) {
1041 //cerr << "Transmitting startup msg" << endl;
1042 transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
1044 lastTransmission = now;
1048 if ((state == 1) && available) {
1049 if (now > startTime + 60) {
1050 transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
1052 lastTransmission = now;
1056 if ((state == 2) && available) {
1057 if (now > startTime + 80) {
1058 transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
1060 lastTransmission = now;
1064 if ((state == 3) && available) {
1065 if (now > startTime + 100) {
1066 transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START,
1069 lastTransmission = now;
1073 // Note: The next four stages are only necessesary when Startup control is
1074 // on a different frequency, compared to ground control
1075 if ((state == 4) && available) {
1076 if (now > startTime + 130) {
1077 transmit(&(*i), MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,
1081 lastTransmission = now;
1085 if ((state == 5) && available) {
1086 if (now > startTime + 140) {
1087 transmit(&(*i), MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND);
1089 lastTransmission = now;
1093 if ((state == 6) && available) {
1094 if (now > startTime + 150) {
1095 transmit(&(*i), MSG_ACKNOWLEDGE_INITIATE_CONTACT,
1098 lastTransmission = now;
1103 // TODO: Switch to APRON control and request pushback Clearance.
1104 // Get Push back clearance
1105 if ((state == 7) && available) {
1106 if (now > startTime + 180) {
1107 transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE,
1110 lastTransmission = now;
1114 if ((state == 8) && available) {
1115 if (now > startTime + 200) {
1116 if (i->pushBackAllowed()) {
1117 i->allowRepeatedTransmissions();
1118 transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE,
1122 transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE,
1124 i->suppressRepeatedTransmissions();
1126 lastTransmission = now;
1130 if ((state == 9) && available) {
1131 i->setHoldPosition(false);
1136 /***************************************************************************
1137 * class FGApproachController
1139 **************************************************************************/
1140 FGApproachController::FGApproachController():
1146 void FGApproachController::announcePosition(int id,
1147 FGAIFlightPlan * intendedRoute,
1148 int currentPosition,
1149 double lat, double lon,
1150 double heading, double speed,
1151 double alt, double radius,
1152 int leg, FGAIAircraft * ref)
1155 TrafficVectorIterator i = activeTraffic.begin();
1156 // Search whether the current id alread has an entry
1157 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1158 if (activeTraffic.size()) {
1159 //while ((i->getId() != id) && i != activeTraffic.end()) {
1160 while (i != activeTraffic.end()) {
1161 if (i->getId() == id) {
1167 // Add a new TrafficRecord if no one exsists for this aircraft.
1168 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1169 FGTrafficRecord rec;
1172 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1173 rec.setRunway(intendedRoute->getRunway());
1175 //rec.setCallSign(callsign);
1176 rec.setAircraft(ref);
1177 activeTraffic.push_back(rec);
1179 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1183 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1184 double heading, double speed, double alt,
1187 TrafficVectorIterator i = activeTraffic.begin();
1188 // Search search if the current id has an entry
1189 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1190 TrafficVectorIterator current, closest;
1191 if (activeTraffic.size()) {
1192 //while ((i->getId() != id) && i != activeTraffic.end()) {
1193 while (i != activeTraffic.end()) {
1194 if (i->getId() == id) {
1200 // // update position of the current aircraft
1201 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1202 SG_LOG(SG_GENERAL, SG_ALERT,
1203 "AI error: updating aircraft without traffic record");
1205 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1207 //cerr << "ApproachController: checking for speed" << endl;
1209 current->getAircraft()->
1210 checkForArrivalTime(string("final001"));
1211 if (time_diff > 15) {
1212 current->setSpeedAdjustment(current->getAircraft()->
1213 getPerformance()->vDescent() *
1215 } else if (time_diff > 5) {
1216 current->setSpeedAdjustment(current->getAircraft()->
1217 getPerformance()->vDescent() *
1219 } else 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() *
1228 current->clearSpeedAdjustment();
1230 //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1232 setDt(getDt() + dt);
1235 void FGApproachController::signOff(int id)
1237 TrafficVectorIterator i = activeTraffic.begin();
1238 // Search search if the current id alread has an entry
1239 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1240 if (activeTraffic.size()) {
1241 //while ((i->getId() != id) && i != activeTraffic.end()) {
1242 while (i != activeTraffic.end()) {
1243 if (i->getId() == id) {
1249 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1250 SG_LOG(SG_GENERAL, SG_ALERT,
1251 "AI error: Aircraft without traffic record is signing off from approach");
1253 i = activeTraffic.erase(i);
1260 bool FGApproachController::hasInstruction(int id)
1262 TrafficVectorIterator i = activeTraffic.begin();
1263 // Search search if the current id has an entry
1264 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1265 if (activeTraffic.size()) {
1266 //while ((i->getId() != id) && i != activeTraffic.end()) {
1267 while (i != activeTraffic.end()) {
1268 if (i->getId() == id) {
1274 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1275 SG_LOG(SG_GENERAL, SG_ALERT,
1276 "AI error: checking ATC instruction for aircraft without traffic record");
1278 return i->hasInstruction();
1284 FGATCInstruction FGApproachController::getInstruction(int id)
1286 TrafficVectorIterator i = activeTraffic.begin();
1287 // Search search if the current id 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: requesting ATC instruction for aircraft without traffic record");
1302 return i->getInstruction();
1304 return FGATCInstruction();
1308 ActiveRunway *FGApproachController::getRunway(string name)
1310 ActiveRunwayVecIterator rwy = activeRunways.begin();
1311 if (activeRunways.size()) {
1312 while (rwy != activeRunways.end()) {
1313 if (rwy->getRunwayName() == name) {
1319 if (rwy == activeRunways.end()) {
1320 ActiveRunway aRwy(name, 0);
1321 activeRunways.push_back(aRwy);
1322 rwy = activeRunways.end() - 1;