From 17c42deae16610939d598a0dabe28d2d4d362469 Mon Sep 17 00:00:00 2001 From: durk Date: Sun, 13 Jul 2008 12:51:06 +0000 Subject: [PATCH] Initial attempt to establish a better integration between AI and ATC code. Various other patches that have been lingering around for a while: * Moved trafficcontrol.[ch]xx from the Airports directory to ATC, where it really belongs. * AI aircraft will request startup clearance, and ground control will approve. * Starting AI Aircraft will be pushed back to a predefined holding point on the ground network, and wait a while before taxiing out to the runway --- src/AIModel/AIAircraft.cxx | 57 +- src/AIModel/AIAircraft.hxx | 3 + src/AIModel/AIFlightPlanCreatePushBack.cxx | 65 +- src/ATC/Makefile.am | 6 + src/ATC/trafficcontrol.cxx | 787 +++++++++++++++++++++ src/ATC/trafficcontrol.hxx | 292 ++++++++ src/ATCDCL/Makefile.am | 4 +- src/Airports/Makefile.am | 1 - src/Airports/dynamicloader.cxx | 51 +- src/Airports/dynamicloader.hxx | 1 + src/Airports/dynamics.cxx | 35 +- src/Airports/dynamics.hxx | 34 +- src/Airports/groundnetwork.cxx | 183 +---- src/Airports/groundnetwork.hxx | 4 +- src/Airports/parking.hxx | 5 +- src/Airports/runwayprefs.cxx | 2 +- src/Main/Makefile.am | 3 +- src/Makefile.am | 1 + 18 files changed, 1244 insertions(+), 290 deletions(-) create mode 100644 src/ATC/Makefile.am create mode 100644 src/ATC/trafficcontrol.cxx create mode 100644 src/ATC/trafficcontrol.hxx diff --git a/src/AIModel/AIAircraft.cxx b/src/AIModel/AIAircraft.cxx index c73919d0a..7a06ba241 100644 --- a/src/AIModel/AIAircraft.cxx +++ b/src/AIModel/AIAircraft.cxx @@ -150,7 +150,8 @@ void FGAIAircraft::setPerformance(const std::string& acclass) { return; } catch (FP_Inactive) { - return; + //return; + groundTargetSpeed = 0; } handleATCRequests(); // ATC also has a word to say @@ -365,7 +366,7 @@ void FGAIAircraft::getGroundElev(double dt) { dt_elev_count += dt; // Update minimally every three secs, but add some randomness - // to prevent all IA objects doing this in synchrony + // to prevent all AI objects doing this in synchrony if (dt_elev_count < (3.0) + (rand() % 10)) return; @@ -413,10 +414,14 @@ void FGAIAircraft::announcePositionToController() { if (trafficRef) { int leg = fp->getLeg(); - // Note that leg was been incremented after creating the current leg, so we should use + // Note that leg has been incremented after creating the current leg, so we should use // leg numbers here that are one higher than the number that is used to create the leg // switch (leg) { + case 2: // Startup and Push back + if (trafficRef->getDepartureAirport()->getDynamics()) + controller = trafficRef->getDepartureAirport()->getDynamics()->getStartupController(); + break; case 3: // Taxiing to runway if (trafficRef->getDepartureAirport()->getDynamics()->getGroundNetwork()->exists()) controller = trafficRef->getDepartureAirport()->getDynamics()->getGroundNetwork(); @@ -425,12 +430,14 @@ void FGAIAircraft::announcePositionToController() { if (trafficRef->getDepartureAirport()->getDynamics()) { controller = trafficRef->getDepartureAirport()->getDynamics()->getTowerController(); //if (trafficRef->getDepartureAirport()->getId() == "EHAM") { - //cerr << trafficRef->getCallSign() << " at runway " << fp->getRunway() << "Ready for departure " - // << trafficRef->getFlightType() << " to " << trafficRef->getArrivalAirport()->getId() << endl; + //string trns = trafficRef->getCallSign() + " at runway " + fp->getRunway() + + // ". Ready for departure. " + trafficRef->getFlightType() + " to " + + // trafficRef->getArrivalAirport()->getId(); + //fgSetString("/sim/messages/atc", trns.c_str()); // if (controller == 0) { //cerr << "Error in assigning controller at " << trafficRef->getDepartureAirport()->getId() << endl; //} - + //} } else { cerr << "Error: Could not find Dynamics at airport : " << trafficRef->getDepartureAirport()->getId() << endl; } @@ -446,29 +453,17 @@ void FGAIAircraft::announcePositionToController() { if ((controller != prevController) && (prevController != 0)) { prevController->signOff(getID()); - string callsign = trafficRef->getCallSign(); - if ( trafficRef->getHeavy()) - callsign += "Heavy"; - switch (leg) { - case 3: - //cerr << callsign << " ready to taxi to runway " << fp->getRunway() << endl; - break; - case 4: - //cerr << callsign << " at runway " << fp->getRunway() << "Ready for take-off. " - // << trafficRef->getFlightRules() << " to " << trafficRef->getArrivalAirport()->getId() - // << "(" << trafficRef->getArrivalAirport()->getName() << ")."<< endl; - break; - } } prevController = controller; if (controller) { controller->announcePosition(getID(), fp, fp->getCurrentWaypoint()->routeIndex, _getLatitude(), _getLongitude(), hdg, speed, altitude_ft, - trafficRef->getRadius(), leg, trafficRef->getCallSign()); + trafficRef->getRadius(), leg, this); } } } +// Process ATC instructions and report back void FGAIAircraft::processATC(FGATCInstruction instruction) { if (instruction.getCheckForCircularWait()) { @@ -494,7 +489,7 @@ void FGAIAircraft::processATC(FGATCInstruction instruction) { } else { if (holdPos) { //if (trafficRef) - // cerr << trafficRef->getCallSign() << " Resuming Taxi " << endl; + // cerr << trafficRef->getCallSign() << " Resuming Taxi." << endl; holdPos = false; } // Change speed Instruction. This can only be excecuted when there is no @@ -760,6 +755,7 @@ void FGAIAircraft::updatePrimaryTargetValues() { throw AI_OutOfSight(); } } + timeElapsed = now - fp->getStartTime(); if (! fp->isActive(now)) { throw FP_Inactive(); } @@ -845,7 +841,7 @@ void FGAIAircraft::updateHeading() { } else { bank_sense = 1.0; } - if (trafficRef) + //if (trafficRef) //cerr << trafficRef->getCallSign() << " Heading " // << hdg << ". Target " << tgt_heading << ". Diff " << fabs(sum - tgt_heading) << ". Speed " << speed << endl; //if (headingDiff > 60) { @@ -875,7 +871,8 @@ void FGAIAircraft::updateHeading() { else headingChangeRate += dt * sign(roll); } - hdg += headingChangeRate * dt; + + hdg += headingChangeRate * dt * (fabs(speed) / 15); headingError = headingDiff; } else { if (fabs(speed) > 1.0) { @@ -979,6 +976,20 @@ void FGAIAircraft::updatePitchAngleTarget() { } } +string FGAIAircraft::atGate() { + string tmp(""); + if (fp->getLeg() < 3) { + if (trafficRef) { + if (fp->getGate() > 0) { + FGParking *park = + trafficRef->getDepartureAirport()->getDynamics()->getParking(fp->getGate()); + tmp = park->getName(); + } + } + } + return tmp; +} + void FGAIAircraft::handleATCRequests() { //TODO implement NullController for having no ATC to save the conditionals if (controller) { diff --git a/src/AIModel/AIAircraft.hxx b/src/AIModel/AIAircraft.hxx index 1c23c2576..f4adf16ba 100644 --- a/src/AIModel/AIAircraft.hxx +++ b/src/AIModel/AIAircraft.hxx @@ -70,6 +70,7 @@ public: void announcePositionToController(); //TODO have to be public? void processATC(FGATCInstruction instruction); + FGAISchedule * getTrafficRef() { return trafficRef; }; virtual const char* getTypeString(void) const { return "aircraft"; } @@ -82,6 +83,7 @@ public: inline double getVerticalSpeed() const { return vs; }; inline double altitudeAGL() const { return props->getFloatValue("position/altitude-agl-ft");}; inline double airspeed() const { return props->getFloatValue("velocities/airspeed-kt");}; + string atGate(); protected: void Run(double dt); @@ -138,6 +140,7 @@ private: bool _getGearDown() const; bool reachedWaypoint; + time_t timeElapsed; PerformanceData* _performance; // the performance data for this aircraft }; diff --git a/src/AIModel/AIFlightPlanCreatePushBack.cxx b/src/AIModel/AIFlightPlanCreatePushBack.cxx index e83e0dcbd..a2deb0a3c 100644 --- a/src/AIModel/AIFlightPlanCreatePushBack.cxx +++ b/src/AIModel/AIFlightPlanCreatePushBack.cxx @@ -41,6 +41,7 @@ void FGAIFlightPlan::createPushBack(bool firstFlight, FGAirport *dep, radius, fltType, aircraftType, airline); } else { if (firstFlight) { + if (!(dep->getDynamics()->getAvailableParking(&lat, &lon, &heading, &gateId, radius, fltType, @@ -70,6 +71,12 @@ void FGAIFlightPlan::createPushBack(bool firstFlight, FGAirport *dep, wpt->routeIndex = -1; waypoints.push_back(wpt); } + //cerr << "Success : GateId = " << gateId << endl; + SG_LOG(SG_INPUT, SG_WARN, "Warning: Succesfully found a parking for a " << + aircraftType << + " of flight type " << fltType << + " of airline " << airline << + " at airport " << dep->getId()); } else { //cerr << "Push Back follow-up Flight" << endl; dep->getDynamics()->getParking(gateId, &lat, &lon, &heading); @@ -89,61 +96,21 @@ void FGAIFlightPlan::createPushBack(bool firstFlight, FGAirport *dep, FGParking *parking = dep->getDynamics()->getParking(gateId); int pushBackNode = parking->getPushBackPoint(); - // initialize the pushback route. Note that parts - // of this procedure should probably be done inside - // taxidraw. This code is likely to change once this - // this is fully implemented in taxidraw. Until that time, - // however, the full initialization procedure looks like this: - // 1) Build a list of all the nodes that are classified as - // pushBack hold points - // 2) For each hold point, use the dykstra algorithm to find a route - // between the gate and the pushBack hold nodes, however use only - // segments that are classified as "pushback" routes. - // 3) return the TaxiRoute class that is non empty. - // 4) store refer this route in the parking object, for future use - - if (pushBackNode < 0) { - //cerr << "Initializing PushBackRoute " << endl; - intVec pushBackNodes; - int nAINodes = dep->getDynamics()->getGroundNetwork()->getNrOfNodes(); - int hits = 0; - parking->setPushBackPoint(0); // default in case no network was found. - // Collect all the nodes that are classified as having pushBack hold status - for (int i = 0; i < nAINodes; i++) { - if (dep->getDynamics()->getGroundNetwork()->findNode(i)->getHoldPointType() == 3) { - pushBackNodes.push_back(i); - } - } - - // For each node found in step 1, check if it can be reached - FGTaxiRoute route; - for (intVecIterator nodes = pushBackNodes.begin(); - nodes != pushBackNodes.end(); - nodes++) { - route = dep->getDynamics()->getGroundNetwork()->findShortestRoute(gateId, *nodes, false); - if (!(route.empty())) { - //cerr << "Found Pushback route of size " << route.size() << endl; - hits++; - parking->setPushBackRoute(new FGTaxiRoute(route)); - parking->setPushBackPoint(*nodes); - pushBackNode = *nodes; - } - } - if (hits == 0) { - SG_LOG(SG_GENERAL, SG_INFO, "No pushback route found for gate " << gateId << " at " << dep->getId()); - } - if (hits > 1) { - SG_LOG(SG_GENERAL, SG_WARN, hits << " pushback routes found for gate " << gateId << " at " << dep->getId()); - } - } - if (pushBackNode > 0) { + pushBackRoute = parking->getPushBackRoute(); + if ((pushBackNode > 0) && (pushBackRoute == 0)) { int node, rte; - //cerr << "Found valid pusback node " << pushBackNode << "for gate " << gateId << endl; + FGTaxiRoute route; + //cerr << "Creating push-back for " << gateId << " (" << parking->getName() << ") using push-back point " << pushBackNode << endl; + route = dep->getDynamics()->getGroundNetwork()->findShortestRoute(gateId, pushBackNode, false); + parking->setPushBackRoute(new FGTaxiRoute(route)); + + pushBackRoute = parking->getPushBackRoute(); int size = pushBackRoute->size(); if (size < 2) { SG_LOG(SG_GENERAL, SG_WARN, "Push back route from gate " << gateId << " has only " << size << " nodes."); + SG_LOG(SG_GENERAL, SG_WARN, "Using " << pushBackNode); } pushBackRoute->first(); waypoint *wpt; diff --git a/src/ATC/Makefile.am b/src/ATC/Makefile.am new file mode 100644 index 000000000..32e4ced92 --- /dev/null +++ b/src/ATC/Makefile.am @@ -0,0 +1,6 @@ +noinst_LIBRARIES = libATC.a + +libATC_a_SOURCES = \ + trafficcontrol.cxx trafficcontrol.hxx + +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx new file mode 100644 index 000000000..85270f32e --- /dev/null +++ b/src/ATC/trafficcontrol.cxx @@ -0,0 +1,787 @@ +// trafficrecord.cxx - Implementation of AIModels ATC code. +// +// Written by Durk Talsma, started September 2006. +// +// Copyright (C) 2006 Durk Talsma. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "trafficcontrol.hxx" +#include +#include +#include + + +/*************************************************************************** + * FGTrafficRecord + **************************************************************************/ +FGTrafficRecord::FGTrafficRecord() : + id(0), waitsForId(0), + currentPos(0), + leg(0), + state(0), + latitude(0), + longitude(0), + heading(0), + speed(0), + altitude(0), + radius(0) { +} + +void FGTrafficRecord::setPositionAndIntentions(int pos, FGAIFlightPlan *route) +{ + + currentPos = pos; + if (intentions.size()) { + intVecIterator i = intentions.begin(); + if ((*i) != pos) { + SG_LOG(SG_GENERAL, SG_ALERT, "Error in FGTrafficRecord::setPositionAndIntentions"); + //cerr << "Pos : " << pos << " Curr " << *(intentions.begin()) << endl; + for (intVecIterator i = intentions.begin(); i != intentions.end() ; i++) { + //cerr << (*i) << " "; + } + //cerr << endl; + } + intentions.erase(i); + } else { + //int legNr, routeNr; + //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint(); + int size = route->getNrOfWayPoints(); + //cerr << "Setting pos" << pos << " "; + //cerr << "setting intentions "; + for (int i = 0; i < size; i++) { + int val = route->getRouteIndex(i); + //cerr << val<< " "; + if ((val) && (val != pos)) + { + intentions.push_back(val); + //cerr << "[set] "; + } + } + //cerr << endl; + //while (route->next(&legNr, &routeNr)) { + //intentions.push_back(routeNr); + //} + //route->rewind(currentPos); + } + //exit(1); +} + +bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord &other) +{ + bool result = false; + //cerr << "Start check 1" << endl; + if (currentPos == other.currentPos) + { + //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl; + result = true; + } + // else if (other.intentions.size()) + // { + // cerr << "Start check 2" << endl; + // intVecIterator i = other.intentions.begin(); + // while (!((i == other.intentions.end()) || ((*i) == currentPos))) + // i++; + // if (i != other.intentions.end()) { + // cerr << "Check Position and intentions: current matches other.intentions" << endl; + // result = true; + // } + else if (intentions.size()) { + //cerr << "Start check 3" << endl; + intVecIterator i = intentions.begin(); + //while (!((i == intentions.end()) || ((*i) == other.currentPos))) + while (i != intentions.end()) { + if ((*i) == other.currentPos) { + break; + } + i++; + } + if (i != intentions.end()) { + //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl; + result = true; + } + } + //cerr << "Done !!" << endl; + return result; +} + +void FGTrafficRecord::setPositionAndHeading(double lat, double lon, double hdg, + double spd, double alt) +{ + latitude = lat; + longitude = lon; + heading = hdg; + speed = spd; + altitude = alt; +} + +int FGTrafficRecord::crosses(FGGroundNetwork *net, FGTrafficRecord &other) +{ + if (checkPositionAndIntentions(other) || (other.checkPositionAndIntentions(*this))) + return -1; + intVecIterator i, j; + int currentTargetNode = 0, otherTargetNode = 0; + if (currentPos > 0) + currentTargetNode = net->findSegment(currentPos )->getEnd()->getIndex(); // OKAY,... + if (other.currentPos > 0) + otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,... + if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0) + return currentTargetNode; + if (intentions.size()) + { + for (i = intentions.begin(); i != intentions.end(); i++) + { + if ((*i) > 0) { + if ((currentTargetNode == net->findSegment(*i)->getEnd()->getIndex())) + { + //cerr << "Current crosses at " << currentTargetNode < 0) { + if (otherTargetNode == net->findSegment(*i)->getEnd()->getIndex()) + { + //cerr << "Other crosses at " << currentTargetNode < 0) && ((*j) > 0)) { + currentTargetNode = net->findSegment(*i)->getEnd()->getIndex(); + otherTargetNode = net->findSegment(*j)->getEnd()->getIndex(); + if (currentTargetNode == otherTargetNode) + { + //cerr << "Routes will cross at " << currentTargetNode << endl; + return currentTargetNode; + } + } + } + } + } + return -1; +} + +bool FGTrafficRecord::onRoute(FGGroundNetwork *net, FGTrafficRecord &other) +{ + int node = -1, othernode = -1; + if (currentPos >0) + node = net->findSegment(currentPos)->getEnd()->getIndex(); + if (other.currentPos > 0) + othernode = net->findSegment(other.currentPos)->getEnd()->getIndex(); + if ((node == othernode) && (node != -1)) + return true; + if (other.intentions.size()) + { + for (intVecIterator i = other.intentions.begin(); i != other.intentions.end(); i++) + { + if (*i > 0) + { + othernode = net->findSegment(*i)->getEnd()->getIndex(); + if ((node == othernode) && (node > -1)) + return true; + } + } + } + //if (other.currentPos > 0) + // othernode = net->findSegment(other.currentPos)->getEnd()->getIndex(); + //if (intentions.size()) + // { + // for (intVecIterator i = intentions.begin(); i != intentions.end(); i++) + // { + // if (*i > 0) + // { + // node = net->findSegment(*i)->getEnd()->getIndex(); + // if ((node == othernode) && (node > -1)) + // return true; + // } + // } + // } + return false; +} + + +bool FGTrafficRecord::isOpposing (FGGroundNetwork *net, FGTrafficRecord &other, int node) +{ + // Check if current segment is the reverse segment for the other aircraft + FGTaxiSegment *opp; + //cerr << "Current segment " << currentPos << endl; + if ((currentPos > 0) && (other.currentPos > 0)) + { + opp = net->findSegment(currentPos)->opposite(); + if (opp) { + if (opp->getIndex() == other.currentPos) + return true; + } + + for (intVecIterator i = intentions.begin(); i != intentions.end(); i++) + { + if (opp = net->findSegment(other.currentPos)->opposite()) + { + if ((*i) > 0) + if (opp->getIndex() == net->findSegment(*i)->getIndex()) + { + if (net->findSegment(*i)->getStart()->getIndex() == node) { + { + //cerr << "Found the node " << node << endl; + return true; + } + } + } + } + if (other.intentions.size()) + { + for (intVecIterator j = other.intentions.begin(); j != other.intentions.end(); j++) + { + // cerr << "Current segment 1 " << (*i) << endl; + if ((*i) > 0) { + if (opp = net->findSegment(*i)->opposite()) + { + if (opp->getIndex() == + net->findSegment(*j)->getIndex()) + { + //cerr << "Nodes " << net->findSegment(*i)->getIndex() + // << " and " << net->findSegment(*j)->getIndex() + // << " are opposites " << endl; + if (net->findSegment(*i)->getStart()->getIndex() == node) { + { + //cerr << "Found the node " << node << endl; + return true; + } + } + } + } + } + } + } + } + } + return false; +} + +void FGTrafficRecord::setSpeedAdjustment(double spd) +{ + instruction.setChangeSpeed(true); + instruction.setSpeed(spd); +} + +void FGTrafficRecord::setHeadingAdjustment(double heading) +{ + instruction.setChangeHeading(true); + instruction.setHeading(heading); +} + + + +/*************************************************************************** + * FGATCInstruction + * + **************************************************************************/ +FGATCInstruction::FGATCInstruction() +{ + holdPattern = false; + holdPosition = false; + changeSpeed = false; + changeHeading = false; + changeAltitude = false; + resolveCircularWait = false; + + double speed = 0; + double heading = 0; + double alt = 0; +} + +bool FGATCInstruction::hasInstruction() +{ + return (holdPattern || holdPosition || changeSpeed || changeHeading || changeAltitude || resolveCircularWait); +} + +string FGATCController::getGateName(FGAIAircraft *ref) +{ + return ref->atGate(); +} + +void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir msgDir) +{ + string sender, receiver; + int commFreq = 0; + //double commFreqD; + switch (msgDir) { + case ATC_AIR_TO_GROUND: + sender = rec->getAircraft()->getTrafficRef()->getCallSign(); + switch (rec->getLeg()) { + case 2: + commFreq = + rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(); + receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground"; + break; + case 3: + receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground"; + break; + case 4: + receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower"; + break; + } + break; + case ATC_GROUND_TO_AIR: + receiver = rec->getAircraft()->getTrafficRef()->getCallSign(); + switch (rec->getLeg()) { + case 2: + commFreq = + rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(); + sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground"; + break; + case 3: + sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground"; + break; + case 4: + sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower"; + break; + } + break; + } + string text; + switch (msgId) { + case MSG_ANNOUNCE_ENGINE_START: + text = sender + ". Ready to Start up"; + break; + case MSG_REQUEST_ENGINE_START: + text = receiver + ", This is " + sender + ". Position " +getGateName(rec->getAircraft()) + + ". Information YY." + + rec->getAircraft()->getTrafficRef()->getFlightRules() + " to " + + rec->getAircraft()->getTrafficRef()->getArrivalAirport()->getName() + ". Request start-up"; + break; + case MSG_PERMIT_ENGINE_START: + text = receiver + ". Start-up approved. YY correct, runway ZZ, AAA departure, squawk BBBB. " + + "For push-back and taxi clearance call CCC.CCC. " + sender + " control."; + break; + case MSG_DENY_ENGINE_START: + text = receiver + ". Standby"; + break; + case MSG_ACKNOWLEDGE_ENGINE_START: + text = receiver + ". Start-up approved. YY correct, runway ZZ, AAA departure, squawk BBBB. " + + "For push-back and taxi clearance call CCC.CCC. " + sender; + break; + default: + break; + } + double currFreq = fgGetDouble("/instrumentation/comm/frequencies/selected-mhz"); + int currFreqI = (int) round(currFreq * 100); + //cerr << "Using " << currFreqI << " and " << commFreq << endl; + if (currFreqI == commFreq) { + fgSetString("/sim/messages/atc", text.c_str()); + //cerr << "Printing Message: " << endl; + } +} + + +/*************************************************************************** + * class FGTowerController + * + **************************************************************************/ +FGTowerController::FGTowerController() : + FGATCController() +{ +} + +// +void FGTowerController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition, + double lat, double lon, double heading, + double speed, double alt, double radius, int leg, + FGAIAircraft *ref) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search whether the current id alread has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + + // Add a new TrafficRecord if no one exsists for this aircraft. + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + FGTrafficRecord rec; + rec.setId(id); + + rec.setPositionAndHeading(lat, lon, heading, speed, alt); + rec.setRunway(intendedRoute->getRunway()); + rec.setLeg(leg); + //rec.setCallSign(callsign); + rec.setAircraft(ref); + activeTraffic.push_back(rec); + } else { + i->setPositionAndHeading(lat, lon, heading, speed, alt); + } +} + +void FGTowerController::update(int id, double lat, double lon, double heading, double speed, double alt, + double dt) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + TrafficVectorIterator current, closest; + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + +// // update position of the current aircraft + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record"); + } else { + i->setPositionAndHeading(lat, lon, heading, speed, alt); + current = i; + } + setDt(getDt() + dt); + +// // see if we already have a clearance record for the currently active runway + ActiveRunwayVecIterator rwy = activeRunways.begin(); + // again, a map might be more efficient here + if (activeRunways.size()) { + //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) { + while (rwy != activeRunways.end()) { + if (rwy->getRunwayName() == current->getRunway()) { + break; + } + rwy++; + } + } + if (rwy == activeRunways.end()) { + ActiveRunway aRwy(current->getRunway(), id); + activeRunways.push_back(aRwy); // Since there are no clearance records for this runway yet + current->setHoldPosition(false); // Clear the current aircraft to continue + } + else { + // Okay, we have a clearance record for this runway, so check + // whether the clearence ID matches that of the current aircraft + if (id == rwy->getCleared()) { + current->setHoldPosition(false); + } else { + current->setHoldPosition(true); + } + } +} + + +void FGTowerController::signOff(int id) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id alread has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + // If this aircraft has left the runway, we can clear the departure record for this runway + ActiveRunwayVecIterator rwy = activeRunways.begin(); + if (activeRunways.size()) { + //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) { + while (rwy != activeRunways.end()) { + if (rwy->getRunwayName() == i->getRunway()) { + break; + } + rwy++; + } + if (rwy != activeRunways.end()) { + rwy = activeRunways.erase(rwy); + } else { + SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff"); + } + } + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower"); + } else { + i = activeTraffic.erase(i); + } +} + +// NOTE: +// IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS +// THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN +// BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS +// WHICH WOULD SIMPLIFY CODE MAINTENANCE. +// Note that this function is probably obsolete +bool FGTowerController::hasInstruction(int id) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) + { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record"); + } else { + return i->hasInstruction(); + } + return false; +} + + +FGATCInstruction FGTowerController::getInstruction(int id) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record"); + } else { + return i->getInstruction(); + } + return FGATCInstruction(); +} + +/*************************************************************************** + * class FGStartupController + * + **************************************************************************/ +FGStartupController::FGStartupController() : + FGATCController() +{ + available = true; +} + +void FGStartupController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition, + double lat, double lon, double heading, + double speed, double alt, double radius, int leg, + FGAIAircraft *ref) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search whether the current id alread has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + + // Add a new TrafficRecord if no one exsists for this aircraft. + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + FGTrafficRecord rec; + rec.setId(id); + + rec.setPositionAndHeading(lat, lon, heading, speed, alt); + rec.setRunway(intendedRoute->getRunway()); + rec.setLeg(leg); + //rec.setCallSign(callsign); + rec.setAircraft(ref); + rec.setHoldPosition(true); + activeTraffic.push_back(rec); + } else { + i->setPositionAndHeading(lat, lon, heading, speed, alt); + + } +} + +// NOTE: +// IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS +// THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN +// BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS +// WHICH WOULD SIMPLIFY CODE MAINTENANCE. +// Note that this function is probably obsolete +bool FGStartupController::hasInstruction(int id) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) + { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record"); + } else { + return i->hasInstruction(); + } + return false; +} + + +FGATCInstruction FGStartupController::getInstruction(int id) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record"); + } else { + return i->getInstruction(); + } + return FGATCInstruction(); +} + +void FGStartupController::signOff(int id) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id alread has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower"); + } else { + i = activeTraffic.erase(i); + } +} + +void FGStartupController::update(int id, double lat, double lon, double heading, double speed, double alt, + double dt) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + TrafficVectorIterator current, closest; + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + +// // update position of the current aircraft + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record"); + } else { + i->setPositionAndHeading(lat, lon, heading, speed, alt); + current = i; + } + setDt(getDt() + dt); + + int state = i->getState(); + time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime(); + time_t now = time(NULL) + fgGetLong("/sim/time/warp"); + + if ((now - lastTransmission) > 3 + (rand() % 15)) { + available = true; + } + + if ((state == 0) && available) { + if (now > startTime) { + transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND); + i->updateState(); + lastTransmission = now; + available = false; + } + } + if ((state == 1) && available) { + if (now > startTime+60) { + transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND); + i->updateState(); + lastTransmission = now; + available = false; + } + } + if ((state == 2) && available) { + if (now > startTime+80) { + transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR); + i->updateState(); + lastTransmission = now; + available = false; + } + } + if ((state == 3) && available){ + if (now > startTime+100) { + transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND); + i->updateState(); + lastTransmission = now; + available = false; + } + } + // TODO: Switch to APRON control and request pushback Clearance. + // Get Push back clearance + if ((state == 4) && available){ + i->setHoldPosition(false); + } +} diff --git a/src/ATC/trafficcontrol.hxx b/src/ATC/trafficcontrol.hxx new file mode 100644 index 000000000..f2e63eff4 --- /dev/null +++ b/src/ATC/trafficcontrol.hxx @@ -0,0 +1,292 @@ +// trafficcontrol.hxx - classes to manage AIModels based air traffic control +// Written by Durk Talsma, started September 2006. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + + +#ifndef _TRAFFIC_CONTROL_HXX_ +#define _TRAFFIC_CONTROL_HXX_ + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include +#include + + + +#include STL_STRING +#include + +SG_USING_STD(string); +SG_USING_STD(vector); + + +typedef vector intVec; +typedef vector::iterator intVecIterator; + + +class FGAIFlightPlan; // forward reference +class FGGroundNetwork; // forward reference +//class FGAISchedule; // forward reference +class FGAIAircraft; // forward reference + +/************************************************************************************** + * class FGATCInstruction + * like class FGATC Controller, this class definition should go into its own file + * and or directory... For now, just testing this stuff out though... + *************************************************************************************/ +class FGATCInstruction +{ +private: + bool holdPattern; + bool holdPosition; + bool changeSpeed; + bool changeHeading; + bool changeAltitude; + bool resolveCircularWait; + + double speed; + double heading; + double alt; +public: + + FGATCInstruction(); + bool hasInstruction (); + bool getHoldPattern () { return holdPattern; }; + bool getHoldPosition () { return holdPosition; }; + bool getChangeSpeed () { return changeSpeed; }; + bool getChangeHeading () { return changeHeading; }; + bool getChangeAltitude() { return changeAltitude; }; + + double getSpeed () { return speed; }; + double getHeading () { return heading; }; + double getAlt () { return alt; }; + + bool getCheckForCircularWait() { return resolveCircularWait; }; + + void setHoldPattern (bool val) { holdPattern = val; }; + void setHoldPosition (bool val) { holdPosition = val; }; + void setChangeSpeed (bool val) { changeSpeed = val; }; + void setChangeHeading (bool val) { changeHeading = val; }; + void setChangeAltitude(bool val) { changeAltitude = val; }; + + void setResolveCircularWait (bool val) { resolveCircularWait = val; }; + + void setSpeed (double val) { speed = val; }; + void setHeading (double val) { heading = val; }; + void setAlt (double val) { alt = val; }; +}; + + + + + +/************************************************************************************** + * class FGTrafficRecord + *************************************************************************************/ +class FGTrafficRecord +{ +private: + int id, waitsForId; + int currentPos; + int leg; + int state; + time_t timer; + intVec intentions; + FGATCInstruction instruction; + double latitude, longitude, heading, speed, altitude, radius; + string runway; + //FGAISchedule *trafficRef; + FGAIAircraft *aircraft; + + +public: + FGTrafficRecord(); + + void setId(int val) { id = val; }; + void setRadius(double rad) { radius = rad;}; + void setPositionAndIntentions(int pos, FGAIFlightPlan *route); + void setRunway(string rwy) { runway = rwy;}; + void setLeg(int lg) { leg = lg;}; + int getId() { return id;}; + int getState() { return state;}; + FGATCInstruction getInstruction() { return instruction;}; + bool hasInstruction() { return instruction.hasInstruction(); }; + void setPositionAndHeading(double lat, double lon, double hdg, double spd, double alt); + bool checkPositionAndIntentions(FGTrafficRecord &other); + int crosses (FGGroundNetwork *, FGTrafficRecord &other); + bool isOpposing (FGGroundNetwork *, FGTrafficRecord &other, int node); + + bool onRoute(FGGroundNetwork *, FGTrafficRecord &other); + + bool getSpeedAdjustment() { return instruction.getChangeSpeed(); }; + + double getLatitude () { return latitude ; }; + double getLongitude() { return longitude; }; + double getHeading () { return heading ; }; + double getSpeed () { return speed ; }; + double getAltitude () { return altitude ; }; + double getRadius () { return radius ; }; + + int getWaitsForId () { return waitsForId; }; + + void setSpeedAdjustment(double spd); + void setHeadingAdjustment(double heading); + void clearSpeedAdjustment () { instruction.setChangeSpeed (false); }; + void clearHeadingAdjustment() { instruction.setChangeHeading(false); }; + + bool hasHeadingAdjustment() { return instruction.getChangeHeading(); }; + bool hasHoldPosition() { return instruction.getHoldPosition(); }; + void setHoldPosition (bool inst) { instruction.setHoldPosition(inst); }; + + void setWaitsForId(int id) { waitsForId = id; }; + + void setResolveCircularWait() { instruction.setResolveCircularWait(true); }; + void clearResolveCircularWait() { instruction.setResolveCircularWait(false); }; + + string getRunway() { return runway; }; + //void setCallSign(string clsgn) { callsign = clsgn; }; + void setAircraft(FGAIAircraft *ref) { aircraft = ref;}; + void updateState() { state++;}; + //string getCallSign() { return callsign; }; + FGAIAircraft *getAircraft() { return aircraft;}; + int getTime() { return timer; }; + int getLeg() { return leg; }; + void setTime(time_t time) { timer = time; }; +}; + +typedef vector TrafficVector; +typedef vector::iterator TrafficVectorIterator; + + +/*********************************************************************** + * Active runway, a utility class to keep track of which aircraft has + * clearance for a given runway. + **********************************************************************/ +class ActiveRunway +{ +private: + string rwy; + int currentlyCleared; +public: + ActiveRunway(string r, int cc) { rwy = r; currentlyCleared = cc; }; + + string getRunwayName() { return rwy; }; + int getCleared () { return currentlyCleared; }; +}; + +typedef vector ActiveRunwayVec; +typedef vector::iterator ActiveRunwayVecIterator; + +/** + * class FGATCController + * NOTE: this class serves as an abstraction layer for all sorts of ATC controller. + *************************************************************************************/ +class FGATCController +{ +private: + double dt_count; +public: + typedef enum { + MSG_ANNOUNCE_ENGINE_START, + MSG_REQUEST_ENGINE_START, + MSG_PERMIT_ENGINE_START, + MSG_DENY_ENGINE_START, + MSG_ACKNOWLEDGE_ENGINE_START } AtcMsgId; + typedef enum { + ATC_AIR_TO_GROUND, + ATC_GROUND_TO_AIR } AtcMsgDir; + FGATCController() { dt_count = 0;}; + virtual ~FGATCController() {}; + virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, + double lat, double lon, + double hdg, double spd, double alt, double radius, int leg, + FGAIAircraft *aircraft) = 0; + virtual void signOff(int id) = 0; + virtual void update(int id, double lat, double lon, + double heading, double speed, double alt, double dt) = 0; + virtual bool hasInstruction(int id) = 0; + virtual FGATCInstruction getInstruction(int id) = 0; + + double getDt() { return dt_count; }; + void setDt(double dt) { dt_count = dt;}; + void transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir msgDir); + string getGateName(FGAIAircraft *aircraft); +}; + +/****************************************************************************** + * class FGTowerControl + *****************************************************************************/ +class FGTowerController : public FGATCController +{ +private: + TrafficVector activeTraffic; + ActiveRunwayVec activeRunways; + +public: + FGTowerController(); + virtual ~FGTowerController() {}; + virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, + double lat, double lon, + double hdg, double spd, double alt, double radius, int leg, + FGAIAircraft *aircraft); + virtual void signOff(int id); + virtual void update(int id, double lat, double lon, + double heading, double speed, double alt, double dt); + virtual bool hasInstruction(int id); + virtual FGATCInstruction getInstruction(int id); + + bool hasActiveTraffic() { return activeTraffic.size() != 0; }; + TrafficVector &getActiveTraffic() { return activeTraffic; }; +}; + +/****************************************************************************** + * class FGStartupController + * handle + *****************************************************************************/ + +class FGStartupController : public FGATCController +{ +private: + TrafficVector activeTraffic; + bool available; + time_t lastTransmission; + //ActiveRunwayVec activeRunways; + +public: + FGStartupController(); + virtual ~FGStartupController() {}; + virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, + double lat, double lon, + double hdg, double spd, double alt, double radius, int leg, + FGAIAircraft *aircraft); + virtual void signOff(int id); + virtual void update(int id, double lat, double lon, + double heading, double speed, double alt, double dt); + virtual bool hasInstruction(int id); + virtual FGATCInstruction getInstruction(int id); + + bool hasActiveTraffic() { return activeTraffic.size() != 0; }; + TrafficVector &getActiveTraffic() { return activeTraffic; }; + +}; + +#endif // _TRAFFIC_CONTROL_HXX diff --git a/src/ATCDCL/Makefile.am b/src/ATCDCL/Makefile.am index c560d1643..a7c199283 100644 --- a/src/ATCDCL/Makefile.am +++ b/src/ATCDCL/Makefile.am @@ -1,6 +1,6 @@ -noinst_LIBRARIES = libATC.a +noinst_LIBRARIES = libATCDCL.a -libATC_a_SOURCES = \ +libATCDCL_a_SOURCES = \ ATC.hxx ATC.cxx \ atis.hxx atis.cxx \ tower.hxx tower.cxx \ diff --git a/src/Airports/Makefile.am b/src/Airports/Makefile.am index 5cc6abe15..a87d26c49 100644 --- a/src/Airports/Makefile.am +++ b/src/Airports/Makefile.am @@ -11,7 +11,6 @@ libAirports_a_SOURCES = \ gnnode.cxx gnnode.hxx \ groundnetwork.cxx groundnetwork.hxx \ dynamics.cxx dynamics.hxx \ - trafficcontrol.cxx trafficcontrol.hxx \ dynamicloader.cxx dynamicloader.hxx \ runwayprefloader.cxx runwayprefloader.hxx \ xmlloader.cxx xmlloader.hxx diff --git a/src/Airports/dynamicloader.cxx b/src/Airports/dynamicloader.cxx index 986b1dd39..3f6733be1 100644 --- a/src/Airports/dynamicloader.cxx +++ b/src/Airports/dynamicloader.cxx @@ -34,6 +34,7 @@ void FGAirportDynamicsXMLLoader::startElement (const char * name, const XMLAttr FGTaxiNode taxiNode; FGTaxiSegment taxiSegment; int index = 0; + string idxStr; taxiSegment.setIndex(index); //cout << "Start element " << name << endl; string attname; @@ -44,15 +45,19 @@ void FGAirportDynamicsXMLLoader::startElement (const char * name, const XMLAttr string lat; string lon; int holdPointType; + int pushBackPoint; if (name == string("Parking")) { + pushBackPoint = 0; for (int i = 0; i < atts.size(); i++) { //cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl; attname = atts.getName(i); - if (attname == string("index")) - park.setIndex(std::atoi(atts.getValue(i))); + if (attname == string("index")) { + park.setIndex(std::atoi(atts.getValue(i))); + idxStr = atts.getValue(i); + } else if (attname == string("type")) park.setType(atts.getValue(i)); else if (attname == string("name")) @@ -72,10 +77,17 @@ void FGAirportDynamicsXMLLoader::startElement (const char * name, const XMLAttr //cerr << "Radius " << radius <addParking(park); } if (name == string("node")) @@ -128,16 +140,41 @@ void FGAirportDynamicsXMLLoader::startElement (const char * name, const XMLAttr void FGAirportDynamicsXMLLoader::endElement (const char * name) { //cout << "End element " << name << endl; + if (name == string("AWOS")) { + _dynamics->addAwosFreq(atoi(value.c_str())); + //cerr << "Adding AWOS" << value<< endl; + } + if (name == string("UNICOM")) { + _dynamics->addUnicomFreq(atoi(value.c_str())); + //cerr << "UNICOM" << value<< endl; + } +if (name == string("CLEARANCE")) { + _dynamics->addClearanceFreq(atoi(value.c_str())); + //cerr << "Adding CLEARANCE" << value<< endl; + } +if (name == string("GROUND")) { + _dynamics->addGroundFreq(atoi(value.c_str())); + //cerr << "Adding GROUND" << value<< endl; + } + +if (name == string("TOWER")) { + _dynamics->addTowerFreq(atoi(value.c_str())); + //cerr << "Adding TOWER" << value<< endl; + } +if (name == string("APPROACH")) { + _dynamics->addApproachFreq(atoi(value.c_str())); + //cerr << "Adding approach" << value<< endl; + } } void FGAirportDynamicsXMLLoader::data (const char * s, int len) { string token = string(s,len); //cout << "Character data " << string(s,len) << endl; - //if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos)) - //value += token; - //else - //value = string(""); + if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos)) + value += token; + else + value = string(""); } void FGAirportDynamicsXMLLoader::pi (const char * target, const char * data) { diff --git a/src/Airports/dynamicloader.hxx b/src/Airports/dynamicloader.hxx index 8f86cf6ab..672cda5af 100644 --- a/src/Airports/dynamicloader.hxx +++ b/src/Airports/dynamicloader.hxx @@ -36,6 +36,7 @@ protected: private: FGAirportDynamics* _dynamics; + string value; }; #endif diff --git a/src/Airports/dynamics.cxx b/src/Airports/dynamics.cxx index 0123013ca..4cb904abf 100644 --- a/src/Airports/dynamics.cxx +++ b/src/Airports/dynamics.cxx @@ -205,10 +205,10 @@ bool FGAirportDynamics::getAvailableParking(double *lat, double *lon, double *he if (!(i->getCodes().empty())) { if ((i->getCodes().find(airline,0) == string::npos)) - { - available = false; - continue; - } + { + available = false; + continue; + } } if (i->getType() != flType) { @@ -231,7 +231,7 @@ bool FGAirportDynamics::getAvailableParking(double *lat, double *lon, double *he i->setAvailable(false); found = true; } - } + } // And finally once more if that didn't work. Now ignore the airline codes, as a last resort for (i = parkings.begin(); !(i == parkings.end() || found); i++) { @@ -303,18 +303,27 @@ void FGAirportDynamics::getParking (int id, double *lat, double* lon, double *he } } -FGParking *FGAirportDynamics::getParking(int i) +FGParking *FGAirportDynamics::getParking(int id) { - if (i < (int)parkings.size() && (i >= 0)) - return &(parkings[i]); - else + FGParkingVecIterator i = parkings.begin(); + for (i = parkings.begin(); i != parkings.end(); i++) + { + if (id == i->getIndex()) { + return &(*i); + } + } return 0; } -string FGAirportDynamics::getParkingName(int i) +string FGAirportDynamics::getParkingName(int id) { - if (i < (int)parkings.size() && i >= 0) - return (parkings[i].getName()); - else + FGParkingVecIterator i = parkings.begin(); + for (i = parkings.begin(); i != parkings.end(); i++) + { + if (id == i->getIndex()) { + return i->getName(); + } + } + return string("overflow"); } void FGAirportDynamics::releaseParking(int id) diff --git a/src/Airports/dynamics.hxx b/src/Airports/dynamics.hxx index 0e5d6f438..f6e344932 100644 --- a/src/Airports/dynamics.hxx +++ b/src/Airports/dynamics.hxx @@ -29,22 +29,27 @@ #include +#include #include "parking.hxx" #include "groundnetwork.hxx" #include "runwayprefs.hxx" -#include "trafficcontrol.hxx" + +//typedef vector DoubleVec; +//typedef vector::iterator DoubleVecIterator; class FGAirport; + class FGAirportDynamics { private: FGAirport* _ap; - FGParkingVec parkings; - FGRunwayPreference rwyPrefs; - FGGroundNetwork groundNetwork; - FGTowerController towerController; + FGParkingVec parkings; + FGRunwayPreference rwyPrefs; + FGStartupController startupController; + FGGroundNetwork groundNetwork; + FGTowerController towerController; time_t lastUpdate; string prevTrafficType; @@ -52,6 +57,12 @@ private: stringVec takeoff; stringVec milActive, comActive, genActive, ulActive; stringVec *currentlyActive; + intVec freqAwos; // + intVec freqUnicom; // + intVec freqClearance;// + intVec freqGround; // + intVec freqTower; // + intVec freqApproach; // // Experimental keep a running average of wind dir and speed to prevent // Erratic runway changes. @@ -67,6 +78,12 @@ public: FGAirportDynamics(const FGAirportDynamics &other); ~FGAirportDynamics(); + void addAwosFreq (int val) { freqAwos.push_back(val); }; + void addUnicomFreq (int val) { freqUnicom.push_back(val); }; + void addClearanceFreq(int val) { freqClearance.push_back(val); }; + void addGroundFreq (int val) { freqGround.push_back(val); }; + void addTowerFreq (int val) { freqTower.push_back(val); }; + void addApproachFreq (int val) { freqApproach.push_back(val); }; void init(); double getLongitude() const; @@ -90,9 +107,12 @@ public: //FGAirport *getAddress() { return this; }; //const string &getName() const { return _name;}; // Returns degrees + int getGroundFrequency() { return freqGround.size() ? freqGround[0] : 0; }; + + FGStartupController *getStartupController() { return &startupController; }; + FGGroundNetwork *getGroundNetwork() { return &groundNetwork; }; + FGTowerController *getTowerController() { return &towerController; }; - FGGroundNetwork *getGroundNetwork() { return &groundNetwork; }; - FGTowerController *getTowerController() { return &towerController; }; void setRwyUse(const FGRunwayPreference& ref); diff --git a/src/Airports/groundnetwork.cxx b/src/Airports/groundnetwork.cxx index 05f786a20..1f2f0fcab 100644 --- a/src/Airports/groundnetwork.cxx +++ b/src/Airports/groundnetwork.cxx @@ -476,189 +476,12 @@ int FGTaxiSegment::getPenalty(int nGates) { return penalty; } -// void FGGroundNetwork::trace(FGTaxiNode *currNode, int end, int depth, double distance) -// { -// // Just check some preconditions of the trace algorithm -// if (nodesStack.size() != routesStack.size()) -// { -// SG_LOG(SG_GENERAL, SG_ALERT, "size of nodesStack and routesStack is not equal. NodesStack :" -// << nodesStack.size() << ". RoutesStack : " << routesStack.size()); -// } -// nodesStack.push_back(currNode->getIndex()); -// totalDistance += distance; -// //cerr << "Starting trace " << currNode->getIndex() << " " << "total distance: " << totalDistance << endl; -// // << currNode->getIndex() << endl; -// -// // If the current route matches the required end point we found a valid route -// // So we can add this to the routing table -// if (currNode->getIndex() == end) -// { -// maxDepth = depth; -// //cerr << "Found route : " << totalDistance << "" << " " << *(nodesStack.end()-1) << " Depth = " << depth << endl; -// routes.push_back(FGTaxiRoute(nodesStack,routesStack,totalDistance, depth)); -// if (nodesStack.empty() || routesStack.empty()) -// { -// printRoutingError(string("while finishing route")); -// } -// nodesStack.pop_back(); -// routesStack.pop_back(); -// if (!(foundRoute)) { -// maxDistance = totalDistance; -// } -// else -// if (totalDistance < maxDistance) -// maxDistance = totalDistance; -// foundRoute = true; -// totalDistance -= distance; -// return; -// } -// -// -// // search if the currentNode has been encountered before -// // if so, we should step back one level, because it is -// // rather rediculous to proceed further from here. -// // if the current node has not been encountered before, -// // i should point to nodesStack.end()-1; and we can continue -// // if i is not nodesStack.end, the previous node was found, -// // and we should return. -// // This only works at trace levels of 1 or higher though -// if (depth > 0) { -// intVecIterator i = nodesStack.begin(); -// while ((*i) != currNode->getIndex()) { -// //cerr << "Route so far : " << (*i) << endl; -// i++; -// } -// if (i != nodesStack.end()-1) { -// if (nodesStack.empty() || routesStack.empty()) -// { -// printRoutingError(string("while returning from an already encountered node")); -// } -// nodesStack.pop_back(); -// routesStack.pop_back(); -// totalDistance -= distance; -// return; -// } -// if (depth >= maxDepth) { -// count++; -// if (!(count % 100000)) { -// maxDepth--; // Gradually decrease maxdepth, to prevent "eternal searches" -// //cerr << "Reducing maxdepth to " << maxDepth << endl; -// } -// nodesStack.pop_back(); -// routesStack.pop_back(); -// totalDistance -= distance; -// return; -// } -// // If the total distance from start to the current waypoint -// // is longer than that of a route we can also stop this trace -// // and go back one level. -// if ((totalDistance > maxDistance) && foundRoute) -// //if (foundRoute) -// { -// //cerr << "Stopping rediculously long trace: " << totalDistance << endl; -// if (nodesStack.empty() || routesStack.empty()) -// { -// printRoutingError(string("while returning from finding a rediculously long route")); -// } -// nodesStack.pop_back(); -// routesStack.pop_back(); -// totalDistance -= distance; -// return; -// } -// } -/* - //cerr << "2" << endl; - if (currNode->getBeginRoute() != currNode->getEndRoute()) - { - double course, length; - //cerr << "3" << endl; - // calculate distance and heading "as the crow flies" between starn and end points" - SGWayPoint first(currNode->getLongitude(), - currNode->getLatitude(), - 0); - //SGWayPoint second (lastNode->getLongitude(), - // lastNode->getLatitude(), - // 0); - - first.CourseAndDistance(destination, &course, &length); - //for (FGTaxiSegmentVectorIterator - // itr = segments.begin(); - // itr != segments.end(); itr++) - // { - // (*itr)->setCourseDiff(course); - // } - //FGTaxiNodeVectorIterator nde = nodes.begin(); - //while (nde != nodes.end()) { - //(*nde)->sortEndSegments(); - //nde++; - - for (FGTaxiSegmentVectorIterator - i = currNode->getBeginRoute(); - i != currNode->getEndRoute(); - i++) - { - (*i)->setCourseDiff(course); - } - currNode->sortEndSegments(foundRoute); - for (FGTaxiSegmentVectorIterator - i = currNode->getBeginRoute(); - i != currNode->getEndRoute(); - i++) - { - //cerr << (*i)->getLength() << endl; - //cerr << (*i)->getIndex() << endl; - int idx = (*i)->getIndex(); - routesStack.push_back((*i)->getIndex()); - trace((*i)->getEnd(), end, depth+1, (*i)->getLength()); - // { - // // cerr << currNode -> getIndex() << " "; - // route.push_back(currNode->getIndex()); - // return true; - // } - } - } - else - { - //SG_LOG( SG_GENERAL, SG_DEBUG, "4" ); - } - if (nodesStack.empty()) - { - printRoutingError(string("while finishing trace")); - } - nodesStack.pop_back(); - // Make sure not to dump the level-zero routesStack entry, because that was never created. - if (depth) - { - routesStack.pop_back(); - //cerr << "leaving trace " << routesStack.size() << endl; - } - totalDistance -= distance; - return; -}*/ -/* -void FGGroundNetwork::printRoutingError(string mess) -{ - SG_LOG(SG_GENERAL, SG_ALERT, "Error in ground network trace algorithm " << mess); - if (nodesStack.empty()) - { - SG_LOG(SG_GENERAL, SG_ALERT, " nodesStack is empty. Dumping routesStack"); - for (intVecIterator i = routesStack.begin() ; i != routesStack.end(); i++) - SG_LOG(SG_GENERAL, SG_ALERT, "Route " << (*i)); - } - if (routesStack.empty()) - { - SG_LOG(SG_GENERAL, SG_ALERT, " routesStack is empty. Dumping nodesStack"); - for (intVecIterator i = nodesStack.begin() ; i != nodesStack.end(); i++) - SG_LOG(SG_GENERAL, SG_ALERT, "Node " << (*i)); - } - //exit(1); -} -*/ +/* ATC Related Functions */ void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition, double lat, double lon, double heading, double speed, double alt, double radius, int leg, - string callsign) + FGAIAircraft *aircraft) { TrafficVectorIterator i = activeTraffic.begin(); // Search search if the current id alread has an entry @@ -679,7 +502,7 @@ void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, in rec.setPositionAndIntentions(currentPosition, intendedRoute); rec.setPositionAndHeading(lat, lon, heading, speed, alt); rec.setRadius(radius); // only need to do this when creating the record. - rec.setCallSign(callsign); + rec.setAircraft(aircraft); activeTraffic.push_back(rec); } else { i->setPositionAndIntentions(currentPosition, intendedRoute); diff --git a/src/Airports/groundnetwork.hxx b/src/Airports/groundnetwork.hxx index f27aff980..33adb7a13 100644 --- a/src/Airports/groundnetwork.hxx +++ b/src/Airports/groundnetwork.hxx @@ -35,7 +35,7 @@ SG_USING_STD(vector); #include "gnnode.hxx" #include "parking.hxx" -#include "trafficcontrol.hxx" +#include class FGTaxiSegment; // forward reference class FGAIFlightPlan; // forward reference @@ -265,7 +265,7 @@ public: virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, double lat, double lon, double hdg, double spd, double alt, - double radius, int leg, string callsign); + double radius, int leg, FGAIAircraft *aircraft); virtual void signOff(int id); virtual void update(int id, double lat, double lon, double heading, double speed, double alt, double dt); virtual bool hasInstruction(int id); diff --git a/src/Airports/parking.hxx b/src/Airports/parking.hxx index 3b50d5d2a..908acf029 100644 --- a/src/Airports/parking.hxx +++ b/src/Airports/parking.hxx @@ -58,11 +58,8 @@ public: FGParking() : heading(0), radius(0), - //parkingName(0), - //type(0), - //airlineCodes(0), available(true), - pushBackPoint(-1), + pushBackPoint(0), pushBackRoute(0) { }; diff --git a/src/Airports/runwayprefs.cxx b/src/Airports/runwayprefs.cxx index 48a0d6ed4..a6112e44e 100644 --- a/src/Airports/runwayprefs.cxx +++ b/src/Airports/runwayprefs.cxx @@ -118,7 +118,7 @@ string ScheduleTime::getName(time_t dayStart) //couldn't find one so return 0; //cerr << "Returning 0 " << endl; } - return string(0); + return string(""); } /****************************************************************************** * RunwayList diff --git a/src/Main/Makefile.am b/src/Main/Makefile.am index 9be3ca374..55376f2bb 100644 --- a/src/Main/Makefile.am +++ b/src/Main/Makefile.am @@ -70,7 +70,7 @@ fgfs_SOURCES = bootstrap.cxx fgfs_LDADD = \ $(top_builddir)/src/Main/libMain.a \ $(top_builddir)/src/Aircraft/libAircraft.a \ - $(top_builddir)/src/ATCDCL/libATC.a \ + $(top_builddir)/src/ATCDCL/libATCDCL.a \ $(top_builddir)/src/Cockpit/libCockpit.a \ $(top_builddir)/src/Cockpit/built_in/libBuilt_in.a \ $(top_builddir)/src/FDM/libFlight.a \ @@ -97,6 +97,7 @@ fgfs_LDADD = \ $(top_builddir)/src/Airports/libAirports.a \ $(MPLAYER_LIBS) \ $(top_builddir)/src/AIModel/libAIModel.a \ + $(top_builddir)/src/ATC/libATC.a \ $(top_builddir)/src/Systems/libSystems.a \ $(top_builddir)/src/Time/libTime.a \ $(top_builddir)/src/Traffic/libTraffic.a \ diff --git a/src/Makefile.am b/src/Makefile.am index af9ce9c76..178419eb0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,6 +2,7 @@ SUBDIRS = \ Include \ Aircraft \ Airports \ + ATC \ ATCDCL \ Autopilot \ Cockpit \ -- 2.39.5