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;
455 FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
456 mgr->addController(this);
459 FGATCController::~FGATCController()
461 cerr << "running FGATController destructor" << endl;
464 string FGATCController::getGateName(FGAIAircraft * ref)
466 return ref->atGate();
469 void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId,
472 string sender, receiver;
476 string atisInformation;
484 string transponderCode;
489 sender = rec->getAircraft()->getTrafficRef()->getCallSign();
490 //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
491 switch (rec->getLeg()) {
494 freqId = rec->getNextFrequency();
496 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
497 getDynamics()->getGroundFrequency(rec->getLeg() + freqId);
499 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
500 getDynamics()->getGroundFrequency(3);
502 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
503 getName() + "-Ground";
505 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
506 getDynamics()->getAtisInformation();
510 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
511 getName() + "-Tower";
514 // Swap sender and receiver value in case of a ground to air transmission
515 if (msgDir == ATC_GROUND_TO_AIR) {
521 case MSG_ANNOUNCE_ENGINE_START:
522 text = sender + ". Ready to Start up";
524 case MSG_REQUEST_ENGINE_START:
526 receiver + ", This is " + sender + ". Position " +
527 getGateName(rec->getAircraft()) + ". Information " +
528 atisInformation + ". " +
529 rec->getAircraft()->getTrafficRef()->getFlightRules() +
531 rec->getAircraft()->getTrafficRef()->getArrivalAirport()->
532 getName() + ". Request start-up";
534 // Acknowledge engine startup permission
535 // Assign departure runway
536 // Assign SID, if necessery (TODO)
537 case MSG_PERMIT_ENGINE_START:
538 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
540 heading = rec->getAircraft()->getTrafficRef()->getCourse();
541 fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
543 rec->getAircraft()->GetFlightPlan()->
544 getRunwayClassFromTrafficType(fltType);
546 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
547 getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
549 rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
550 fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
551 getDynamics()->getSID(activeRunway, heading);
552 rec->getAircraft()->GetFlightPlan()->setSID(fp);
554 SID = fp->getName() + " departure";
556 SID = "fly runway heading ";
558 //snprintf(buffer, 7, "%3.2f", heading);
559 fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
560 transponderCode = genTransponderCode(fltRules);
561 rec->getAircraft()->SetTransponderCode(transponderCode);
563 receiver + ". Start-up approved. " + atisInformation +
564 " correct, runway " + activeRunway + ", " + SID + ", squawk " +
565 transponderCode + ". " +
566 "For push-back and taxi clearance call " + taxiFreqStr + ". " +
567 sender + " control.";
569 case MSG_DENY_ENGINE_START:
570 text = receiver + ". Standby";
572 case MSG_ACKNOWLEDGE_ENGINE_START:
573 fp = rec->getAircraft()->GetFlightPlan()->getSID();
576 rec->getAircraft()->GetFlightPlan()->getSID()->getName() +
579 SID = "fly runway heading ";
581 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
582 activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
583 transponderCode = rec->getAircraft()->GetTransponderCode();
585 receiver + ". Start-up approved. " + atisInformation +
586 " correct, runway " + activeRunway + ", " + SID + ", squawk " +
587 transponderCode + ". " +
588 "For push-back and taxi clearance call " + taxiFreqStr + ". " +
591 case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
592 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
593 text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
595 case MSG_INITIATE_CONTACT:
596 text = receiver + ". With you. " + sender;
598 case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
599 text = receiver + ". Roger. " + sender;
601 case MSG_REQUEST_PUSHBACK_CLEARANCE:
602 text = receiver + ". Request push-back. " + sender;
604 case MSG_PERMIT_PUSHBACK_CLEARANCE:
605 text = receiver + ". Push-back approved. " + sender;
607 case MSG_HOLD_PUSHBACK_CLEARANCE:
608 text = receiver + ". Standby. " + sender;
610 case MSG_REQUEST_TAXI_CLEARANCE:
611 text = receiver + ". Ready to Taxi. " + sender;
613 case MSG_ISSUE_TAXI_CLEARANCE:
614 text = receiver + ". Cleared to taxi. " + sender;
616 case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
617 text = receiver + ". Cleared to taxi. " + sender;
619 case MSG_HOLD_POSITION:
620 text = receiver + ". Hold Position. " + sender;
622 case MSG_ACKNOWLEDGE_HOLD_POSITION:
623 text = receiver + ". Holding Position. " + sender;
625 case MSG_RESUME_TAXI:
626 text = receiver + ". Resume Taxiing. " + sender;
628 case MSG_ACKNOWLEDGE_RESUME_TAXI:
629 text = receiver + ". Continuing Taxi. " + sender;
632 text = text + sender + ". Transmitting unknown Message";
635 double onBoardRadioFreq0 =
636 fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
637 double onBoardRadioFreq1 =
638 fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
639 int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
640 int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
641 //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl;
643 // Display ATC message only when one of the radios is tuned
644 // the relevant frequency.
645 // Note that distance attenuation is currently not yet implemented
646 if ((onBoardRadioFreqI0 == stationFreq)
647 || (onBoardRadioFreqI1 == stationFreq)) {
648 if (rec->allowTransmissions()) {
649 fgSetString("/sim/messages/atc", text.c_str());
654 string FGATCController::formatATCFrequency3_2(int freq)
657 snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
658 return string(buffer);
661 // TODO: Set transponder codes according to real-world routes.
662 // The current version just returns a random string of four octal numbers.
663 string FGATCController::genTransponderCode(string fltRules)
665 if (fltRules == "VFR") {
666 return string("1200");
669 snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
671 return string(buffer);
675 /***************************************************************************
676 * class FGTowerController
678 **************************************************************************/
679 FGTowerController::FGTowerController():
685 void FGTowerController::announcePosition(int id,
686 FGAIFlightPlan * intendedRoute,
687 int currentPosition, double lat,
688 double lon, double heading,
689 double speed, double alt,
690 double radius, int leg,
693 TrafficVectorIterator i = activeTraffic.begin();
694 // Search whether the current id alread has an entry
695 // This might be faster using a map instead of a vector, but let's start by taking a safe route
696 if (activeTraffic.size()) {
697 //while ((i->getId() != id) && i != activeTraffic.end()) {
698 while (i != activeTraffic.end()) {
699 if (i->getId() == id) {
705 // Add a new TrafficRecord if no one exsists for this aircraft.
706 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
710 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
711 rec.setRunway(intendedRoute->getRunway());
713 //rec.setCallSign(callsign);
714 rec.setAircraft(ref);
715 activeTraffic.push_back(rec);
717 i->setPositionAndHeading(lat, lon, heading, speed, alt);
721 void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
722 double heading, double speed, double alt,
725 TrafficVectorIterator i = activeTraffic.begin();
726 // Search whether the current id has an entry
727 // This might be faster using a map instead of a vector, but let's start by taking a safe route
728 TrafficVectorIterator current, closest;
729 if (activeTraffic.size()) {
730 //while ((i->getId() != id) && i != activeTraffic.end()) {
731 while (i != activeTraffic.end()) {
732 if (i->getId() == id) {
738 // // update position of the current aircraft
739 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
740 SG_LOG(SG_GENERAL, SG_ALERT,
741 "AI error: updating aircraft without traffic record");
743 i->setPositionAndHeading(lat, lon, heading, speed, alt);
748 // // see if we already have a clearance record for the currently active runway
749 ActiveRunwayVecIterator rwy = activeRunways.begin();
750 // again, a map might be more efficient here
751 if (activeRunways.size()) {
752 //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
753 while (rwy != activeRunways.end()) {
754 if (rwy->getRunwayName() == current->getRunway()) {
760 if (rwy == activeRunways.end()) {
761 ActiveRunway aRwy(current->getRunway(), id);
762 activeRunways.push_back(aRwy); // Since there are no clearance records for this runway yet
763 current->setHoldPosition(false); // Clear the current aircraft to continue
765 // Okay, we have a clearance record for this runway, so check
766 // whether the clearence ID matches that of the current aircraft
767 if (id == rwy->getCleared()) {
768 current->setHoldPosition(false);
770 current->setHoldPosition(true);
776 void FGTowerController::signOff(int id)
778 TrafficVectorIterator i = activeTraffic.begin();
779 // Search search if the current id alread has an entry
780 // This might be faster using a map instead of a vector, but let's start by taking a safe route
781 if (activeTraffic.size()) {
782 //while ((i->getId() != id) && i != activeTraffic.end()) {
783 while (i != activeTraffic.end()) {
784 if (i->getId() == id) {
790 // If this aircraft has left the runway, we can clear the departure record for this runway
791 ActiveRunwayVecIterator rwy = activeRunways.begin();
792 if (activeRunways.size()) {
793 //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
794 while (rwy != activeRunways.end()) {
795 if (rwy->getRunwayName() == i->getRunway()) {
800 if (rwy != activeRunways.end()) {
801 rwy = activeRunways.erase(rwy);
803 SG_LOG(SG_GENERAL, SG_ALERT,
804 "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
807 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
808 SG_LOG(SG_GENERAL, SG_ALERT,
809 "AI error: Aircraft without traffic record is signing off from tower");
811 i = activeTraffic.erase(i);
816 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
817 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
818 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
819 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
820 // Note that this function is probably obsolete
821 bool FGTowerController::hasInstruction(int id)
823 TrafficVectorIterator i = activeTraffic.begin();
824 // Search search if the current id has an entry
825 // This might be faster using a map instead of a vector, but let's start by taking a safe route
826 if (activeTraffic.size()) {
827 //while ((i->getId() != id) && i != activeTraffic.end()) {
828 while (i != activeTraffic.end()) {
829 if (i->getId() == id) {
835 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
836 SG_LOG(SG_GENERAL, SG_ALERT,
837 "AI error: checking ATC instruction for aircraft without traffic record");
839 return i->hasInstruction();
845 FGATCInstruction FGTowerController::getInstruction(int id)
847 TrafficVectorIterator i = activeTraffic.begin();
848 // Search search if the current id has an entry
849 // This might be faster using a map instead of a vector, but let's start by taking a safe route
850 if (activeTraffic.size()) {
851 //while ((i->getId() != id) && i != activeTraffic.end()) {
852 while (i != activeTraffic.end()) {
853 if (i->getId() == id) {
859 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
860 SG_LOG(SG_GENERAL, SG_ALERT,
861 "AI error: requesting ATC instruction for aircraft without traffic record");
863 return i->getInstruction();
865 return FGATCInstruction();
868 /***************************************************************************
869 * class FGStartupController
871 **************************************************************************/
872 FGStartupController::FGStartupController():
877 void FGStartupController::announcePosition(int id,
878 FGAIFlightPlan * intendedRoute,
879 int currentPosition, double lat,
880 double lon, double heading,
881 double speed, double alt,
882 double radius, int leg,
885 TrafficVectorIterator i = activeTraffic.begin();
886 // Search whether the current id alread has an entry
887 // This might be faster using a map instead of a vector, but let's start by taking a safe route
888 if (activeTraffic.size()) {
889 //while ((i->getId() != id) && i != activeTraffic.end()) {
890 while (i != activeTraffic.end()) {
891 if (i->getId() == id) {
897 // Add a new TrafficRecord if no one exsists for this aircraft.
898 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
902 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
903 rec.setRunway(intendedRoute->getRunway());
905 //rec.setCallSign(callsign);
906 rec.setAircraft(ref);
907 rec.setHoldPosition(true);
908 activeTraffic.push_back(rec);
910 i->setPositionAndHeading(lat, lon, heading, speed, alt);
916 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
917 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
918 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
919 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
920 // Note that this function is probably obsolete
921 bool FGStartupController::hasInstruction(int id)
923 TrafficVectorIterator i = activeTraffic.begin();
924 // Search search if the current id has an entry
925 // This might be faster using a map instead of a vector, but let's start by taking a safe route
926 if (activeTraffic.size()) {
927 //while ((i->getId() != id) && i != activeTraffic.end()) {
928 while (i != activeTraffic.end()) {
929 if (i->getId() == id) {
935 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
936 SG_LOG(SG_GENERAL, SG_ALERT,
937 "AI error: checking ATC instruction for aircraft without traffic record");
939 return i->hasInstruction();
945 FGATCInstruction FGStartupController::getInstruction(int id)
947 TrafficVectorIterator i = activeTraffic.begin();
948 // Search search if the current id has an entry
949 // This might be faster using a map instead of a vector, but let's start by taking a safe route
950 if (activeTraffic.size()) {
951 //while ((i->getId() != id) && i != activeTraffic.end()) {
952 while (i != activeTraffic.end()) {
953 if (i->getId() == id) {
959 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
960 SG_LOG(SG_GENERAL, SG_ALERT,
961 "AI error: requesting ATC instruction for aircraft without traffic record");
963 return i->getInstruction();
965 return FGATCInstruction();
968 void FGStartupController::signOff(int id)
970 TrafficVectorIterator i = activeTraffic.begin();
971 // Search search if the current id alread has an entry
972 // This might be faster using a map instead of a vector, but let's start by taking a safe route
973 if (activeTraffic.size()) {
974 //while ((i->getId() != id) && i != activeTraffic.end()) {
975 while (i != activeTraffic.end()) {
976 if (i->getId() == id) {
982 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
983 SG_LOG(SG_GENERAL, SG_ALERT,
984 "AI error: Aircraft without traffic record is signing off from tower");
986 i = activeTraffic.erase(i);
990 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
991 double heading, double speed, double alt,
994 TrafficVectorIterator i = activeTraffic.begin();
995 // Search search if the current id has an entry
996 // This might be faster using a map instead of a vector, but let's start by taking a safe route
997 TrafficVectorIterator current, closest;
998 if (activeTraffic.size()) {
999 //while ((i->getId() != id) && i != activeTraffic.end()) {
1000 while (i != activeTraffic.end()) {
1001 if (i->getId() == id) {
1007 // // update position of the current aircraft
1008 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1009 SG_LOG(SG_GENERAL, SG_ALERT,
1010 "AI error: updating aircraft without traffic record");
1012 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1015 setDt(getDt() + dt);
1017 int state = i->getState();
1019 i->getAircraft()->getTrafficRef()->getDepartureTime();
1020 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
1021 //cerr << i->getAircraft()->getTrafficRef()->getCallSign()
1022 // << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1023 // << " at parking " << getGateName(i->getAircraft()) << endl;
1025 if ((now - lastTransmission) > 3 + (rand() % 15)) {
1029 if ((state == 0) && available) {
1030 if (now > startTime) {
1031 //cerr << "Transmitting startup msg" << endl;
1032 transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
1034 lastTransmission = now;
1038 if ((state == 1) && available) {
1039 if (now > startTime + 60) {
1040 transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
1042 lastTransmission = now;
1046 if ((state == 2) && available) {
1047 if (now > startTime + 80) {
1048 transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
1050 lastTransmission = now;
1054 if ((state == 3) && available) {
1055 if (now > startTime + 100) {
1056 transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START,
1059 lastTransmission = now;
1063 // Note: The next four stages are only necessesary when Startup control is
1064 // on a different frequency, compared to ground control
1065 if ((state == 4) && available) {
1066 if (now > startTime + 130) {
1067 transmit(&(*i), MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,
1071 lastTransmission = now;
1075 if ((state == 5) && available) {
1076 if (now > startTime + 140) {
1077 transmit(&(*i), MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND);
1079 lastTransmission = now;
1083 if ((state == 6) && available) {
1084 if (now > startTime + 150) {
1085 transmit(&(*i), MSG_ACKNOWLEDGE_INITIATE_CONTACT,
1088 lastTransmission = now;
1093 // TODO: Switch to APRON control and request pushback Clearance.
1094 // Get Push back clearance
1095 if ((state == 7) && available) {
1096 if (now > startTime + 180) {
1097 transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE,
1100 lastTransmission = now;
1104 if ((state == 8) && available) {
1105 if (now > startTime + 200) {
1106 if (i->pushBackAllowed()) {
1107 i->allowRepeatedTransmissions();
1108 transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE,
1112 transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE,
1114 i->suppressRepeatedTransmissions();
1116 lastTransmission = now;
1120 if ((state == 9) && available) {
1121 i->setHoldPosition(false);
1126 /***************************************************************************
1127 * class FGApproachController
1129 **************************************************************************/
1130 FGApproachController::FGApproachController():
1136 void FGApproachController::announcePosition(int id,
1137 FGAIFlightPlan * intendedRoute,
1138 int currentPosition,
1139 double lat, double lon,
1140 double heading, double speed,
1141 double alt, double radius,
1142 int leg, FGAIAircraft * ref)
1144 TrafficVectorIterator i = activeTraffic.begin();
1145 // Search whether the current id alread has an entry
1146 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1147 if (activeTraffic.size()) {
1148 //while ((i->getId() != id) && i != activeTraffic.end()) {
1149 while (i != activeTraffic.end()) {
1150 if (i->getId() == id) {
1156 // Add a new TrafficRecord if no one exsists for this aircraft.
1157 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1158 FGTrafficRecord rec;
1161 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1162 rec.setRunway(intendedRoute->getRunway());
1164 //rec.setCallSign(callsign);
1165 rec.setAircraft(ref);
1166 activeTraffic.push_back(rec);
1168 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1172 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1173 double heading, double speed, double alt,
1176 TrafficVectorIterator i = activeTraffic.begin();
1177 // Search search if the current id has an entry
1178 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1179 TrafficVectorIterator current, closest;
1180 if (activeTraffic.size()) {
1181 //while ((i->getId() != id) && i != activeTraffic.end()) {
1182 while (i != activeTraffic.end()) {
1183 if (i->getId() == id) {
1189 // // update position of the current aircraft
1190 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1191 SG_LOG(SG_GENERAL, SG_ALERT,
1192 "AI error: updating aircraft without traffic record");
1194 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1196 //cerr << "ApproachController: checking for speed" << endl;
1198 current->getAircraft()->
1199 checkForArrivalTime(string("final001"));
1200 if (time_diff > 15) {
1201 current->setSpeedAdjustment(current->getAircraft()->
1202 getPerformance()->vDescent() *
1204 } else if (time_diff > 5) {
1205 current->setSpeedAdjustment(current->getAircraft()->
1206 getPerformance()->vDescent() *
1208 } else if (time_diff < -15) {
1209 current->setSpeedAdjustment(current->getAircraft()->
1210 getPerformance()->vDescent() *
1212 } else if (time_diff < -5) {
1213 current->setSpeedAdjustment(current->getAircraft()->
1214 getPerformance()->vDescent() *
1217 current->clearSpeedAdjustment();
1219 //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1221 setDt(getDt() + dt);
1224 void FGApproachController::signOff(int id)
1226 TrafficVectorIterator i = activeTraffic.begin();
1227 // Search search if the current id alread has an entry
1228 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1229 if (activeTraffic.size()) {
1230 //while ((i->getId() != id) && i != activeTraffic.end()) {
1231 while (i != activeTraffic.end()) {
1232 if (i->getId() == id) {
1238 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1239 SG_LOG(SG_GENERAL, SG_ALERT,
1240 "AI error: Aircraft without traffic record is signing off from approach");
1242 i = activeTraffic.erase(i);
1249 bool FGApproachController::hasInstruction(int id)
1251 TrafficVectorIterator i = activeTraffic.begin();
1252 // Search search if the current id has an entry
1253 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1254 if (activeTraffic.size()) {
1255 //while ((i->getId() != id) && i != activeTraffic.end()) {
1256 while (i != activeTraffic.end()) {
1257 if (i->getId() == id) {
1263 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1264 SG_LOG(SG_GENERAL, SG_ALERT,
1265 "AI error: checking ATC instruction for aircraft without traffic record");
1267 return i->hasInstruction();
1273 FGATCInstruction FGApproachController::getInstruction(int id)
1275 TrafficVectorIterator i = activeTraffic.begin();
1276 // Search search if the current id has an entry
1277 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1278 if (activeTraffic.size()) {
1279 //while ((i->getId() != id) && i != activeTraffic.end()) {
1280 while (i != activeTraffic.end()) {
1281 if (i->getId() == id) {
1287 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1288 SG_LOG(SG_GENERAL, SG_ALERT,
1289 "AI error: requesting ATC instruction for aircraft without traffic record");
1291 return i->getInstruction();
1293 return FGATCInstruction();
1297 ActiveRunway *FGApproachController::getRunway(string name)
1299 ActiveRunwayVecIterator rwy = activeRunways.begin();
1300 if (activeRunways.size()) {
1301 while (rwy != activeRunways.end()) {
1302 if (rwy->getRunwayName() == name) {
1308 if (rwy == activeRunways.end()) {
1309 ActiveRunway aRwy(name, 0);
1310 activeRunways.push_back(aRwy);
1311 rwy = activeRunways.end() - 1;