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.
31 #include <osg/Geometry>
32 #include <osg/MatrixTransform>
35 #include <simgear/scene/material/EffectGeode.hxx>
36 #include <simgear/scene/material/matlib.hxx>
37 #include <simgear/scene/material/mat.hxx>
38 #include <simgear/scene/util/OsgMath.hxx>
39 #include <simgear/timing/sg_time.hxx>
41 #include <Scenery/scenery.hxx>
43 #include "trafficcontrol.hxx"
44 #include "atc_mgr.hxx"
45 #include <AIModel/AIAircraft.hxx>
46 #include <AIModel/AIFlightPlan.hxx>
47 #include <AIModel/performancedata.hxx>
48 #include <ATC/atc_mgr.hxx>
49 #include <Traffic/TrafficMgr.hxx>
50 #include <Airports/groundnetwork.hxx>
51 #include <Airports/dynamics.hxx>
52 #include <Airports/airport.hxx>
53 #include <Radio/radio.hxx>
61 /***************************************************************************
63 **************************************************************************/
64 time_t ActiveRunway::requestTimeSlot(time_t eta)
67 time_t separation = 90;
69 if (estimatedArrivalTimes.empty()) {
70 estimatedArrivalTimes.push_back(eta);
73 TimeVectorIterator i = estimatedArrivalTimes.begin();
74 //cerr << "Checking eta slots " << eta << ": " << endl;
75 for (i = estimatedArrivalTimes.begin();
76 i != estimatedArrivalTimes.end(); i++) {
77 //cerr << "Stored time : " << (*i) << endl;
79 i = estimatedArrivalTimes.begin();
80 if ((eta + separation) < (*i)) {
83 //cerr << "Storing at beginning" << endl;
85 while ((i != estimatedArrivalTimes.end()) && (!found)) {
86 TimeVectorIterator j = i + 1;
87 if (j == estimatedArrivalTimes.end()) {
88 if (((*i) + separation) < eta) {
89 //cerr << "Storing at end" << endl;
92 newEta = (*i) + separation;
93 //cerr << "Storing at end + separation" << endl;
96 if ((((*j) - (*i)) > (separation * 2))) { // found a potential slot
97 // now check whether this slot is usable:
98 // 1) eta should fall between the two points
99 // i.e. eta > i AND eta < j
101 //cerr << "Found potential slot after " << (*i) << endl;
102 if (eta > (*i) && (eta < (*j))) {
104 if (eta < ((*i) + separation)) {
105 newEta = (*i) + separation;
106 //cerr << "Using original" << (*i) << " + separation " << endl;
109 //cerr << "Using original after " << (*i) << endl;
111 } else if (eta < (*i)) {
113 newEta = (*i) + separation;
114 //cerr << "Using delayed slot after " << (*i) << endl;
117 if (((*j) - separation) < eta) {
119 if (((*i) + separation) < eta) {
121 cerr << "Using original after " << (*i) << endl;
123 newEta = (*i) + separation;
124 cerr << "Using " << (*i) << " + separation " << endl;
132 //cerr << ". done. New ETA : " << newEta << endl;
134 estimatedArrivalTimes.push_back(newEta);
135 sort(estimatedArrivalTimes.begin(), estimatedArrivalTimes.end());
136 // do some housekeeping : remove any timestamps that are past
138 time_t now = globals->get_time_params()->get_cur_time();
140 TimeVectorIterator i = estimatedArrivalTimes.begin();
141 while (i != estimatedArrivalTimes.end()) {
143 //cerr << "Deleting timestamp " << (*i) << " (now = " << now << "). " << endl;
144 estimatedArrivalTimes.erase(i);
145 i = estimatedArrivalTimes.begin();
153 void ActiveRunway::printDepartureCue()
155 cout << "Departure cue for " << rwy << ": " << endl;
156 for (AircraftVecIterator atc = departureCue.begin(); atc != departureCue.end(); atc++) {
157 cout << " " << (*atc)->getCallSign() << " " << (*atc)->getTakeOffStatus();
158 cout << " " << (*atc)->_getLatitude() << " " << (*atc)->_getLongitude() << (*atc)-> getSpeed() << " " << (*atc)->getAltitude() << endl;
163 FGAIAircraft* ActiveRunway::getFirstOfStatus(int stat)
165 for (AircraftVecIterator atc =departureCue.begin(); atc != departureCue.end(); atc++) {
166 if ((*atc)->getTakeOffStatus() == stat)
174 /***************************************************************************
176 **************************************************************************/
177 FGTrafficRecord::FGTrafficRecord():
178 id(0), waitsForId(0),
183 allowTransmission(true),
187 latitude(0), longitude(0), heading(0), speed(0), altitude(0), radius(0)
191 FGTrafficRecord::~FGTrafficRecord()
194 aircraft->clearATCController();
198 void FGTrafficRecord::setPositionAndIntentions(int pos,
199 FGAIFlightPlan * route)
203 if (! intentions.empty()) {
204 intVecIterator i = intentions.begin();
206 SG_LOG(SG_ATC, SG_ALERT,
207 "Error in FGTrafficRecord::setPositionAndIntentions at " << SG_ORIGIN);
211 //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
212 int size = route->getNrOfWayPoints();
213 //cerr << "Setting pos" << pos << " ";
214 //cerr << "setting intentions ";
215 for (int i = 2; i < size; i++) {
216 int val = route->getRouteIndex(i);
217 intentions.push_back(val);
222 void FGTrafficRecord::setAircraft(FGAIAircraft *ref)
227 FGAIAircraft* FGTrafficRecord::getAircraft() const
229 return aircraft.ptr();
233 * Check if another aircraft is ahead of the current one, and on the same
234 * return true / false is the is/isn't the case.
236 ****************************************************************************/
238 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other)
241 //cerr << "Start check 1" << endl;
242 if (currentPos == other.currentPos) {
243 //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
246 // else if (! other.intentions.empty())
248 // cerr << "Start check 2" << endl;
249 // intVecIterator i = other.intentions.begin();
250 // while (!((i == other.intentions.end()) || ((*i) == currentPos)))
252 // if (i != other.intentions.end()) {
253 // cerr << "Check Position and intentions: current matches other.intentions" << endl;
256 else if (! intentions.empty()) {
257 //cerr << "Start check 3" << endl;
258 intVecIterator i = intentions.begin();
259 //while (!((i == intentions.end()) || ((*i) == other.currentPos)))
260 while (i != intentions.end()) {
261 if ((*i) == other.currentPos) {
266 if (i != intentions.end()) {
267 //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
271 //cerr << "Done !!" << endl;
275 void FGTrafficRecord::setPositionAndHeading(double lat, double lon,
276 double hdg, double spd,
286 int FGTrafficRecord::crosses(FGGroundNetwork * net,
287 FGTrafficRecord & other)
289 if (checkPositionAndIntentions(other)
290 || (other.checkPositionAndIntentions(*this)))
293 int currentTargetNode = 0, otherTargetNode = 0;
295 currentTargetNode = net->findSegment(currentPos)->getEnd()->getIndex(); // OKAY,...
296 if (other.currentPos > 0)
297 otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,...
298 if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
299 return currentTargetNode;
300 if (! intentions.empty()) {
301 for (i = intentions.begin(); i != intentions.end(); i++) {
303 if (currentTargetNode ==
304 net->findSegment(*i)->getEnd()->getIndex()) {
305 //cerr << "Current crosses at " << currentTargetNode <<endl;
306 return currentTargetNode;
311 if (! other.intentions.empty()) {
312 for (i = other.intentions.begin(); i != other.intentions.end();
315 if (otherTargetNode ==
316 net->findSegment(*i)->getEnd()->getIndex()) {
317 //cerr << "Other crosses at " << currentTargetNode <<endl;
318 return otherTargetNode;
323 if (! intentions.empty() && ! other.intentions.empty()) {
324 for (i = intentions.begin(); i != intentions.end(); i++) {
325 for (j = other.intentions.begin(); j != other.intentions.end();
327 //cerr << "finding segment " << *i << " and " << *j << endl;
328 if (((*i) > 0) && ((*j) > 0)) {
330 net->findSegment(*i)->getEnd()->getIndex();
332 net->findSegment(*j)->getEnd()->getIndex();
333 if (currentTargetNode == otherTargetNode) {
334 //cerr << "Routes will cross at " << currentTargetNode << endl;
335 return currentTargetNode;
344 bool FGTrafficRecord::onRoute(FGGroundNetwork * net,
345 FGTrafficRecord & other)
347 int node = -1, othernode = -1;
349 node = net->findSegment(currentPos)->getEnd()->getIndex();
350 if (other.currentPos > 0)
352 net->findSegment(other.currentPos)->getEnd()->getIndex();
353 if ((node == othernode) && (node != -1))
355 if (! other.intentions.empty()) {
356 for (intVecIterator i = other.intentions.begin();
357 i != other.intentions.end(); i++) {
359 othernode = net->findSegment(*i)->getEnd()->getIndex();
360 if ((node == othernode) && (node > -1))
365 //if (other.currentPos > 0)
366 // othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
367 //if (! intentions.empty())
369 // for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
373 // node = net->findSegment(*i)->getEnd()->getIndex();
374 // if ((node == othernode) && (node > -1))
383 bool FGTrafficRecord::isOpposing(FGGroundNetwork * net,
384 FGTrafficRecord & other, int node)
386 // Check if current segment is the reverse segment for the other aircraft
388 //cerr << "Current segment " << currentPos << endl;
389 if ((currentPos > 0) && (other.currentPos > 0)) {
390 opp = net->findSegment(currentPos)->opposite();
392 if (opp->getIndex() == other.currentPos)
396 for (intVecIterator i = intentions.begin(); i != intentions.end();
398 if ((opp = net->findSegment(other.currentPos)->opposite())) {
400 if (opp->getIndex() ==
401 net->findSegment(*i)->getIndex()) {
402 if (net->findSegment(*i)->getStart()->getIndex() ==
405 //cerr << "Found the node " << node << endl;
411 if (! other.intentions.empty()) {
412 for (intVecIterator j = other.intentions.begin();
413 j != other.intentions.end(); j++) {
414 // cerr << "Current segment 1 " << (*i) << endl;
416 if ((opp = net->findSegment(*i)->opposite())) {
417 if (opp->getIndex() ==
418 net->findSegment(*j)->getIndex()) {
419 //cerr << "Nodes " << net->findSegment(*i)->getIndex()
420 // << " and " << net->findSegment(*j)->getIndex()
421 // << " are opposites " << endl;
422 if (net->findSegment(*i)->getStart()->
423 getIndex() == node) {
425 //cerr << "Found the node " << node << endl;
439 bool FGTrafficRecord::isActive(int margin) const
441 time_t now = globals->get_time_params()->get_cur_time();
442 time_t deptime = aircraft->getTrafficRef()->getDepartureTime();
443 return ((now + margin) > deptime);
447 void FGTrafficRecord::setSpeedAdjustment(double spd)
449 instruction.setChangeSpeed(true);
450 instruction.setSpeed(spd);
453 void FGTrafficRecord::setHeadingAdjustment(double heading)
455 instruction.setChangeHeading(true);
456 instruction.setHeading(heading);
459 bool FGTrafficRecord::pushBackAllowed() const
461 return allowPushback;
467 /***************************************************************************
470 **************************************************************************/
471 FGATCInstruction::FGATCInstruction()
474 holdPosition = false;
476 changeHeading = false;
477 changeAltitude = false;
478 resolveCircularWait = false;
486 bool FGATCInstruction::hasInstruction() const
488 return (holdPattern || holdPosition || changeSpeed || changeHeading
489 || changeAltitude || resolveCircularWait);
492 /***************************************************************************
495 **************************************************************************/
500 FGATCController::FGATCController()
502 //cerr << "running FGATController constructor" << endl;
505 lastTransmission = 0;
507 lastTransmissionDirection = ATC_AIR_TO_GROUND;
511 FGATCController::~FGATCController()
513 FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
514 mgr->removeController(this);
517 string FGATCController::getGateName(FGAIAircraft * ref)
519 return ref->atGate();
522 bool FGATCController::isUserAircraft(FGAIAircraft* ac)
524 return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false;
527 void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, AtcMsgId msgId,
528 AtcMsgDir msgDir, bool audible)
530 string sender, receiver;
535 string atisInformation;
544 string transponderCode;
547 string instructionText;
551 sender = rec->getAircraft()->getTrafficRef()->getCallSign();
552 if (rec->getAircraft()->getTaxiClearanceRequest()) {
553 instructionText = "push-back and taxi";
555 instructionText = "taxi";
557 //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
558 switch (rec->getLeg()) {
561 freqId = rec->getNextFrequency();
563 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
564 getDynamics()->getGroundFrequency(rec->getLeg() + freqId);
566 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
567 getDynamics()->getGroundFrequency(2);
569 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
570 getDynamics()->getTowerFrequency(2);
572 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
573 getName() + "-Ground";
575 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
576 getDynamics()->getAtisSequence();
580 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
581 getName() + "-Tower";
584 // Swap sender and receiver value in case of a ground to air transmission
585 if (msgDir == ATC_GROUND_TO_AIR) {
592 case MSG_ANNOUNCE_ENGINE_START:
593 text = sender + ". Ready to Start up";
595 case MSG_REQUEST_ENGINE_START:
597 receiver + ", This is " + sender + ". Position " +
598 getGateName(rec->getAircraft()) + ". Information " +
599 atisInformation + ". " +
600 rec->getAircraft()->getTrafficRef()->getFlightRules() +
602 rec->getAircraft()->getTrafficRef()->getArrivalAirport()->
603 getName() + ". Request start-up";
605 // Acknowledge engine startup permission
606 // Assign departure runway
607 // Assign SID, if necessery (TODO)
608 case MSG_PERMIT_ENGINE_START:
609 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
611 heading = rec->getAircraft()->getTrafficRef()->getCourse();
612 fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
614 rec->getAircraft()->GetFlightPlan()->
615 getRunwayClassFromTrafficType(fltType);
617 rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
618 getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
620 rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
622 rec->getAircraft()->GetFlightPlan()->setSID(fp);
624 SID = fp->getName() + " departure";
626 SID = "fly runway heading ";
628 //snprintf(buffer, 7, "%3.2f", heading);
629 fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
630 transponderCode = genTransponderCode(fltRules);
631 rec->getAircraft()->SetTransponderCode(transponderCode);
633 receiver + ". Start-up approved. " + atisInformation +
634 " correct, runway " + activeRunway + ", " + SID + ", squawk " +
635 transponderCode + ". " +
636 "For "+ instructionText + " clearance call " + taxiFreqStr + ". " +
637 sender + " control.";
639 case MSG_DENY_ENGINE_START:
640 text = receiver + ". Standby";
642 case MSG_ACKNOWLEDGE_ENGINE_START:
643 fp = rec->getAircraft()->GetFlightPlan()->getSID();
646 rec->getAircraft()->GetFlightPlan()->getSID()->getName() +
649 SID = "fly runway heading ";
651 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
652 activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
653 transponderCode = rec->getAircraft()->GetTransponderCode();
656 receiver + ". Start-up approved. " + atisInformation +
657 " correct, runway " + activeRunway + ", " + SID + ", squawk " +
658 transponderCode + ". " +
659 "For " + instructionText + " clearance call " + taxiFreqStr + ". " +
662 case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
663 taxiFreqStr = formatATCFrequency3_2(taxiFreq);
664 text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
666 case MSG_INITIATE_CONTACT:
667 text = receiver + ". With you. " + sender;
669 case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
670 text = receiver + ". Roger. " + sender;
672 case MSG_REQUEST_PUSHBACK_CLEARANCE:
673 if (rec->getAircraft()->getTaxiClearanceRequest()) {
674 text = receiver + ". Request push-back. " + sender;
676 text = receiver + ". Request Taxi clearance. " + sender;
679 case MSG_PERMIT_PUSHBACK_CLEARANCE:
680 if (rec->getAircraft()->getTaxiClearanceRequest()) {
681 text = receiver + ". Push-back approved. " + sender;
683 text = receiver + ". Cleared to Taxi." + sender;
686 case MSG_HOLD_PUSHBACK_CLEARANCE:
687 text = receiver + ". Standby. " + sender;
689 case MSG_REQUEST_TAXI_CLEARANCE:
690 text = receiver + ". Ready to Taxi. " + sender;
692 case MSG_ISSUE_TAXI_CLEARANCE:
693 text = receiver + ". Cleared to taxi. " + sender;
695 case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
696 text = receiver + ". Cleared to taxi. " + sender;
698 case MSG_HOLD_POSITION:
699 text = receiver + ". Hold Position. " + sender;
701 case MSG_ACKNOWLEDGE_HOLD_POSITION:
702 text = receiver + ". Holding Position. " + sender;
704 case MSG_RESUME_TAXI:
705 text = receiver + ". Resume Taxiing. " + sender;
707 case MSG_ACKNOWLEDGE_RESUME_TAXI:
708 text = receiver + ". Continuing Taxi. " + sender;
710 case MSG_REPORT_RUNWAY_HOLD_SHORT:
711 activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
712 //activeRunway = "test";
713 text = receiver + ". Holding short runway "
717 //cerr << "1 Currently at leg " << rec->getLeg() << endl;
719 case MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT:
720 activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
721 text = receiver + "Roger. Holding short runway "
725 //cerr << "2 Currently at leg " << rec->getLeg() << endl;
727 case MSG_SWITCH_TOWER_FREQUENCY:
728 towerFreqStr = formatATCFrequency3_2(towerFreq);
729 text = receiver + "Contact Tower at " + towerFreqStr + ". " + sender;
731 //cerr << "3 Currently at leg " << rec->getLeg() << endl;
733 case MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY:
734 towerFreqStr = formatATCFrequency3_2(towerFreq);
735 text = receiver + "Roger, switching to tower at " + towerFreqStr + ". " + sender;
737 //cerr << "4 Currently at leg " << rec->getLeg() << endl;
741 text = text + sender + ". Transmitting unknown Message";
745 double onBoardRadioFreq0 =
746 fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
747 double onBoardRadioFreq1 =
748 fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
749 int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
750 int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
751 //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl;
753 // Display ATC message only when one of the radios is tuned
754 // the relevant frequency.
755 // Note that distance attenuation is currently not yet implemented
757 if ((stationFreq > 0)&&
758 ((onBoardRadioFreqI0 == stationFreq)||
759 (onBoardRadioFreqI1 == stationFreq))) {
760 if (rec->allowTransmissions()) {
762 if( fgGetBool( "/sim/radio/use-itm-attenuation", false ) ) {
763 //cerr << "Using ITM radio propagation" << endl;
764 FGRadioTransmission* radio = new FGRadioTransmission();
766 double sender_alt_ft, sender_alt;
768 sender_pos = parent->parent()->geod();
771 sender_alt_ft = rec->getAltitude();
772 sender_alt = sender_alt_ft * SG_FEET_TO_METER;
773 sender_pos= SGGeod::fromDegM( rec->getLongitude(),
774 rec->getLatitude(), sender_alt );
776 double frequency = ((double)stationFreq) / 100;
777 radio->receiveATC(sender_pos, frequency, text, ground_to_air);
781 fgSetString("/sim/messages/atc", text.c_str());
786 FGATCDialogNew::instance()->addEntry(1, text);
791 string FGATCController::formatATCFrequency3_2(int freq)
794 snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
795 return string(buffer);
798 // TODO: Set transponder codes according to real-world routes.
799 // The current version just returns a random string of four octal numbers.
800 string FGATCController::genTransponderCode(const string& fltRules)
802 if (fltRules == "VFR") {
803 return string("1200");
806 snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
808 return string(buffer);
812 void FGATCController::init()
815 FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
816 mgr->addController(this);
821 /***************************************************************************
822 * class FGTowerController
824 **************************************************************************/
825 FGTowerController::FGTowerController(FGAirportDynamics *par) :
832 void FGTowerController::announcePosition(int id,
833 FGAIFlightPlan * intendedRoute,
834 int currentPosition, double lat,
835 double lon, double heading,
836 double speed, double alt,
837 double radius, int leg,
841 TrafficVectorIterator i = activeTraffic.begin();
842 // Search whether the current id alread has an entry
843 // This might be faster using a map instead of a vector, but let's start by taking a safe route
844 if (! activeTraffic.empty()) {
845 //while ((i->getId() != id) && i != activeTraffic.end()) {
846 while (i != activeTraffic.end()) {
847 if (i->getId() == id) {
853 // Add a new TrafficRecord if no one exsists for this aircraft.
854 if (i == activeTraffic.end() || (activeTraffic.empty())) {
858 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
859 rec.setRunway(intendedRoute->getRunway());
861 //rec.setCallSign(callsign);
862 rec.setRadius(radius);
863 rec.setAircraft(ref);
864 activeTraffic.push_back(rec);
865 // Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway.
866 ActiveRunwayVecIterator rwy = activeRunways.begin();
867 if (! activeRunways.empty()) {
868 while (rwy != activeRunways.end()) {
869 if (rwy->getRunwayName() == intendedRoute->getRunway()) {
875 if (rwy == activeRunways.end()) {
876 ActiveRunway aRwy(intendedRoute->getRunway(), id);
877 aRwy.addToDepartureCue(ref);
878 activeRunways.push_back(aRwy);
879 rwy = (activeRunways.end()-1);
881 rwy->addToDepartureCue(ref);
884 //cerr << ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getDepartureCueSize() << " for takeoff " << endl;
886 i->setPositionAndHeading(lat, lon, heading, speed, alt);
890 void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
891 double heading, double speed, double alt,
894 TrafficVectorIterator i = activeTraffic.begin();
895 // Search whether the current id has an entry
896 // This might be faster using a map instead of a vector, but let's start by taking a safe route
897 TrafficVectorIterator current, closest;
898 if (! activeTraffic.empty()) {
899 //while ((i->getId() != id) && i != activeTraffic.end()) {
900 while (i != activeTraffic.end()) {
901 if (i->getId() == id) {
907 // // update position of the current aircraft
908 if (i == activeTraffic.end() || (activeTraffic.empty())) {
909 SG_LOG(SG_ATC, SG_ALERT,
910 "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
912 i->setPositionAndHeading(lat, lon, heading, speed, alt);
917 // see if we already have a clearance record for the currently active runway
918 // NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is
919 // already exists here. So, we can simplify the current code.
921 ActiveRunwayVecIterator rwy = activeRunways.begin();
922 //if (parent->getId() == fgGetString("/sim/presets/airport-id")) {
923 // for (rwy = activeRunways.begin(); rwy != activeRunways.end(); rwy++) {
924 // rwy->printDepartureCue();
928 rwy = activeRunways.begin();
929 while (rwy != activeRunways.end()) {
930 if (rwy->getRunwayName() == current->getRunway()) {
936 // only bother running the following code if the current aircraft is the
937 // first in line for depature
938 /* if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
939 if (rwy->getCleared()) {
940 if (id == rwy->getCleared()) {
941 current->setHoldPosition(false);
943 current->setHoldPosition(true);
946 // For now. At later stages, this will probably be the place to check for inbound traffc.
950 // only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control
951 FGAIAircraft* ac= rwy->getFirstAircraftInDepartureCue();
952 if (ac->getTakeOffStatus() == 1) {
953 ac->setTakeOffStatus(2);
955 if (current->getAircraft()->getTakeOffStatus() == 2) {
956 current -> setHoldPosition(false);
958 current->setHoldPosition(true);
960 int clearanceId = rwy->getCleared();
962 if (id == clearanceId) {
963 current->setHoldPosition(false);
966 if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
968 FGAIAircraft *ac = rwy->getFirstOfStatus(1);
970 ac->setTakeOffStatus(2);
976 void FGTowerController::signOff(int id)
978 TrafficVectorIterator i = activeTraffic.begin();
979 // Search search if the current id alread has an entry
980 // This might be faster using a map instead of a vector, but let's start by taking a safe route
981 if (! activeTraffic.empty()) {
982 //while ((i->getId() != id) && i != activeTraffic.end()) {
983 while (i != activeTraffic.end()) {
984 if (i->getId() == id) {
990 // If this aircraft has left the runway, we can clear the departure record for this runway
991 ActiveRunwayVecIterator rwy = activeRunways.begin();
992 if (! activeRunways.empty()) {
993 //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
994 while (rwy != activeRunways.end()) {
995 if (rwy->getRunwayName() == i->getRunway()) {
1000 if (rwy != activeRunways.end()) {
1002 rwy->updateDepartureCue();
1004 SG_LOG(SG_ATC, SG_ALERT,
1005 "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff at " << SG_ORIGIN);
1008 if (i == activeTraffic.end() || (activeTraffic.empty())) {
1009 SG_LOG(SG_ATC, SG_ALERT,
1010 "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN);
1012 i->getAircraft()->resetTakeOffStatus();
1013 i = activeTraffic.erase(i);
1014 //cerr << "Signing off from tower controller" << endl;
1019 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
1020 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
1021 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1022 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1023 // Note that this function is probably obsolete
1024 bool FGTowerController::hasInstruction(int id)
1026 TrafficVectorIterator i = activeTraffic.begin();
1027 // Search search if the current id has an entry
1028 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1029 if (! activeTraffic.empty()) {
1030 //while ((i->getId() != id) && i != activeTraffic.end()) {
1031 while (i != activeTraffic.end()) {
1032 if (i->getId() == id) {
1038 if (i == activeTraffic.end() || activeTraffic.empty()) {
1039 SG_LOG(SG_ATC, SG_ALERT,
1040 "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1042 return i->hasInstruction();
1048 FGATCInstruction FGTowerController::getInstruction(int id)
1050 TrafficVectorIterator i = activeTraffic.begin();
1051 // Search search if the current id has an entry
1052 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1053 if (! activeTraffic.empty()) {
1054 //while ((i->getId() != id) && i != activeTraffic.end()) {
1055 while (i != activeTraffic.end()) {
1056 if (i->getId() == id) {
1062 if (i == activeTraffic.end() || activeTraffic.empty()) {
1063 SG_LOG(SG_ATC, SG_ALERT,
1064 "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1066 return i->getInstruction();
1068 return FGATCInstruction();
1071 void FGTowerController::render(bool visible) {
1072 //std::cerr << "FGTowerController::render function not yet implemented" << std::endl;
1075 string FGTowerController::getName() {
1076 return string(parent->getId() + "-tower");
1079 void FGTowerController::update(double dt)
1086 /***************************************************************************
1087 * class FGStartupController
1089 **************************************************************************/
1090 FGStartupController::FGStartupController(FGAirportDynamics *par):
1096 void FGStartupController::announcePosition(int id,
1097 FGAIFlightPlan * intendedRoute,
1098 int currentPosition, double lat,
1099 double lon, double heading,
1100 double speed, double alt,
1101 double radius, int leg,
1105 TrafficVectorIterator i = activeTraffic.begin();
1106 // Search whether the current id alread has an entry
1107 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1108 if (! activeTraffic.empty()) {
1109 //while ((i->getId() != id) && i != activeTraffic.end()) {
1110 while (i != activeTraffic.end()) {
1111 if (i->getId() == id) {
1117 // Add a new TrafficRecord if no one exsists for this aircraft.
1118 if (i == activeTraffic.end() || activeTraffic.empty()) {
1119 FGTrafficRecord rec;
1122 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1123 rec.setRunway(intendedRoute->getRunway());
1125 rec.setPositionAndIntentions(currentPosition, intendedRoute);
1126 //rec.setCallSign(callsign);
1127 rec.setAircraft(ref);
1128 rec.setHoldPosition(true);
1129 activeTraffic.push_back(rec);
1131 i->setPositionAndIntentions(currentPosition, intendedRoute);
1132 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1138 // IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
1139 // THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
1140 // BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
1141 // WHICH WOULD SIMPLIFY CODE MAINTENANCE.
1142 // Note that this function is probably obsolete
1143 bool FGStartupController::hasInstruction(int id)
1145 TrafficVectorIterator i = activeTraffic.begin();
1146 // Search search if the current id has an entry
1147 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1148 if (! activeTraffic.empty()) {
1149 //while ((i->getId() != id) && i != activeTraffic.end()) {
1150 while (i != activeTraffic.end()) {
1151 if (i->getId() == id) {
1157 if (i == activeTraffic.end() || activeTraffic.empty()) {
1158 SG_LOG(SG_ATC, SG_ALERT,
1159 "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1161 return i->hasInstruction();
1167 FGATCInstruction FGStartupController::getInstruction(int id)
1169 TrafficVectorIterator i = activeTraffic.begin();
1170 // Search search if the current id has an entry
1171 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1172 if (! activeTraffic.empty()) {
1173 //while ((i->getId() != id) && i != activeTraffic.end()) {
1174 while (i != activeTraffic.end()) {
1175 if (i->getId() == id) {
1181 if (i == activeTraffic.end() || activeTraffic.empty()) {
1182 SG_LOG(SG_ATC, SG_ALERT,
1183 "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1185 return i->getInstruction();
1187 return FGATCInstruction();
1190 void FGStartupController::signOff(int id)
1192 TrafficVectorIterator i = activeTraffic.begin();
1193 // Search search if the current id alread has an entry
1194 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1195 if (! activeTraffic.empty()) {
1196 //while ((i->getId() != id) && i != activeTraffic.end()) {
1197 while (i != activeTraffic.end()) {
1198 if (i->getId() == id) {
1204 if (i == activeTraffic.end() || activeTraffic.empty()) {
1205 SG_LOG(SG_ATC, SG_ALERT,
1206 "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN);
1208 //cerr << i->getAircraft()->getCallSign() << " signing off from startupcontroller" << endl;
1209 i = activeTraffic.erase(i);
1213 bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId,
1216 int state = i->getState();
1217 if ((state == st) && available) {
1218 if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
1220 //cerr << "Checking state " << st << " for " << i->getAircraft()->getCallSign() << endl;
1221 SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
1222 int n = trans_num->getIntValue();
1224 trans_num->setIntValue(-1);
1225 // PopupCallback(n);
1226 //cerr << "Selected transmission message " << n << endl;
1227 FGATCDialogNew::instance()->removeEntry(1);
1229 //cerr << "creading message for " << i->getAircraft()->getCallSign() << endl;
1230 transmit(&(*i), &(*parent), msgId, msgDir, false);
1234 if (now > startTime) {
1235 //cerr << "Transmitting startup msg" << endl;
1236 transmit(&(*i), &(*parent), msgId, msgDir, true);
1238 lastTransmission = now;
1246 void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
1247 double heading, double speed, double alt,
1250 TrafficVectorIterator i = activeTraffic.begin();
1251 // Search search if the current id has an entry
1252 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1253 TrafficVectorIterator current, closest;
1254 if (! activeTraffic.empty()) {
1255 //while ((i->getId() != id) && i != activeTraffic.end()) {
1256 while (i != activeTraffic.end()) {
1257 if (i->getId() == id) {
1263 // // update position of the current aircraft
1265 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1266 SG_LOG(SG_ATC, SG_ALERT,
1267 "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
1269 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1272 setDt(getDt() + dt);
1274 int state = i->getState();
1276 // The user controlled aircraft should have crased here, because it doesn't have a traffic reference.
1277 // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
1278 time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
1279 time_t now = globals->get_time_params()->get_cur_time();
1281 //cerr << i->getAircraft()->getTrafficRef()->getCallSign()
1282 // << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
1283 // << " at parking " << getGateName(i->getAircraft()) << endl;
1285 if ((now - lastTransmission) > 3 + (rand() % 15)) {
1289 checkTransmissionState(0, now, (startTime + 0 ), i, MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
1290 checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
1291 checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
1292 checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND);
1293 if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, ATC_AIR_TO_GROUND)) {
1296 checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND);
1297 checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT, ATC_GROUND_TO_AIR);
1298 checkTransmissionState(7, now, (startTime + 180), i, MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND);
1302 if ((state == 8) && available) {
1303 if (now > startTime + 200) {
1304 if (i->pushBackAllowed()) {
1305 i->allowRepeatedTransmissions();
1306 transmit(&(*i), &(*parent), MSG_PERMIT_PUSHBACK_CLEARANCE,
1307 ATC_GROUND_TO_AIR, true);
1310 transmit(&(*i), &(*parent), MSG_HOLD_PUSHBACK_CLEARANCE,
1311 ATC_GROUND_TO_AIR, true);
1312 i->suppressRepeatedTransmissions();
1314 lastTransmission = now;
1318 if ((state == 9) && available) {
1319 i->setHoldPosition(false);
1323 // Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
1324 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
1325 double lon, double elev, double hdg, double slope)
1327 SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
1328 obj_pos = makeZUpFrame(geod);
1329 // hdg is not a compass heading, but a counter-clockwise rotation
1330 // around the Z axis
1331 obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
1333 obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
1338 void FGStartupController::render(bool visible)
1340 //std::cerr << "Rendering startup controller" << std::endl;
1341 SGMaterialLib *matlib = globals->get_matlib();
1344 globals->get_scenery()->get_scene_graph()->removeChild(group);
1345 //while (group->getNumChildren()) {
1346 // cerr << "Number of children: " << group->getNumChildren() << endl;
1347 //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
1348 //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
1349 //geode->releaseGLObjects();
1350 //group->removeChild(geode);
1355 group = new osg::Group;
1356 FGScenery * local_scenery = globals->get_scenery();
1357 //double elevation_meters = 0.0;
1358 //double elevation_feet = 0.0;
1361 //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
1363 time_t now = globals->get_time_params()->get_cur_time();
1365 for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
1366 if (i->isActive(300)) {
1367 // Handle start point
1368 int pos = i->getCurrentPosition();
1369 //cerr << "rendering for " << i->getAircraft()->getCallSign() << "pos = " << pos << endl;
1371 FGTaxiSegment *segment = parent->getGroundNetwork()->findSegment(pos);
1372 SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
1373 SGGeod end (segment->getEnd()->geod());
1375 double length = SGGeodesy::distanceM(start, end);
1376 //heading = SGGeodesy::headingDeg(start->geod(), end->geod());
1378 double az2, heading; //, distanceM;
1379 SGGeodesy::inverse(start, end, heading, az2, length);
1380 double coveredDistance = length * 0.5;
1382 SGGeodesy::direct(start, heading, coveredDistance, center, az2);
1383 //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
1384 ///////////////////////////////////////////////////////////////////////////////
1385 // Make a helper function out of this
1386 osg::Matrix obj_pos;
1387 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1388 obj_trans->setDataVariance(osg::Object::STATIC);
1389 // Experimental: Calculate slope here, based on length, and the individual elevations
1390 double elevationStart;
1391 if (isUserAircraft((i)->getAircraft())) {
1392 elevationStart = fgGetDouble("/position/ground-elev-m");
1394 elevationStart = ((i)->getAircraft()->_getAltitude() * SG_FEET_TO_METER);
1396 double elevationEnd = segment->getEnd()->getElevationM();
1397 if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1398 SGGeod center2 = end;
1399 center2.setElevationM(SG_MAX_ELEVATION_M);
1400 if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1401 //elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1402 //elevation_meters += 0.5;
1405 elevationEnd = parent->getElevation();
1407 segment->getEnd()->setElevation(elevationEnd);
1410 double elevationMean = (elevationStart + elevationEnd) / 2.0;
1411 double elevDiff = elevationEnd - elevationStart;
1413 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1415 //cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
1417 WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean + 0.5 + dx, -(heading), slope );
1420 obj_trans->setMatrix( obj_pos );
1421 //osg::Vec3 center(0, 0, 0)
1423 float width = length /2.0;
1424 osg::Vec3 corner(-width, 0, 0.25f);
1425 osg::Vec3 widthVec(2*width + 1, 0, 0);
1426 osg::Vec3 heightVec(0, 1, 0);
1427 osg::Geometry* geometry;
1428 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1429 simgear::EffectGeode* geode = new simgear::EffectGeode;
1430 geode->setName("test");
1431 geode->addDrawable(geometry);
1432 //osg::Node *custom_obj;
1434 if (segment->hasBlock(now)) {
1435 mat = matlib->find("UnidirectionalTaperRed", center);
1437 mat = matlib->find("UnidirectionalTaperGreen", center);
1440 geode->setEffect(mat->get_effect());
1441 obj_trans->addChild(geode);
1442 // wire as much of the scene graph together as we can
1443 //->addChild( obj_trans );
1444 group->addChild( obj_trans );
1445 /////////////////////////////////////////////////////////////////////
1447 //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
1449 for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
1450 osg::Matrix obj_pos;
1453 //cerr << "rendering for " << i->getAircraft()->getCallSign() << "intention = " << k << endl;
1454 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
1455 obj_trans->setDataVariance(osg::Object::STATIC);
1456 FGTaxiSegment *segment = parent->getGroundNetwork()->findSegment(k);
1458 double elevationStart = segment->getStart()->getElevationM();
1459 double elevationEnd = segment->getEnd ()->getElevationM();
1460 if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
1461 SGGeod center2 = segment->getStart()->geod();
1462 center2.setElevationM(SG_MAX_ELEVATION_M);
1463 if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
1464 //elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
1465 //elevation_meters += 0.5;
1468 elevationStart = parent->getElevation();
1470 segment->getStart()->setElevation(elevationStart);
1472 if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
1473 SGGeod center2 = segment->getEnd()->geod();
1474 center2.setElevationM(SG_MAX_ELEVATION_M);
1475 if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
1476 //elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
1477 //elevation_meters += 0.5;
1480 elevationEnd = parent->getElevation();
1482 segment->getEnd()->setElevation(elevationEnd);
1485 double elevationMean = (elevationStart + elevationEnd) / 2.0;
1486 double elevDiff = elevationEnd - elevationStart;
1487 double length = segment->getLength();
1488 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
1490 //cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
1492 SGGeod segCenter(segment->getCenter());
1493 WorldCoordinate( obj_pos, segCenter.getLatitudeDeg(),
1494 segCenter.getLongitudeDeg(), elevationMean + 0.5 + dx, -(segment->getHeading()), slope );
1496 //WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), parent->getElevation()+8+dx, -(segment->getHeading()) );
1498 obj_trans->setMatrix( obj_pos );
1499 //osg::Vec3 center(0, 0, 0)
1501 float width = segment->getLength() /2.0;
1502 osg::Vec3 corner(-width, 0, 0.25f);
1503 osg::Vec3 widthVec(2*width + 1, 0, 0);
1504 osg::Vec3 heightVec(0, 1, 0);
1505 osg::Geometry* geometry;
1506 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
1507 simgear::EffectGeode* geode = new simgear::EffectGeode;
1508 geode->setName("test");
1509 geode->addDrawable(geometry);
1510 //osg::Node *custom_obj;
1512 if (segment->hasBlock(now)) {
1513 mat = matlib->find("UnidirectionalTaperRed", segCenter);
1515 mat = matlib->find("UnidirectionalTaperGreen", segCenter);
1518 geode->setEffect(mat->get_effect());
1519 obj_trans->addChild(geode);
1520 // wire as much of the scene graph together as we can
1521 //->addChild( obj_trans );
1522 group->addChild( obj_trans );
1524 //cerr << "BIG FAT WARNING: k is here : " << pos << endl;
1530 globals->get_scenery()->get_scene_graph()->addChild(group);
1534 string FGStartupController::getName() {
1535 return string(parent->getId() + "-startup");
1538 void FGStartupController::update(double dt)
1545 /***************************************************************************
1546 * class FGApproachController
1548 **************************************************************************/
1549 FGApproachController::FGApproachController(FGAirportDynamics *par):
1556 void FGApproachController::announcePosition(int id,
1557 FGAIFlightPlan * intendedRoute,
1558 int currentPosition,
1559 double lat, double lon,
1560 double heading, double speed,
1561 double alt, double radius,
1562 int leg, FGAIAircraft * ref)
1565 TrafficVectorIterator i = activeTraffic.begin();
1566 // Search whether the current id alread has an entry
1567 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1568 if (! activeTraffic.empty()) {
1569 //while ((i->getId() != id) && i != activeTraffic.end()) {
1570 while (i != activeTraffic.end()) {
1571 if (i->getId() == id) {
1577 // Add a new TrafficRecord if no one exsists for this aircraft.
1578 if (i == activeTraffic.end() || activeTraffic.empty()) {
1579 FGTrafficRecord rec;
1582 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
1583 rec.setRunway(intendedRoute->getRunway());
1585 //rec.setCallSign(callsign);
1586 rec.setAircraft(ref);
1587 activeTraffic.push_back(rec);
1589 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1593 void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
1594 double heading, double speed, double alt,
1597 TrafficVectorIterator i = activeTraffic.begin();
1598 // Search search if the current id has an entry
1599 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1600 TrafficVectorIterator current, closest;
1601 if (! activeTraffic.empty()) {
1602 //while ((i->getId() != id) && i != activeTraffic.end()) {
1603 while (i != activeTraffic.end()) {
1604 if (i->getId() == id) {
1610 // // update position of the current aircraft
1611 if (i == activeTraffic.end() || activeTraffic.empty()) {
1612 SG_LOG(SG_ATC, SG_ALERT,
1613 "AI error: updating aircraft without traffic record at " << SG_ORIGIN);
1615 i->setPositionAndHeading(lat, lon, heading, speed, alt);
1617 //cerr << "ApproachController: checking for speed" << endl;
1619 current->getAircraft()->
1620 checkForArrivalTime(string("final001"));
1621 if (time_diff > 15) {
1622 current->setSpeedAdjustment(current->getAircraft()->
1623 getPerformance()->vDescent() *
1625 } else if (time_diff > 5) {
1626 current->setSpeedAdjustment(current->getAircraft()->
1627 getPerformance()->vDescent() *
1629 } else if (time_diff < -15) {
1630 current->setSpeedAdjustment(current->getAircraft()->
1631 getPerformance()->vDescent() *
1633 } else if (time_diff < -5) {
1634 current->setSpeedAdjustment(current->getAircraft()->
1635 getPerformance()->vDescent() *
1638 current->clearSpeedAdjustment();
1640 //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
1642 setDt(getDt() + dt);
1645 void FGApproachController::signOff(int id)
1647 TrafficVectorIterator i = activeTraffic.begin();
1648 // Search search if the current id alread has an entry
1649 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1650 if (! activeTraffic.empty()) {
1651 //while ((i->getId() != id) && i != activeTraffic.end()) {
1652 while (i != activeTraffic.end()) {
1653 if (i->getId() == id) {
1659 if (i == activeTraffic.end() || activeTraffic.empty()) {
1660 SG_LOG(SG_ATC, SG_ALERT,
1661 "AI error: Aircraft without traffic record is signing off from approach at " << SG_ORIGIN);
1663 i = activeTraffic.erase(i);
1667 void FGApproachController::update(double dt)
1674 bool FGApproachController::hasInstruction(int id)
1676 TrafficVectorIterator i = activeTraffic.begin();
1677 // Search search if the current id has an entry
1678 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1679 if (! activeTraffic.empty()) {
1680 //while ((i->getId() != id) && i != activeTraffic.end()) {
1681 while (i != activeTraffic.end()) {
1682 if (i->getId() == id) {
1688 if (i == activeTraffic.end() || activeTraffic.empty()) {
1689 SG_LOG(SG_ATC, SG_ALERT,
1690 "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1692 return i->hasInstruction();
1698 FGATCInstruction FGApproachController::getInstruction(int id)
1700 TrafficVectorIterator i = activeTraffic.begin();
1701 // Search search if the current id has an entry
1702 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1703 if (activeTraffic.size()) {
1704 //while ((i->getId() != id) && i != activeTraffic.end()) {
1705 while (i != activeTraffic.end()) {
1706 if (i->getId() == id) {
1712 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1713 SG_LOG(SG_ATC, SG_ALERT,
1714 "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
1716 return i->getInstruction();
1718 return FGATCInstruction();
1722 ActiveRunway *FGApproachController::getRunway(const string& name)
1724 ActiveRunwayVecIterator rwy = activeRunways.begin();
1725 if (activeRunways.size()) {
1726 while (rwy != activeRunways.end()) {
1727 if (rwy->getRunwayName() == name) {
1733 if (rwy == activeRunways.end()) {
1734 ActiveRunway aRwy(name, 0);
1735 activeRunways.push_back(aRwy);
1736 rwy = activeRunways.end() - 1;
1741 void FGApproachController::render(bool visible) {
1742 //std::cerr << "FGApproachController::render function not yet implemented" << std::endl;
1747 string FGApproachController::getName() {
1748 return string(parent->getId() + "-approach");