diff --git a/src/ATC/ATCController.cxx b/src/ATC/ATCController.cxx new file mode 100644 index 000000000..188833c4d --- /dev/null +++ b/src/ATC/ATCController.cxx @@ -0,0 +1,433 @@ +// Extracted from 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$ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "trafficcontrol.hxx" +#include "atc_mgr.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using std::sort; +using std::string; + +/*************************************************************************** + * FGATCController + * + **************************************************************************/ + +FGATCController::FGATCController() +{ + dt_count = 0; + available = true; + lastTransmission = 0; + initialized = false; + lastTransmissionDirection = ATC_AIR_TO_GROUND; + group = NULL; +} + +FGATCController::~FGATCController() +{ + if (initialized) { + auto mgr = globals->get_subsystem(); + mgr->removeController(this); + } +} + +void FGATCController::init() +{ + if (!initialized) { + auto mgr = globals->get_subsystem(); + mgr->addController(this); + initialized = true; + } +} + +string FGATCController::getGateName(FGAIAircraft * ref) +{ + return ref->atGate(); +} + +bool FGATCController::isUserAircraft(FGAIAircraft* ac) +{ + return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false; +}; + +void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, AtcMsgId msgId, + AtcMsgDir msgDir, bool audible) +{ + string sender, receiver; + int stationFreq = 0; + int taxiFreq = 0; + int towerFreq = 0; + int freqId = 0; + string atisInformation; + string text; + string taxiFreqStr; + string towerFreqStr; + double heading = 0; + string activeRunway; + string fltType; + string rwyClass; + string SID; + string transponderCode; + FGAIFlightPlan *fp; + string fltRules; + string instructionText; + int ground_to_air=0; + + //double commFreqD; + sender = rec->getAircraft()->getTrafficRef()->getCallSign(); + if (rec->getAircraft()->getTaxiClearanceRequest()) { + instructionText = "push-back and taxi"; + } else { + instructionText = "taxi"; + } + + SG_LOG(SG_ATC, SG_DEBUG, "transmitting for: " << sender << "Leg = " << rec->getLeg()); + + auto depApt = rec->getAircraft()->getTrafficRef()->getDepartureAirport(); + + //FIXME Must be transferred to controller and fallback for GROUND -> TOWER if GROUND not set + switch (rec->getLeg()) { + case 1: + case 2: + // avoid crash FLIGHTGEAR-ER + if (!depApt) { + SG_LOG(SG_ATC, SG_DEV_ALERT, "TrafficRec has empty departure airport, can't transmit"); + return; + } + + freqId = rec->getNextFrequency(); + stationFreq = depApt->getDynamics()->getGroundFrequency(rec->getLeg() + freqId); + taxiFreq = depApt->getDynamics()->getGroundFrequency(2); + towerFreq = depApt->getDynamics()->getTowerFrequency(2); + receiver = depApt->getName() + "-Ground"; + atisInformation = depApt->getDynamics()->getAtisSequence(); + break; + case 3: + if (!depApt) { + SG_LOG(SG_ATC, SG_DEV_ALERT, "TrafficRec has empty departure airport, can't transmit"); + return; + } + + receiver = depApt->getName() + "-Tower"; + break; + } + + // Swap sender and receiver value in case of a ground to air transmission + if (msgDir == ATC_GROUND_TO_AIR) { + string tmp = sender; + sender = receiver; + receiver = tmp; + ground_to_air = 1; + } + + 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 " + + atisInformation + ". " + + rec->getAircraft()->getTrafficRef()->getFlightRules() + + " to " + + rec->getAircraft()->getTrafficRef()->getArrivalAirport()-> + getName() + ". Request start-up."; + break; + // Acknowledge engine startup permission + // Assign departure runway + // Assign SID, if necessery (TODO) + case MSG_PERMIT_ENGINE_START: + taxiFreqStr = formatATCFrequency3_2(taxiFreq); + + heading = rec->getAircraft()->getTrafficRef()->getCourse(); + fltType = rec->getAircraft()->getTrafficRef()->getFlightType(); + rwyClass = + rec->getAircraft()->GetFlightPlan()-> + getRunwayClassFromTrafficType(fltType); + + rec->getAircraft()->getTrafficRef()->getDepartureAirport()-> + getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, + heading); + rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway); + fp = NULL; + rec->getAircraft()->GetFlightPlan()->setSID(fp); + if (fp) { + SID = fp->getName() + " departure"; + } else { + SID = "fly runway heading "; + } + //snprintf(buffer, 7, "%3.2f", heading); + fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules(); + transponderCode = genTransponderCode(fltRules); + rec->getAircraft()->SetTransponderCode(transponderCode); + text = + receiver + ". Start-up approved. " + atisInformation + + " correct, runway " + activeRunway + ", " + SID + ", squawk " + + transponderCode + ". " + + "For "+ instructionText + " clearance call " + taxiFreqStr + ". " + + sender + " control."; + break; + case MSG_DENY_ENGINE_START: + text = receiver + ". Standby."; + break; + case MSG_ACKNOWLEDGE_ENGINE_START: + fp = rec->getAircraft()->GetFlightPlan()->getSID(); + if (fp) { + SID = + rec->getAircraft()->GetFlightPlan()->getSID()->getName() + + " departure"; + } else { + SID = "fly runway heading "; + } + taxiFreqStr = formatATCFrequency3_2(taxiFreq); + activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway(); + transponderCode = rec->getAircraft()->GetTransponderCode(); + + text = + receiver + ". Start-up approved. " + atisInformation + + " correct, runway " + activeRunway + ", " + SID + ", squawk " + + transponderCode + ". " + + "For " + instructionText + " clearance call " + taxiFreqStr + ". " + + sender + "."; + break; + case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY: + taxiFreqStr = formatATCFrequency3_2(taxiFreq); + text = receiver + ". Switching to " + taxiFreqStr + ". " + sender + "."; + break; + case MSG_INITIATE_CONTACT: + text = receiver + ". With you. " + sender + "."; + break; + case MSG_ACKNOWLEDGE_INITIATE_CONTACT: + text = receiver + ". Roger. " + sender + "."; + break; + case MSG_REQUEST_PUSHBACK_CLEARANCE: + if (rec->getAircraft()->getTaxiClearanceRequest()) { + text = receiver + ". Request push-back. " + sender + "."; + } else { + text = receiver + ". Request Taxi clearance. " + sender + "."; + } + break; + case MSG_PERMIT_PUSHBACK_CLEARANCE: + if (rec->getAircraft()->getTaxiClearanceRequest()) { + text = receiver + ". Push-back approved. " + sender + "."; + } else { + text = receiver + ". Cleared to Taxi. " + sender + "."; + } + break; + case MSG_HOLD_PUSHBACK_CLEARANCE: + text = receiver + ". Standby. " + sender + "."; + break; + case MSG_REQUEST_TAXI_CLEARANCE: + text = receiver + ". Ready to Taxi. " + sender + "."; + break; + case MSG_ISSUE_TAXI_CLEARANCE: + text = receiver + ". Cleared to taxi. " + sender + "."; + break; + case MSG_ACKNOWLEDGE_TAXI_CLEARANCE: + text = receiver + ". Cleared to taxi. " + sender + "."; + break; + case MSG_HOLD_POSITION: + text = receiver + ". Hold Position. " + sender + "."; + break; + case MSG_ACKNOWLEDGE_HOLD_POSITION: + text = receiver + ". Holding Position. " + sender + "."; + break; + case MSG_RESUME_TAXI: + text = receiver + ". Resume Taxiing. " + sender + "."; + break; + case MSG_ACKNOWLEDGE_RESUME_TAXI: + text = receiver + ". Continuing Taxi. " + sender + "."; + break; + case MSG_REPORT_RUNWAY_HOLD_SHORT: + activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway(); + //activeRunway = "test"; + text = receiver + ". Holding short runway " + + activeRunway + + ". " + sender + "."; + //text = "test1"; + SG_LOG(SG_ATC, SG_DEBUG, "1 Currently at leg " << rec->getLeg()); + break; + case MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT: + activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway(); + text = receiver + " Roger. Holding short runway " + // + activeRunway + + ". " + sender + "."; + //text = "test2"; + SG_LOG(SG_ATC, SG_DEBUG, "2 Currently at leg " << rec->getLeg()); + break; + case MSG_SWITCH_TOWER_FREQUENCY: + towerFreqStr = formatATCFrequency3_2(towerFreq); + text = receiver + " Contact Tower at " + towerFreqStr + ". " + sender + "."; + //text = "test3"; + SG_LOG(SG_ATC, SG_DEBUG, "3 Currently at leg " << rec->getLeg()); + break; + case MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY: + towerFreqStr = formatATCFrequency3_2(towerFreq); + text = receiver + " Roger, switching to tower at " + towerFreqStr + ". " + sender + "."; + //text = "test4"; + SG_LOG(SG_ATC, SG_DEBUG, "4 Currently at leg " << rec->getLeg()); + break; + default: + //text = "test3"; + text = text + sender + ". Transmitting unknown Message."; + break; + } + + const bool atcAudioEnabled = fgGetBool("/sim/sound/atc/enabled", false); + if (audible && atcAudioEnabled) { + double onBoardRadioFreq0 = + fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz"); + double onBoardRadioFreq1 = + fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz"); + int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5); + int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5); + SG_LOG(SG_ATC, SG_DEBUG, "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl); + + // Display ATC message only when one of the radios is tuned + // the relevant frequency. + // Note that distance attenuation is currently not yet implemented + + if ((stationFreq > 0)&& + ((onBoardRadioFreqI0 == stationFreq)|| + (onBoardRadioFreqI1 == stationFreq))) { + if (rec->allowTransmissions()) { + + if( fgGetBool( "/sim/radio/use-itm-attenuation", false ) ) { + SG_LOG(SG_ATC, SG_DEBUG, "Using ITM radio propagation"); + FGRadioTransmission* radio = new FGRadioTransmission(); + SGGeod sender_pos; + double sender_alt_ft, sender_alt; + if(ground_to_air) { + sender_pos = parent->parent()->geod(); + } + else { + sender_alt_ft = rec->getAltitude(); + sender_alt = sender_alt_ft * SG_FEET_TO_METER; + sender_pos= SGGeod::fromDegM( rec->getLongitude(), + rec->getLatitude(), sender_alt ); + } + double frequency = ((double)stationFreq) / 100; + radio->receiveATC(sender_pos, frequency, text, ground_to_air); + delete radio; + } + else { + fgSetString("/sim/messages/atc", text.c_str()); + } + } + } + } else { + //FGATCDialogNew::instance()->addEntry(1, text); + } +} + +/* +* Format integer frequency xxxyy as xxx.yy +* @param freq - integer value +* @return the formatted string +*/ +string FGATCController::formatATCFrequency3_2(int freq) +{ + char buffer[7]; // does this ever need to be freed? + snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0)); + return string(buffer); +} + +// TODO: Set transponder codes according to real-world routes. +// The current version just returns a random string of four octal numbers. +string FGATCController::genTransponderCode(const string& fltRules) +{ + if (fltRules == "VFR") + return string("1200"); + + std::default_random_engine generator; + std::uniform_int_distribution distribution(0, 7); + + unsigned val = ( + distribution(generator) * 1000 + + distribution(generator) * 100 + + distribution(generator) * 10 + + distribution(generator)); + + return std::to_string(val); +} + +void FGATCController::eraseDeadTraffic(TrafficVector& vec) +{ + auto it = std::remove_if(vec.begin(), vec.end(), [](const FGTrafficRecord& traffic) + { + if (!traffic.getAircraft()) { + return true; + } + return traffic.getAircraft()->getDie(); + }); + vec.erase(it, vec.end()); +} + +void FGATCController::clearTrafficControllers(TrafficVector& vec) +{ + for (const auto& traffic : vec) { + if (!traffic.getAircraft()) { + continue; + } + + traffic.getAircraft()->clearATCController(); + } +} + +TrafficVectorIterator FGATCController::searchActiveTraffic(TrafficVector& vec, int id) +{ + return std::find_if(vec.begin(), vec.end(), [id] (const FGTrafficRecord& rec) + { return rec.getId() == id; }); +} + diff --git a/src/ATC/ATCController.hxx b/src/ATC/ATCController.hxx new file mode 100644 index 000000000..5d381a0eb --- /dev/null +++ b/src/ATC/ATCController.hxx @@ -0,0 +1,129 @@ +// Extracted from 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 ATC_CONTROLLER_HXX +#define ATC_CONTROLLER_HXX + +#include + +#include +#include +#include +#include + +#include +// There is probably a better include than sg_geodesy to get the SG_NM_TO_METER... +#include +#include +#include +#include + +#include + +/** + * class FGATCController + * NOTE: this class serves as an abstraction layer for all sorts of ATC controllers. + *************************************************************************************/ +class FGATCController +{ +private: + + +protected: + bool initialized; + bool available; + time_t lastTransmission; + + double dt_count; + osg::Group* group; + + std::string formatATCFrequency3_2(int ); + std::string genTransponderCode(const std::string& fltRules); + bool isUserAircraft(FGAIAircraft*); + void clearTrafficControllers(TrafficVector& vec); + TrafficVectorIterator searchActiveTraffic(TrafficVector& vec, int id); + void eraseDeadTraffic(TrafficVector& vec); +public: + typedef enum { + MSG_ANNOUNCE_ENGINE_START, + MSG_REQUEST_ENGINE_START, + MSG_PERMIT_ENGINE_START, + MSG_DENY_ENGINE_START, + MSG_ACKNOWLEDGE_ENGINE_START, + MSG_REQUEST_PUSHBACK_CLEARANCE, + MSG_PERMIT_PUSHBACK_CLEARANCE, + MSG_HOLD_PUSHBACK_CLEARANCE, + MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, + MSG_INITIATE_CONTACT, + MSG_ACKNOWLEDGE_INITIATE_CONTACT, + MSG_REQUEST_TAXI_CLEARANCE, + MSG_ISSUE_TAXI_CLEARANCE, + MSG_ACKNOWLEDGE_TAXI_CLEARANCE, + MSG_HOLD_POSITION, + MSG_ACKNOWLEDGE_HOLD_POSITION, + MSG_RESUME_TAXI, + MSG_ACKNOWLEDGE_RESUME_TAXI, + MSG_REPORT_RUNWAY_HOLD_SHORT, + MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT, + MSG_SWITCH_TOWER_FREQUENCY, + MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY + } AtcMsgId; + + typedef enum { + ATC_AIR_TO_GROUND, + ATC_GROUND_TO_AIR + } AtcMsgDir; + FGATCController(); + virtual ~FGATCController(); + void init(); + + 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 updateAircraftInformation(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, FGAirportDynamics *parent, AtcMsgId msgId, AtcMsgDir msgDir, bool audible); + std::string getGateName(FGAIAircraft *aircraft); + virtual void render(bool) = 0; + virtual std::string getName() = 0; + + virtual void update(double) = 0; + + +protected: + // guard variable to avoid modifying state during destruction + bool _isDestroying = false; + +private: + + AtcMsgDir lastTransmissionDirection; +}; + +#endif \ No newline at end of file diff --git a/src/ATC/ApproachController.cxx b/src/ATC/ApproachController.cxx new file mode 100644 index 000000000..041d8cbea --- /dev/null +++ b/src/ATC/ApproachController.cxx @@ -0,0 +1,232 @@ +// Extracted from 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$ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "trafficcontrol.hxx" +#include "atc_mgr.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using std::sort; +using std::string; + +/*************************************************************************** + * class FGApproachController + * subclass of FGATCController + **************************************************************************/ + +FGApproachController::FGApproachController(FGAirportDynamics *par): + FGATCController() +{ + parent = par; +} + +FGApproachController::~FGApproachController() +{ + _isDestroying = true; + clearTrafficControllers(activeTraffic); +} + + +void FGApproachController::announcePosition(int id, + FGAIFlightPlan * intendedRoute, + int currentPosition, + double lat, double lon, + double heading, double speed, + double alt, double radius, + int leg, FGAIAircraft * ref) +{ + init(); + + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + + // Add a new TrafficRecord if no one exsists for this aircraft. + if (i == activeTraffic.end() || activeTraffic.empty()) { + 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 FGApproachController::updateAircraftInformation(int id, double lat, double lon, + double heading, double speed, double alt, + double dt) +{ + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + TrafficVectorIterator current; + + // update position of the current aircraft + if (i == activeTraffic.end() || activeTraffic.empty()) { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: updating aircraft without traffic record at " << SG_ORIGIN); + } else { + i->setPositionAndHeading(lat, lon, heading, speed, alt); + current = i; + SG_LOG(SG_ATC, SG_BULK, "ApproachController: checking for speed"); + time_t time_diff = + current->getAircraft()-> + checkForArrivalTime(string("final001")); + if (time_diff > 15) { + current->setSpeedAdjustment(current->getAircraft()-> + getPerformance()->vDescent() * + 1.35); + } else if (time_diff > 5) { + current->setSpeedAdjustment(current->getAircraft()-> + getPerformance()->vDescent() * + 1.2); + } else if (time_diff < -15) { + current->setSpeedAdjustment(current->getAircraft()-> + getPerformance()->vDescent() * + 0.65); + } else if (time_diff < -5) { + current->setSpeedAdjustment(current->getAircraft()-> + getPerformance()->vDescent() * + 0.8); + } else { + current->clearSpeedAdjustment(); + } + //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff); + } + setDt(getDt() + dt); +} + +/* Search for and erase traffic record with a specific id */ +void FGApproachController::signOff(int id) +{ + // ensure we don't modify activeTraffic during destruction + if (_isDestroying) + return; + + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + + if (i == activeTraffic.end() || activeTraffic.empty()) { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: Aircraft without traffic record is signing off from approach at " << SG_ORIGIN); + } else { + i = activeTraffic.erase(i); + } +} + +/* Periodically check for and remove dead traffic records */ +void FGApproachController::update(double dt) +{ + FGATCController::eraseDeadTraffic(activeTraffic); +} + +bool FGApproachController::hasInstruction(int id) +{ + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + + if (i == activeTraffic.end() || activeTraffic.empty()) { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN); + } else { + return i->hasInstruction(); + } + return false; +} + + +FGATCInstruction FGApproachController::getInstruction(int id) +{ + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN); + } else { + return i->getInstruction(); + } + return FGATCInstruction(); +} + + +ActiveRunway *FGApproachController::getRunway(const string& name) +{ + ActiveRunwayVecIterator rwy = activeRunways.begin(); + if (activeRunways.size()) { + while (rwy != activeRunways.end()) { + if (rwy->getRunwayName() == name) { + break; + } + rwy++; + } + } + if (rwy == activeRunways.end()) { + ActiveRunway aRwy(name, 0); + activeRunways.push_back(aRwy); + rwy = activeRunways.end() - 1; + } + return &(*rwy); +} + +void FGApproachController::render(bool visible) { + // Must be BULK in order to prevent it being called each frame + SG_LOG(SG_ATC, SG_BULK, "FGApproachController::render function not yet implemented"); +} + +string FGApproachController::getName() { + return string(parent->getId() + "-approach"); +} diff --git a/src/ATC/ApproachController.hxx b/src/ATC/ApproachController.hxx new file mode 100644 index 000000000..9bd52571a --- /dev/null +++ b/src/ATC/ApproachController.hxx @@ -0,0 +1,78 @@ +// Extracted from 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 APPROACH_CONTROLLER_HXX +#define APPROACH_CONTROLLER_HXX + +#include + +#include +#include +#include +#include + +#include +// There is probably a better include than sg_geodesy to get the SG_NM_TO_METER... +#include +#include +#include +#include + +#include +#include + +/****************************************************************************** + * class FGTowerControl + *****************************************************************************/ +class FGApproachController : public FGATCController +{ +private: + TrafficVector activeTraffic; + ActiveRunwayVec activeRunways; + FGAirportDynamics *parent; + +public: + FGApproachController(FGAirportDynamics * parent); + virtual ~FGApproachController(); + + 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 updateAircraftInformation(int id, double lat, double lon, + double heading, double speed, double alt, double dt); + virtual bool hasInstruction(int id); + virtual FGATCInstruction getInstruction(int id); + + virtual void render(bool); + virtual std::string getName(); + virtual void update(double dt); + + ActiveRunway* getRunway(const std::string& name); + + bool hasActiveTraffic() { + return ! activeTraffic.empty(); + }; + TrafficVector &getActiveTraffic() { + return activeTraffic; + }; +}; + +#endif diff --git a/src/ATC/CMakeLists.txt b/src/ATC/CMakeLists.txt index eb77c3d10..7bc9d0e20 100644 --- a/src/ATC/CMakeLists.txt +++ b/src/ATC/CMakeLists.txt @@ -7,7 +7,11 @@ set(SOURCES ATISEncoder.cxx MetarPropertiesATISInformationProvider.cxx CurrentWeatherATISInformationProvider.cxx + ATCController.cxx + ApproachController.cxx GroundController.cxx + StartupController.cxx + TowerController.cxx ) set(HEADERS @@ -17,7 +21,11 @@ set(HEADERS ATISEncoder.hxx MetarPropertiesATISInformationProvider.hxx CurrentWeatherATISInformationProvider.hxx + ATCController.hxx + ApproachController.hxx GroundController.hxx + StartupController.hxx + TowerController.hxx ) flightgear_component(ATC "${SOURCES}" "${HEADERS}") diff --git a/src/ATC/GroundController.cxx b/src/ATC/GroundController.cxx index f08d861aa..a71325108 100644 --- a/src/ATC/GroundController.cxx +++ b/src/ATC/GroundController.cxx @@ -58,7 +58,8 @@ #include -#include "GroundController.hxx" +#include +#include using std::string; @@ -926,8 +927,8 @@ void FGGroundController::update(double dt) updateActiveTraffic(i, priority, now); } - eraseDeadTraffic(startupTraffic); - eraseDeadTraffic(activeTraffic); + FGATCController::eraseDeadTraffic(startupTraffic); + FGATCController::eraseDeadTraffic(activeTraffic); } void FGGroundController::updateStartupTraffic(TrafficVectorIterator i, diff --git a/src/ATC/StartupController.cxx b/src/ATC/StartupController.cxx new file mode 100644 index 000000000..068ec9d43 --- /dev/null +++ b/src/ATC/StartupController.cxx @@ -0,0 +1,490 @@ +// Extracted from 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$ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "trafficcontrol.hxx" +#include "atc_mgr.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using std::sort; +using std::string; + +/*************************************************************************** + * class FGStartupController + * subclass of FGATCController + **************************************************************************/ +FGStartupController::FGStartupController(FGAirportDynamics *par): + FGATCController() +{ + parent = par; +} + +FGStartupController::~FGStartupController() +{ + _isDestroying = true; + clearTrafficControllers(activeTraffic); +} + +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) +{ + init(); + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + + // Add a new TrafficRecord if no one exsists for this aircraft. + if (i == activeTraffic.end() || activeTraffic.empty()) { + FGTrafficRecord rec; + rec.setId(id); + + rec.setPositionAndHeading(lat, lon, heading, speed, alt); + rec.setRunway(intendedRoute->getRunway()); + rec.setLeg(leg); + rec.setPositionAndIntentions(currentPosition, intendedRoute); + //rec.setCallSign(callsign); + rec.setAircraft(ref); + rec.setHoldPosition(true); + activeTraffic.push_back(rec); + } else { + i->setPositionAndIntentions(currentPosition, intendedRoute); + 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) +{ + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + + if (i == activeTraffic.end() || activeTraffic.empty()) { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN); + } else { + return i->hasInstruction(); + } + return false; +} + + +FGATCInstruction FGStartupController::getInstruction(int id) +{ + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + + if (i == activeTraffic.end() || activeTraffic.empty()) { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN); + } else { + return i->getInstruction(); + } + return FGATCInstruction(); +} + +void FGStartupController::signOff(int id) +{ + // ensure we don't modify activeTraffic during destruction + if (_isDestroying) + return; + + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + + if (i == activeTraffic.end() || activeTraffic.empty()) { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN); + } else { + SG_LOG(SG_ATC, SG_DEBUG, i->getAircraft()->getCallSign() << " signing off from startupcontroller"); + i = activeTraffic.erase(i); + } +} + +bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId, + AtcMsgDir msgDir) +{ + int state = i->getState(); + if ((state == st) && available) { + if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) { + + SG_LOG(SG_ATC, SG_BULK, "Checking state " << st << " for " << i->getAircraft()->getCallSign()); + SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); + int n = trans_num->getIntValue(); + if (n == 0) { + trans_num->setIntValue(-1); + // PopupCallback(n); + SG_LOG(SG_ATC, SG_BULK, "Selected transmission message " << n); + //FGATCDialogNew::instance()->removeEntry(1); + } else { + SG_LOG(SG_ATC, SG_BULK, "Creating message for " << i->getAircraft()->getCallSign()); + transmit(&(*i), &(*parent), msgId, msgDir, false); + return false; + } + } + if (now > startTime) { + SG_LOG(SG_ATC, SG_BULK, "Transmitting startup msg"); + transmit(&(*i), &(*parent), msgId, msgDir, true); + i->updateState(); + lastTransmission = now; + available = false; + return true; + } + } + return false; +} + +void FGStartupController::updateAircraftInformation(int id, double lat, double lon, + double heading, double speed, double alt, + double dt) +{ + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + TrafficVectorIterator current, closest; + + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: updating aircraft without traffic record at " << SG_ORIGIN); + return; + } else { + i->setPositionAndHeading(lat, lon, heading, speed, alt); + current = i; + } + setDt(getDt() + dt); + + int state = i->getState(); + + // Sentry FLIGHTGEAR-2Q : don't crash on null TrafficRef + // Sentry FLIGHTGEAR-129: don't crash on null aircraft + if (!i->getAircraft() || !i->getAircraft()->getTrafficRef()) { + SG_LOG(SG_ATC, SG_ALERT, "AI traffic: updating aircraft without traffic ref"); + return; + } + + // The user controlled aircraft should have crased here, because it doesn't have a traffic reference. + // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight. + time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime(); + time_t now = globals->get_time_params()->get_cur_time(); + + SG_LOG(SG_ATC, SG_BULK, i->getAircraft()->getTrafficRef()->getCallSign() + << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available + << " at parking " << getGateName(i->getAircraft())); + + if ((now - lastTransmission) > 3 + (rand() % 15)) { + available = true; + } + + checkTransmissionState(0, now, (startTime + 0 ), i, MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND); + checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND); + checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR); + checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND); + if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, ATC_AIR_TO_GROUND)) { + i->nextFrequency(); + } + checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND); + checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT, ATC_GROUND_TO_AIR); + checkTransmissionState(7, now, (startTime + 180), i, MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND); + + + + if ((state == 8) && available) { + if (now > startTime + 200) { + if (i->pushBackAllowed()) { + i->allowRepeatedTransmissions(); + transmit(&(*i), &(*parent), MSG_PERMIT_PUSHBACK_CLEARANCE, + ATC_GROUND_TO_AIR, true); + i->updateState(); + } else { + transmit(&(*i), &(*parent), MSG_HOLD_PUSHBACK_CLEARANCE, + ATC_GROUND_TO_AIR, true); + i->suppressRepeatedTransmissions(); + } + lastTransmission = now; + available = false; + } + } + if ((state == 9) && available) { + i->setHoldPosition(false); + } +} + +// Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that. +static void WorldCoordinate(osg::Matrix& obj_pos, double lat, + double lon, double elev, double hdg, double slope) +{ + SGGeod geod = SGGeod::fromDegM(lon, lat, elev); + obj_pos = makeZUpFrame(geod); + // hdg is not a compass heading, but a counter-clockwise rotation + // around the Z axis + obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS, + 0.0, 0.0, 1.0)); + obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS, + 0.0, 1.0, 0.0)); +} + + +void FGStartupController::render(bool visible) +{ + SG_LOG(SG_ATC, SG_DEBUG, "Rendering startup controller"); + SGMaterialLib *matlib = globals->get_matlib(); + if (group) { + //int nr = ; + globals->get_scenery()->get_scene_graph()->removeChild(group); + //while (group->getNumChildren()) { + // SG_LOG(SG_ATC, SG_BULK, "Number of children: " << group->getNumChildren()); + //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0); + //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0); + //geode->releaseGLObjects(); + //group->removeChild(geode); + //delete geode; + group = 0; + } + if (visible) { + group = new osg::Group; + FGScenery * local_scenery = globals->get_scenery(); + //double elevation_meters = 0.0; + //double elevation_feet = 0.0; + + FGGroundNetwork* groundNet = parent->parent()->groundNetwork(); + + //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) { + double dx = 0; + time_t now = globals->get_time_params()->get_cur_time(); + + for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) { + if (i->isActive(300)) { + // Handle start point + int pos = i->getCurrentPosition(); + SG_LOG(SG_ATC, SG_BULK, "rendering for " << i->getAircraft()->getCallSign() << "pos = " << pos); + if (pos > 0) { + FGTaxiSegment *segment = groundNet->findSegment(pos); + SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude()))); + SGGeod end (segment->getEnd()->geod()); + + double length = SGGeodesy::distanceM(start, end); + //heading = SGGeodesy::headingDeg(start->geod(), end->geod()); + + double az2, heading; //, distanceM; + SGGeodesy::inverse(start, end, heading, az2, length); + double coveredDistance = length * 0.5; + SGGeod center; + SGGeodesy::direct(start, heading, coveredDistance, center, az2); + SG_LOG(SG_ATC, SG_BULK, "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading); + /////////////////////////////////////////////////////////////////////////////// + // Make a helper function out of this + osg::Matrix obj_pos; + osg::MatrixTransform *obj_trans = new osg::MatrixTransform; + obj_trans->setDataVariance(osg::Object::STATIC); + // Experimental: Calculate slope here, based on length, and the individual elevations + double elevationStart; + if (isUserAircraft((i)->getAircraft())) { + elevationStart = fgGetDouble("/position/ground-elev-m"); + } else { + elevationStart = ((i)->getAircraft()->_getAltitude() * SG_FEET_TO_METER); + } + double elevationEnd = segment->getEnd()->getElevationM(); + if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) { + SGGeod center2 = end; + center2.setElevationM(SG_MAX_ELEVATION_M); + if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { + //elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5; + //elevation_meters += 0.5; + } + else { + elevationEnd = parent->getElevation(); + } + segment->getEnd()->setElevation(elevationEnd); + } + + double elevationMean = (elevationStart + elevationEnd) / 2.0; + double elevDiff = elevationEnd - elevationStart; + + double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES; + + SG_LOG(SG_ATC, SG_BULK, "1. Using mean elevation : " << elevationMean << " and " << slope); + + WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean + 0.5 + dx, -(heading), slope ); + ; + + obj_trans->setMatrix( obj_pos ); + //osg::Vec3 center(0, 0, 0) + + float width = length /2.0; + osg::Vec3 corner(-width, 0, 0.25f); + osg::Vec3 widthVec(2*width + 1, 0, 0); + osg::Vec3 heightVec(0, 1, 0); + osg::Geometry* geometry; + geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec); + simgear::EffectGeode* geode = new simgear::EffectGeode; + geode->setName("test"); + geode->addDrawable(geometry); + //osg::Node *custom_obj; + SGMaterial *mat; + if (segment->hasBlock(now)) { + mat = matlib->find("UnidirectionalTaperRed", center); + } else { + mat = matlib->find("UnidirectionalTaperGreen", center); + } + if (mat) + geode->setEffect(mat->get_effect()); + obj_trans->addChild(geode); + // wire as much of the scene graph together as we can + //->addChild( obj_trans ); + group->addChild( obj_trans ); + ///////////////////////////////////////////////////////////////////// + } else { + SG_LOG(SG_ATC, SG_DEBUG, "BIG FAT WARNING: current position is here : " << pos); + } + for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) { + osg::Matrix obj_pos; + int k = (*j); + if (k > 0) { + SG_LOG(SG_ATC, SG_BULK, "rendering for " << i->getAircraft()->getCallSign() << "intention = " << k); + osg::MatrixTransform *obj_trans = new osg::MatrixTransform; + obj_trans->setDataVariance(osg::Object::STATIC); + FGTaxiSegment *segment = groundNet->findSegment(k); + + double elevationStart = segment->getStart()->getElevationM(); + double elevationEnd = segment->getEnd ()->getElevationM(); + if ((elevationStart == 0) || (elevationStart == parent->getElevation())) { + SGGeod center2 = segment->getStart()->geod(); + center2.setElevationM(SG_MAX_ELEVATION_M); + if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) { + //elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5; + //elevation_meters += 0.5; + } + else { + elevationStart = parent->getElevation(); + } + segment->getStart()->setElevation(elevationStart); + } + if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) { + SGGeod center2 = segment->getEnd()->geod(); + center2.setElevationM(SG_MAX_ELEVATION_M); + if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { + //elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5; + //elevation_meters += 0.5; + } + else { + elevationEnd = parent->getElevation(); + } + segment->getEnd()->setElevation(elevationEnd); + } + + double elevationMean = (elevationStart + elevationEnd) / 2.0; + double elevDiff = elevationEnd - elevationStart; + double length = segment->getLength(); + double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES; + + SG_LOG(SG_ATC, SG_BULK, "2. Using mean elevation : " << elevationMean << " and " << slope); + + SGGeod segCenter(segment->getCenter()); + WorldCoordinate( obj_pos, segCenter.getLatitudeDeg(), + segCenter.getLongitudeDeg(), elevationMean + 0.5 + dx, -(segment->getHeading()), slope ); + + //WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), parent->getElevation()+8+dx, -(segment->getHeading()) ); + + obj_trans->setMatrix( obj_pos ); + //osg::Vec3 center(0, 0, 0) + + float width = segment->getLength() /2.0; + osg::Vec3 corner(-width, 0, 0.25f); + osg::Vec3 widthVec(2*width + 1, 0, 0); + osg::Vec3 heightVec(0, 1, 0); + osg::Geometry* geometry; + geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec); + simgear::EffectGeode* geode = new simgear::EffectGeode; + geode->setName("test"); + geode->addDrawable(geometry); + //osg::Node *custom_obj; + SGMaterial *mat; + if (segment->hasBlock(now)) { + mat = matlib->find("UnidirectionalTaperRed", segCenter); + } else { + mat = matlib->find("UnidirectionalTaperGreen", segCenter); + } + if (mat) + geode->setEffect(mat->get_effect()); + obj_trans->addChild(geode); + // wire as much of the scene graph together as we can + //->addChild( obj_trans ); + group->addChild( obj_trans ); + } else { + SG_LOG(SG_ATC, SG_DEBUG, "BIG FAT WARNING: k is here : " << pos); + } + } + dx += 0.2; + } + } + globals->get_scenery()->get_scene_graph()->addChild(group); + } +} + +string FGStartupController::getName() { + return string(parent->getId() + "-startup"); +} + +void FGStartupController::update(double dt) +{ + FGATCController::eraseDeadTraffic(activeTraffic); +} + diff --git a/src/ATC/StartupController.hxx b/src/ATC/StartupController.hxx new file mode 100644 index 000000000..47daf96dd --- /dev/null +++ b/src/ATC/StartupController.hxx @@ -0,0 +1,83 @@ +// Extracted from 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 STARTUP_CONTROLLER_HXX +#define STARTUP_CONTROLLER_HXX + +#include + +#include +#include +#include +#include + +#include +// There is probably a better include than sg_geodesy to get the SG_NM_TO_METER... +#include +#include +#include +#include + +#include +#include + +/****************************************************************************** + * class FGStartupController + * handle + *****************************************************************************/ + +class FGStartupController : public FGATCController +{ +private: + TrafficVector activeTraffic; + //ActiveRunwayVec activeRunways; + FGAirportDynamics *parent; + +public: + FGStartupController(FGAirportDynamics *parent); + 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 updateAircraftInformation(int id, double lat, double lon, + double heading, double speed, double alt, double dt); + virtual bool hasInstruction(int id); + virtual FGATCInstruction getInstruction(int id); + + virtual void render(bool); + virtual std::string getName(); + virtual void update(double dt); + + bool hasActiveTraffic() { + return ! activeTraffic.empty(); + }; + TrafficVector &getActiveTraffic() { + return activeTraffic; + }; + + // Hpoefully, we can move this function to the base class, but I need to verify what is needed for the other controllers before doing so. + bool checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId, + AtcMsgDir msgDir); + +}; + +#endif \ No newline at end of file diff --git a/src/ATC/TowerController.cxx b/src/ATC/TowerController.cxx new file mode 100644 index 000000000..890a3ae1f --- /dev/null +++ b/src/ATC/TowerController.cxx @@ -0,0 +1,294 @@ +// Extracted from 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$ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "trafficcontrol.hxx" +#include "atc_mgr.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using std::sort; +using std::string; + +/*************************************************************************** + * class FGTowerController +s * subclass of FGATCController + **************************************************************************/ + +FGTowerController::FGTowerController(FGAirportDynamics *par) : + FGATCController() +{ + parent = par; +} + +FGTowerController::~FGTowerController() +{ + _isDestroying = true; + clearTrafficControllers(activeTraffic); +} + +// +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) +{ + init(); + + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + + // Add a new TrafficRecord if no one exsists for this aircraft. + if (i == activeTraffic.end() || (activeTraffic.empty())) { + FGTrafficRecord rec; + rec.setId(id); + + rec.setPositionAndHeading(lat, lon, heading, speed, alt); + rec.setRunway(intendedRoute->getRunway()); + rec.setLeg(leg); + //rec.setCallSign(callsign); + rec.setRadius(radius); + rec.setAircraft(ref); + activeTraffic.push_back(rec); + // Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway. + ActiveRunwayVecIterator rwy = activeRunways.begin(); + if (! activeRunways.empty()) { + while (rwy != activeRunways.end()) { + if (rwy->getRunwayName() == intendedRoute->getRunway()) { + break; + } + rwy++; + } + } + if (rwy == activeRunways.end()) { + ActiveRunway aRwy(intendedRoute->getRunway(), id); + aRwy.addToDepartureQueue(ref); + activeRunways.push_back(aRwy); + rwy = (activeRunways.end()-1); + } else { + rwy->addToDepartureQueue(ref); + } + + SG_LOG(SG_ATC, SG_DEBUG, ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getdepartureQueueSize() << " for takeoff "); + } else { + i->setPositionAndHeading(lat, lon, heading, speed, alt); + } +} + +void FGTowerController::updateAircraftInformation(int id, double lat, double lon, + double heading, double speed, double alt, + double dt) +{ + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + + setDt(getDt() + dt); + + if (i == activeTraffic.end() || (activeTraffic.empty())) { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: updating aircraft without traffic record at " << + SG_ORIGIN); + return; + } + + // Update the position of the current aircraft + FGTrafficRecord& current = *i; + current.setPositionAndHeading(lat, lon, heading, speed, alt); + + // see if we already have a clearance record for the currently active runway + // NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is + // already exists here. So, we can simplify the current code. + + ActiveRunwayVecIterator rwy = activeRunways.begin(); + //if (parent->getId() == fgGetString("/sim/presets/airport-id")) { + // for (rwy = activeRunways.begin(); rwy != activeRunways.end(); rwy++) { + // rwy->printdepartureQueue(); + // } + //} + + rwy = activeRunways.begin(); + while (rwy != activeRunways.end()) { + if (rwy->getRunwayName() == current.getRunway()) { + break; + } + rwy++; + } + + // only bother running the following code if the current aircraft is the + // first in line for depature + /* if (current.getAircraft() == rwy->getFirstAircraftIndepartureQueue()) { + if (rwy->getCleared()) { + if (id == rwy->getCleared()) { + current.setHoldPosition(false); + } else { + current.setHoldPosition(true); + } + } else { + // For now. At later stages, this will probably be the place to check for inbound traffc. + rwy->setCleared(id); + } + } */ + // only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control + auto ac = rwy->getFirstAircraftInDepartureQueue(); + if (ac) { + if (ac->getTakeOffStatus() == 1) { + // transmit takeoff clearance + ac->setTakeOffStatus(2); + } + } + + if (current.getAircraft()->getTakeOffStatus() == 2) { + current.setHoldPosition(false); + } else { + current.setHoldPosition(true); + } + int clearanceId = rwy->getCleared(); + if (clearanceId) { + if (id == clearanceId) { + current.setHoldPosition(false); + } + } else { + if (current.getAircraft() == rwy->getFirstAircraftInDepartureQueue()) { + rwy->setCleared(id); + auto ac = rwy->getFirstOfStatus(1); + if (ac) + ac->setTakeOffStatus(2); + // transmit takeoff clearacne? But why twice? + } + } +} + + +void FGTowerController::signOff(int id) +{ + // ensure we don't modify activeTraffic during destruction + if (_isDestroying) + return; + + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + if (i == activeTraffic.end() || (activeTraffic.empty())) { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN); + return; + } + + const auto trafficRunway = i->getRunway(); + auto runwayIt = std::find_if(activeRunways.begin(), activeRunways.end(), + [&trafficRunway](const ActiveRunway& ar) { + return ar.getRunwayName() == trafficRunway; + }); + + if (runwayIt != activeRunways.end()) { + runwayIt->setCleared(0); + runwayIt->updateDepartureQueue(); + } else { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff at " << SG_ORIGIN); + } + + i->getAircraft()->resetTakeOffStatus(); + activeTraffic.erase(i); + SG_LOG(SG_ATC, SG_DEBUG, "Signing off from tower controller"); +} + +// 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) +{ + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + + if (i == activeTraffic.end() || activeTraffic.empty()) { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN); + } else { + return i->hasInstruction(); + } + return false; +} + + +FGATCInstruction FGTowerController::getInstruction(int id) +{ + // Search activeTraffic for a record matching our id + TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); + + if (i == activeTraffic.end() || activeTraffic.empty()) { + SG_LOG(SG_ATC, SG_ALERT, + "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN); + } else { + return i->getInstruction(); + } + return FGATCInstruction(); +} + +void FGTowerController::render(bool visible) { + // this should be bulk, since its called quite often + SG_LOG(SG_ATC, SG_BULK, "FGTowerController::render function not yet implemented"); +} + +string FGTowerController::getName() { + return string(parent->getId() + "-tower"); +} + +void FGTowerController::update(double dt) +{ + FGATCController::eraseDeadTraffic(activeTraffic); +} diff --git a/src/ATC/TowerController.hxx b/src/ATC/TowerController.hxx new file mode 100644 index 000000000..670fcd216 --- /dev/null +++ b/src/ATC/TowerController.hxx @@ -0,0 +1,75 @@ +// Extracted from 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 TOWER_CONTROLLER_HXX +#define TOWER_CONTROLLER_HXX + +#include + +#include +#include +#include +#include + +#include +// There is probably a better include than sg_geodesy to get the SG_NM_TO_METER... +#include +#include +#include +#include + +#include +#include + +/****************************************************************************** + * class FGTowerControl + *****************************************************************************/ +class FGTowerController : public FGATCController +{ +private: + TrafficVector activeTraffic; + ActiveRunwayVec activeRunways; + FGAirportDynamics *parent; + +public: + FGTowerController(FGAirportDynamics *parent); + 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 updateAircraftInformation(int id, double lat, double lon, + double heading, double speed, double alt, double dt); + virtual bool hasInstruction(int id); + virtual FGATCInstruction getInstruction(int id); + + virtual void render(bool); + virtual std::string getName(); + virtual void update(double dt); + bool hasActiveTraffic() { + return ! activeTraffic.empty(); + }; + TrafficVector &getActiveTraffic() { + return activeTraffic; + }; +}; + +#endif \ No newline at end of file diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 9fab23755..92c23e24e 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -55,27 +55,6 @@ using std::sort; using std::string; -namespace { - -void clearTrafficControllers(TrafficVector& vec) -{ - for (const auto& traffic : vec) { - if (!traffic.getAircraft()) { - continue; - } - - traffic.getAircraft()->clearATCController(); - } -} - -TrafficVectorIterator searchActiveTraffic(TrafficVector& vec, int id) -{ - return std::find_if(vec.begin(), vec.end(), [id] (const FGTrafficRecord& rec) - { return rec.getId() == id; }); -} - -} // of anonymous namespace - /*************************************************************************** * ActiveRunway **************************************************************************/ @@ -578,1198 +557,9 @@ bool FGATCInstruction::hasInstruction() const -/*************************************************************************** - * FGATCController - * - **************************************************************************/ -FGATCController::FGATCController() -{ - dt_count = 0; - available = true; - lastTransmission = 0; - initialized = false; - lastTransmissionDirection = ATC_AIR_TO_GROUND; - group = NULL; -} -FGATCController::~FGATCController() -{ - if (initialized) { - auto mgr = globals->get_subsystem(); - mgr->removeController(this); - } -} -void FGATCController::init() -{ - if (!initialized) { - auto mgr = globals->get_subsystem(); - mgr->addController(this); - initialized = true; - } -} -string FGATCController::getGateName(FGAIAircraft * ref) -{ - return ref->atGate(); -} -bool FGATCController::isUserAircraft(FGAIAircraft* ac) -{ - return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false; -}; -void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, AtcMsgId msgId, - AtcMsgDir msgDir, bool audible) -{ - string sender, receiver; - int stationFreq = 0; - int taxiFreq = 0; - int towerFreq = 0; - int freqId = 0; - string atisInformation; - string text; - string taxiFreqStr; - string towerFreqStr; - double heading = 0; - string activeRunway; - string fltType; - string rwyClass; - string SID; - string transponderCode; - FGAIFlightPlan *fp; - string fltRules; - string instructionText; - int ground_to_air=0; - - //double commFreqD; - sender = rec->getAircraft()->getTrafficRef()->getCallSign(); - if (rec->getAircraft()->getTaxiClearanceRequest()) { - instructionText = "push-back and taxi"; - } else { - instructionText = "taxi"; - } - - SG_LOG(SG_ATC, SG_DEBUG, "transmitting for: " << sender << "Leg = " << rec->getLeg()); - - auto depApt = rec->getAircraft()->getTrafficRef()->getDepartureAirport(); - - switch (rec->getLeg()) { - case 1: - case 2: - // avoid crash FLIGHTGEAR-ER - if (!depApt) { - SG_LOG(SG_ATC, SG_DEV_ALERT, "TrafficRec has empty departure airport, can't transmit"); - return; - } - - freqId = rec->getNextFrequency(); - stationFreq = depApt->getDynamics()->getGroundFrequency(rec->getLeg() + freqId); - taxiFreq = depApt->getDynamics()->getGroundFrequency(2); - towerFreq = depApt->getDynamics()->getTowerFrequency(2); - receiver = depApt->getName() + "-Ground"; - atisInformation = depApt->getDynamics()->getAtisSequence(); - break; - case 3: - if (!depApt) { - SG_LOG(SG_ATC, SG_DEV_ALERT, "TrafficRec has empty departure airport, can't transmit"); - return; - } - - receiver = depApt->getName() + "-Tower"; - break; - } - - // Swap sender and receiver value in case of a ground to air transmission - if (msgDir == ATC_GROUND_TO_AIR) { - string tmp = sender; - sender = receiver; - receiver = tmp; - ground_to_air = 1; - } - - 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 " + - atisInformation + ". " + - rec->getAircraft()->getTrafficRef()->getFlightRules() + - " to " + - rec->getAircraft()->getTrafficRef()->getArrivalAirport()-> - getName() + ". Request start-up."; - break; - // Acknowledge engine startup permission - // Assign departure runway - // Assign SID, if necessery (TODO) - case MSG_PERMIT_ENGINE_START: - taxiFreqStr = formatATCFrequency3_2(taxiFreq); - - heading = rec->getAircraft()->getTrafficRef()->getCourse(); - fltType = rec->getAircraft()->getTrafficRef()->getFlightType(); - rwyClass = - rec->getAircraft()->GetFlightPlan()-> - getRunwayClassFromTrafficType(fltType); - - rec->getAircraft()->getTrafficRef()->getDepartureAirport()-> - getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, - heading); - rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway); - fp = NULL; - rec->getAircraft()->GetFlightPlan()->setSID(fp); - if (fp) { - SID = fp->getName() + " departure"; - } else { - SID = "fly runway heading "; - } - //snprintf(buffer, 7, "%3.2f", heading); - fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules(); - transponderCode = genTransponderCode(fltRules); - rec->getAircraft()->SetTransponderCode(transponderCode); - text = - receiver + ". Start-up approved. " + atisInformation + - " correct, runway " + activeRunway + ", " + SID + ", squawk " + - transponderCode + ". " + - "For "+ instructionText + " clearance call " + taxiFreqStr + ". " + - sender + " control."; - break; - case MSG_DENY_ENGINE_START: - text = receiver + ". Standby."; - break; - case MSG_ACKNOWLEDGE_ENGINE_START: - fp = rec->getAircraft()->GetFlightPlan()->getSID(); - if (fp) { - SID = - rec->getAircraft()->GetFlightPlan()->getSID()->getName() + - " departure"; - } else { - SID = "fly runway heading "; - } - taxiFreqStr = formatATCFrequency3_2(taxiFreq); - activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway(); - transponderCode = rec->getAircraft()->GetTransponderCode(); - - text = - receiver + ". Start-up approved. " + atisInformation + - " correct, runway " + activeRunway + ", " + SID + ", squawk " + - transponderCode + ". " + - "For " + instructionText + " clearance call " + taxiFreqStr + ". " + - sender + "."; - break; - case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY: - taxiFreqStr = formatATCFrequency3_2(taxiFreq); - text = receiver + ". Switching to " + taxiFreqStr + ". " + sender + "."; - break; - case MSG_INITIATE_CONTACT: - text = receiver + ". With you. " + sender + "."; - break; - case MSG_ACKNOWLEDGE_INITIATE_CONTACT: - text = receiver + ". Roger. " + sender + "."; - break; - case MSG_REQUEST_PUSHBACK_CLEARANCE: - if (rec->getAircraft()->getTaxiClearanceRequest()) { - text = receiver + ". Request push-back. " + sender + "."; - } else { - text = receiver + ". Request Taxi clearance. " + sender + "."; - } - break; - case MSG_PERMIT_PUSHBACK_CLEARANCE: - if (rec->getAircraft()->getTaxiClearanceRequest()) { - text = receiver + ". Push-back approved. " + sender + "."; - } else { - text = receiver + ". Cleared to Taxi. " + sender + "."; - } - break; - case MSG_HOLD_PUSHBACK_CLEARANCE: - text = receiver + ". Standby. " + sender + "."; - break; - case MSG_REQUEST_TAXI_CLEARANCE: - text = receiver + ". Ready to Taxi. " + sender + "."; - break; - case MSG_ISSUE_TAXI_CLEARANCE: - text = receiver + ". Cleared to taxi. " + sender + "."; - break; - case MSG_ACKNOWLEDGE_TAXI_CLEARANCE: - text = receiver + ". Cleared to taxi. " + sender + "."; - break; - case MSG_HOLD_POSITION: - text = receiver + ". Hold Position. " + sender + "."; - break; - case MSG_ACKNOWLEDGE_HOLD_POSITION: - text = receiver + ". Holding Position. " + sender + "."; - break; - case MSG_RESUME_TAXI: - text = receiver + ". Resume Taxiing. " + sender + "."; - break; - case MSG_ACKNOWLEDGE_RESUME_TAXI: - text = receiver + ". Continuing Taxi. " + sender + "."; - break; - case MSG_REPORT_RUNWAY_HOLD_SHORT: - activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway(); - //activeRunway = "test"; - text = receiver + ". Holding short runway " - + activeRunway - + ". " + sender + "."; - //text = "test1"; - SG_LOG(SG_ATC, SG_DEBUG, "1 Currently at leg " << rec->getLeg()); - break; - case MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT: - activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway(); - text = receiver + " Roger. Holding short runway " - // + activeRunway - + ". " + sender + "."; - //text = "test2"; - SG_LOG(SG_ATC, SG_DEBUG, "2 Currently at leg " << rec->getLeg()); - break; - case MSG_SWITCH_TOWER_FREQUENCY: - towerFreqStr = formatATCFrequency3_2(towerFreq); - text = receiver + " Contact Tower at " + towerFreqStr + ". " + sender + "."; - //text = "test3"; - SG_LOG(SG_ATC, SG_DEBUG, "3 Currently at leg " << rec->getLeg()); - break; - case MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY: - towerFreqStr = formatATCFrequency3_2(towerFreq); - text = receiver + " Roger, switching to tower at " + towerFreqStr + ". " + sender + "."; - //text = "test4"; - SG_LOG(SG_ATC, SG_DEBUG, "4 Currently at leg " << rec->getLeg()); - break; - default: - //text = "test3"; - text = text + sender + ". Transmitting unknown Message."; - break; - } - - const bool atcAudioEnabled = fgGetBool("/sim/sound/atc/enabled", false); - if (audible && atcAudioEnabled) { - double onBoardRadioFreq0 = - fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz"); - double onBoardRadioFreq1 = - fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz"); - int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5); - int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5); - SG_LOG(SG_ATC, SG_DEBUG, "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl); - - // Display ATC message only when one of the radios is tuned - // the relevant frequency. - // Note that distance attenuation is currently not yet implemented - - if ((stationFreq > 0)&& - ((onBoardRadioFreqI0 == stationFreq)|| - (onBoardRadioFreqI1 == stationFreq))) { - if (rec->allowTransmissions()) { - - if( fgGetBool( "/sim/radio/use-itm-attenuation", false ) ) { - SG_LOG(SG_ATC, SG_DEBUG, "Using ITM radio propagation"); - FGRadioTransmission* radio = new FGRadioTransmission(); - SGGeod sender_pos; - double sender_alt_ft, sender_alt; - if(ground_to_air) { - sender_pos = parent->parent()->geod(); - } - else { - sender_alt_ft = rec->getAltitude(); - sender_alt = sender_alt_ft * SG_FEET_TO_METER; - sender_pos= SGGeod::fromDegM( rec->getLongitude(), - rec->getLatitude(), sender_alt ); - } - double frequency = ((double)stationFreq) / 100; - radio->receiveATC(sender_pos, frequency, text, ground_to_air); - delete radio; - } - else { - fgSetString("/sim/messages/atc", text.c_str()); - } - } - } - } else { - //FGATCDialogNew::instance()->addEntry(1, text); - } -} - -/* -* Format integer frequency xxxyy as xxx.yy -* @param freq - integer value -* @return the formatted string -*/ -string FGATCController::formatATCFrequency3_2(int freq) -{ - char buffer[7]; // does this ever need to be freed? - snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0)); - return string(buffer); -} - -// TODO: Set transponder codes according to real-world routes. -// The current version just returns a random string of four octal numbers. -string FGATCController::genTransponderCode(const string& fltRules) -{ - if (fltRules == "VFR") - return string("1200"); - - std::default_random_engine generator; - std::uniform_int_distribution distribution(0, 7); - - unsigned val = ( - distribution(generator) * 1000 + - distribution(generator) * 100 + - distribution(generator) * 10 + - distribution(generator)); - - return std::to_string(val); -} - -void FGATCController::eraseDeadTraffic(TrafficVector& vec) -{ - auto it = std::remove_if(vec.begin(), vec.end(), [](const FGTrafficRecord& traffic) - { - if (!traffic.getAircraft()) { - return true; - } - return traffic.getAircraft()->getDie(); - }); - vec.erase(it, vec.end()); -} - - - -/*************************************************************************** - * class FGTowerController - * subclass of FGATCController - **************************************************************************/ - -FGTowerController::FGTowerController(FGAirportDynamics *par) : - FGATCController() -{ - parent = par; -} - -FGTowerController::~FGTowerController() -{ - _isDestroying = true; - clearTrafficControllers(activeTraffic); -} - -// -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) -{ - init(); - - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - - // Add a new TrafficRecord if no one exsists for this aircraft. - if (i == activeTraffic.end() || (activeTraffic.empty())) { - FGTrafficRecord rec; - rec.setId(id); - - rec.setPositionAndHeading(lat, lon, heading, speed, alt); - rec.setRunway(intendedRoute->getRunway()); - rec.setLeg(leg); - //rec.setCallSign(callsign); - rec.setRadius(radius); - rec.setAircraft(ref); - activeTraffic.push_back(rec); - // Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway. - ActiveRunwayVecIterator rwy = activeRunways.begin(); - if (! activeRunways.empty()) { - while (rwy != activeRunways.end()) { - if (rwy->getRunwayName() == intendedRoute->getRunway()) { - break; - } - rwy++; - } - } - if (rwy == activeRunways.end()) { - ActiveRunway aRwy(intendedRoute->getRunway(), id); - aRwy.addToDepartureQueue(ref); - activeRunways.push_back(aRwy); - rwy = (activeRunways.end()-1); - } else { - rwy->addToDepartureQueue(ref); - } - - SG_LOG(SG_ATC, SG_DEBUG, ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getdepartureQueueSize() << " for takeoff "); - } else { - i->setPositionAndHeading(lat, lon, heading, speed, alt); - } -} - -void FGTowerController::updateAircraftInformation(int id, double lat, double lon, - double heading, double speed, double alt, - double dt) -{ - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - - setDt(getDt() + dt); - - if (i == activeTraffic.end() || (activeTraffic.empty())) { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: updating aircraft without traffic record at " << - SG_ORIGIN); - return; - } - - // Update the position of the current aircraft - FGTrafficRecord& current = *i; - current.setPositionAndHeading(lat, lon, heading, speed, alt); - - // see if we already have a clearance record for the currently active runway - // NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is - // already exists here. So, we can simplify the current code. - - ActiveRunwayVecIterator rwy = activeRunways.begin(); - //if (parent->getId() == fgGetString("/sim/presets/airport-id")) { - // for (rwy = activeRunways.begin(); rwy != activeRunways.end(); rwy++) { - // rwy->printdepartureQueue(); - // } - //} - - rwy = activeRunways.begin(); - while (rwy != activeRunways.end()) { - if (rwy->getRunwayName() == current.getRunway()) { - break; - } - rwy++; - } - - // only bother running the following code if the current aircraft is the - // first in line for depature - /* if (current.getAircraft() == rwy->getFirstAircraftIndepartureQueue()) { - if (rwy->getCleared()) { - if (id == rwy->getCleared()) { - current.setHoldPosition(false); - } else { - current.setHoldPosition(true); - } - } else { - // For now. At later stages, this will probably be the place to check for inbound traffc. - rwy->setCleared(id); - } - } */ - // only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control - auto ac = rwy->getFirstAircraftInDepartureQueue(); - if (ac) { - if (ac->getTakeOffStatus() == 1) { - // transmit takeoff clearance - ac->setTakeOffStatus(2); - } - } - - if (current.getAircraft()->getTakeOffStatus() == 2) { - current.setHoldPosition(false); - } else { - current.setHoldPosition(true); - } - int clearanceId = rwy->getCleared(); - if (clearanceId) { - if (id == clearanceId) { - current.setHoldPosition(false); - } - } else { - if (current.getAircraft() == rwy->getFirstAircraftInDepartureQueue()) { - rwy->setCleared(id); - auto ac = rwy->getFirstOfStatus(1); - if (ac) - ac->setTakeOffStatus(2); - // transmit takeoff clearacne? But why twice? - } - } -} - - -void FGTowerController::signOff(int id) -{ - // ensure we don't modify activeTraffic during destruction - if (_isDestroying) - return; - - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - if (i == activeTraffic.end() || (activeTraffic.empty())) { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN); - return; - } - - const auto trafficRunway = i->getRunway(); - auto runwayIt = std::find_if(activeRunways.begin(), activeRunways.end(), - [&trafficRunway](const ActiveRunway& ar) { - return ar.getRunwayName() == trafficRunway; - }); - - if (runwayIt != activeRunways.end()) { - runwayIt->setCleared(0); - runwayIt->updateDepartureQueue(); - } else { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff at " << SG_ORIGIN); - } - - i->getAircraft()->resetTakeOffStatus(); - activeTraffic.erase(i); - SG_LOG(SG_ATC, SG_DEBUG, "Signing off from tower controller"); -} - -// 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) -{ - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - - if (i == activeTraffic.end() || activeTraffic.empty()) { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN); - } else { - return i->hasInstruction(); - } - return false; -} - - -FGATCInstruction FGTowerController::getInstruction(int id) -{ - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - - if (i == activeTraffic.end() || activeTraffic.empty()) { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN); - } else { - return i->getInstruction(); - } - return FGATCInstruction(); -} - -void FGTowerController::render(bool visible) { - // this should be bulk, since its called quite often - SG_LOG(SG_ATC, SG_BULK, "FGTowerController::render function not yet implemented"); -} - -string FGTowerController::getName() { - return string(parent->getId() + "-tower"); -} - -void FGTowerController::update(double dt) -{ - eraseDeadTraffic(activeTraffic); -} - - - -/*************************************************************************** - * class FGStartupController - * subclass of FGATCController - **************************************************************************/ -FGStartupController::FGStartupController(FGAirportDynamics *par): - FGATCController() -{ - parent = par; -} - -FGStartupController::~FGStartupController() -{ - _isDestroying = true; - clearTrafficControllers(activeTraffic); -} - -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) -{ - init(); - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - - // Add a new TrafficRecord if no one exsists for this aircraft. - if (i == activeTraffic.end() || activeTraffic.empty()) { - FGTrafficRecord rec; - rec.setId(id); - - rec.setPositionAndHeading(lat, lon, heading, speed, alt); - rec.setRunway(intendedRoute->getRunway()); - rec.setLeg(leg); - rec.setPositionAndIntentions(currentPosition, intendedRoute); - //rec.setCallSign(callsign); - rec.setAircraft(ref); - rec.setHoldPosition(true); - activeTraffic.push_back(rec); - } else { - i->setPositionAndIntentions(currentPosition, intendedRoute); - 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) -{ - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - - if (i == activeTraffic.end() || activeTraffic.empty()) { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN); - } else { - return i->hasInstruction(); - } - return false; -} - - -FGATCInstruction FGStartupController::getInstruction(int id) -{ - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - - if (i == activeTraffic.end() || activeTraffic.empty()) { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN); - } else { - return i->getInstruction(); - } - return FGATCInstruction(); -} - -void FGStartupController::signOff(int id) -{ - // ensure we don't modify activeTraffic during destruction - if (_isDestroying) - return; - - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - - if (i == activeTraffic.end() || activeTraffic.empty()) { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN); - } else { - SG_LOG(SG_ATC, SG_DEBUG, i->getAircraft()->getCallSign() << " signing off from startupcontroller"); - i = activeTraffic.erase(i); - } -} - -bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId, - AtcMsgDir msgDir) -{ - int state = i->getState(); - if ((state == st) && available) { - if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) { - - SG_LOG(SG_ATC, SG_BULK, "Checking state " << st << " for " << i->getAircraft()->getCallSign()); - SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); - int n = trans_num->getIntValue(); - if (n == 0) { - trans_num->setIntValue(-1); - // PopupCallback(n); - SG_LOG(SG_ATC, SG_BULK, "Selected transmission message " << n); - //FGATCDialogNew::instance()->removeEntry(1); - } else { - SG_LOG(SG_ATC, SG_BULK, "Creating message for " << i->getAircraft()->getCallSign()); - transmit(&(*i), &(*parent), msgId, msgDir, false); - return false; - } - } - if (now > startTime) { - SG_LOG(SG_ATC, SG_BULK, "Transmitting startup msg"); - transmit(&(*i), &(*parent), msgId, msgDir, true); - i->updateState(); - lastTransmission = now; - available = false; - return true; - } - } - return false; -} - -void FGStartupController::updateAircraftInformation(int id, double lat, double lon, - double heading, double speed, double alt, - double dt) -{ - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - TrafficVectorIterator current, closest; - - if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: updating aircraft without traffic record at " << SG_ORIGIN); - return; - } else { - i->setPositionAndHeading(lat, lon, heading, speed, alt); - current = i; - } - setDt(getDt() + dt); - - int state = i->getState(); - - // Sentry FLIGHTGEAR-2Q : don't crash on null TrafficRef - // Sentry FLIGHTGEAR-129: don't crash on null aircraft - if (!i->getAircraft() || !i->getAircraft()->getTrafficRef()) { - SG_LOG(SG_ATC, SG_ALERT, "AI traffic: updating aircraft without traffic ref"); - return; - } - - // The user controlled aircraft should have crased here, because it doesn't have a traffic reference. - // NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight. - time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime(); - time_t now = globals->get_time_params()->get_cur_time(); - - SG_LOG(SG_ATC, SG_BULK, i->getAircraft()->getTrafficRef()->getCallSign() - << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available - << " at parking " << getGateName(i->getAircraft())); - - if ((now - lastTransmission) > 3 + (rand() % 15)) { - available = true; - } - - checkTransmissionState(0, now, (startTime + 0 ), i, MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND); - checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND); - checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR); - checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND); - if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, ATC_AIR_TO_GROUND)) { - i->nextFrequency(); - } - checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND); - checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT, ATC_GROUND_TO_AIR); - checkTransmissionState(7, now, (startTime + 180), i, MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND); - - - - if ((state == 8) && available) { - if (now > startTime + 200) { - if (i->pushBackAllowed()) { - i->allowRepeatedTransmissions(); - transmit(&(*i), &(*parent), MSG_PERMIT_PUSHBACK_CLEARANCE, - ATC_GROUND_TO_AIR, true); - i->updateState(); - } else { - transmit(&(*i), &(*parent), MSG_HOLD_PUSHBACK_CLEARANCE, - ATC_GROUND_TO_AIR, true); - i->suppressRepeatedTransmissions(); - } - lastTransmission = now; - available = false; - } - } - if ((state == 9) && available) { - i->setHoldPosition(false); - } -} - -// Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that. -static void WorldCoordinate(osg::Matrix& obj_pos, double lat, - double lon, double elev, double hdg, double slope) -{ - SGGeod geod = SGGeod::fromDegM(lon, lat, elev); - obj_pos = makeZUpFrame(geod); - // hdg is not a compass heading, but a counter-clockwise rotation - // around the Z axis - obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS, - 0.0, 0.0, 1.0)); - obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS, - 0.0, 1.0, 0.0)); -} - - -void FGStartupController::render(bool visible) -{ - SG_LOG(SG_ATC, SG_DEBUG, "Rendering startup controller"); - SGMaterialLib *matlib = globals->get_matlib(); - if (group) { - //int nr = ; - globals->get_scenery()->get_scene_graph()->removeChild(group); - //while (group->getNumChildren()) { - // SG_LOG(SG_ATC, SG_BULK, "Number of children: " << group->getNumChildren()); - //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0); - //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0); - //geode->releaseGLObjects(); - //group->removeChild(geode); - //delete geode; - group = 0; - } - if (visible) { - group = new osg::Group; - FGScenery * local_scenery = globals->get_scenery(); - //double elevation_meters = 0.0; - //double elevation_feet = 0.0; - - FGGroundNetwork* groundNet = parent->parent()->groundNetwork(); - - //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) { - double dx = 0; - time_t now = globals->get_time_params()->get_cur_time(); - - for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) { - if (i->isActive(300)) { - // Handle start point - int pos = i->getCurrentPosition(); - SG_LOG(SG_ATC, SG_BULK, "rendering for " << i->getAircraft()->getCallSign() << "pos = " << pos); - if (pos > 0) { - FGTaxiSegment *segment = groundNet->findSegment(pos); - SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude()))); - SGGeod end (segment->getEnd()->geod()); - - double length = SGGeodesy::distanceM(start, end); - //heading = SGGeodesy::headingDeg(start->geod(), end->geod()); - - double az2, heading; //, distanceM; - SGGeodesy::inverse(start, end, heading, az2, length); - double coveredDistance = length * 0.5; - SGGeod center; - SGGeodesy::direct(start, heading, coveredDistance, center, az2); - SG_LOG(SG_ATC, SG_BULK, "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading); - /////////////////////////////////////////////////////////////////////////////// - // Make a helper function out of this - osg::Matrix obj_pos; - osg::MatrixTransform *obj_trans = new osg::MatrixTransform; - obj_trans->setDataVariance(osg::Object::STATIC); - // Experimental: Calculate slope here, based on length, and the individual elevations - double elevationStart; - if (isUserAircraft((i)->getAircraft())) { - elevationStart = fgGetDouble("/position/ground-elev-m"); - } else { - elevationStart = ((i)->getAircraft()->_getAltitude() * SG_FEET_TO_METER); - } - double elevationEnd = segment->getEnd()->getElevationM(); - if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) { - SGGeod center2 = end; - center2.setElevationM(SG_MAX_ELEVATION_M); - if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { - //elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5; - //elevation_meters += 0.5; - } - else { - elevationEnd = parent->getElevation(); - } - segment->getEnd()->setElevation(elevationEnd); - } - - double elevationMean = (elevationStart + elevationEnd) / 2.0; - double elevDiff = elevationEnd - elevationStart; - - double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES; - - SG_LOG(SG_ATC, SG_BULK, "1. Using mean elevation : " << elevationMean << " and " << slope); - - WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean + 0.5 + dx, -(heading), slope ); - ; - - obj_trans->setMatrix( obj_pos ); - //osg::Vec3 center(0, 0, 0) - - float width = length /2.0; - osg::Vec3 corner(-width, 0, 0.25f); - osg::Vec3 widthVec(2*width + 1, 0, 0); - osg::Vec3 heightVec(0, 1, 0); - osg::Geometry* geometry; - geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec); - simgear::EffectGeode* geode = new simgear::EffectGeode; - geode->setName("test"); - geode->addDrawable(geometry); - //osg::Node *custom_obj; - SGMaterial *mat; - if (segment->hasBlock(now)) { - mat = matlib->find("UnidirectionalTaperRed", center); - } else { - mat = matlib->find("UnidirectionalTaperGreen", center); - } - if (mat) - geode->setEffect(mat->get_effect()); - obj_trans->addChild(geode); - // wire as much of the scene graph together as we can - //->addChild( obj_trans ); - group->addChild( obj_trans ); - ///////////////////////////////////////////////////////////////////// - } else { - SG_LOG(SG_ATC, SG_DEBUG, "BIG FAT WARNING: current position is here : " << pos); - } - for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) { - osg::Matrix obj_pos; - int k = (*j); - if (k > 0) { - SG_LOG(SG_ATC, SG_BULK, "rendering for " << i->getAircraft()->getCallSign() << "intention = " << k); - osg::MatrixTransform *obj_trans = new osg::MatrixTransform; - obj_trans->setDataVariance(osg::Object::STATIC); - FGTaxiSegment *segment = groundNet->findSegment(k); - - double elevationStart = segment->getStart()->getElevationM(); - double elevationEnd = segment->getEnd ()->getElevationM(); - if ((elevationStart == 0) || (elevationStart == parent->getElevation())) { - SGGeod center2 = segment->getStart()->geod(); - center2.setElevationM(SG_MAX_ELEVATION_M); - if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) { - //elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5; - //elevation_meters += 0.5; - } - else { - elevationStart = parent->getElevation(); - } - segment->getStart()->setElevation(elevationStart); - } - if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) { - SGGeod center2 = segment->getEnd()->geod(); - center2.setElevationM(SG_MAX_ELEVATION_M); - if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { - //elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5; - //elevation_meters += 0.5; - } - else { - elevationEnd = parent->getElevation(); - } - segment->getEnd()->setElevation(elevationEnd); - } - - double elevationMean = (elevationStart + elevationEnd) / 2.0; - double elevDiff = elevationEnd - elevationStart; - double length = segment->getLength(); - double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES; - - SG_LOG(SG_ATC, SG_BULK, "2. Using mean elevation : " << elevationMean << " and " << slope); - - SGGeod segCenter(segment->getCenter()); - WorldCoordinate( obj_pos, segCenter.getLatitudeDeg(), - segCenter.getLongitudeDeg(), elevationMean + 0.5 + dx, -(segment->getHeading()), slope ); - - //WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), parent->getElevation()+8+dx, -(segment->getHeading()) ); - - obj_trans->setMatrix( obj_pos ); - //osg::Vec3 center(0, 0, 0) - - float width = segment->getLength() /2.0; - osg::Vec3 corner(-width, 0, 0.25f); - osg::Vec3 widthVec(2*width + 1, 0, 0); - osg::Vec3 heightVec(0, 1, 0); - osg::Geometry* geometry; - geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec); - simgear::EffectGeode* geode = new simgear::EffectGeode; - geode->setName("test"); - geode->addDrawable(geometry); - //osg::Node *custom_obj; - SGMaterial *mat; - if (segment->hasBlock(now)) { - mat = matlib->find("UnidirectionalTaperRed", segCenter); - } else { - mat = matlib->find("UnidirectionalTaperGreen", segCenter); - } - if (mat) - geode->setEffect(mat->get_effect()); - obj_trans->addChild(geode); - // wire as much of the scene graph together as we can - //->addChild( obj_trans ); - group->addChild( obj_trans ); - } else { - SG_LOG(SG_ATC, SG_DEBUG, "BIG FAT WARNING: k is here : " << pos); - } - } - dx += 0.2; - } - } - globals->get_scenery()->get_scene_graph()->addChild(group); - } -} - -string FGStartupController::getName() { - return string(parent->getId() + "-startup"); -} - -void FGStartupController::update(double dt) -{ - eraseDeadTraffic(activeTraffic); -} - - - -/*************************************************************************** - * class FGApproachController - * subclass of FGATCController - **************************************************************************/ - -FGApproachController::FGApproachController(FGAirportDynamics *par): - FGATCController() -{ - parent = par; -} - -FGApproachController::~FGApproachController() -{ - _isDestroying = true; - clearTrafficControllers(activeTraffic); -} - - -void FGApproachController::announcePosition(int id, - FGAIFlightPlan * intendedRoute, - int currentPosition, - double lat, double lon, - double heading, double speed, - double alt, double radius, - int leg, FGAIAircraft * ref) -{ - init(); - - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - - // Add a new TrafficRecord if no one exsists for this aircraft. - if (i == activeTraffic.end() || activeTraffic.empty()) { - 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 FGApproachController::updateAircraftInformation(int id, double lat, double lon, - double heading, double speed, double alt, - double dt) -{ - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - TrafficVectorIterator current; - - // update position of the current aircraft - if (i == activeTraffic.end() || activeTraffic.empty()) { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: updating aircraft without traffic record at " << SG_ORIGIN); - } else { - i->setPositionAndHeading(lat, lon, heading, speed, alt); - current = i; - SG_LOG(SG_ATC, SG_BULK, "ApproachController: checking for speed"); - time_t time_diff = - current->getAircraft()-> - checkForArrivalTime(string("final001")); - if (time_diff > 15) { - current->setSpeedAdjustment(current->getAircraft()-> - getPerformance()->vDescent() * - 1.35); - } else if (time_diff > 5) { - current->setSpeedAdjustment(current->getAircraft()-> - getPerformance()->vDescent() * - 1.2); - } else if (time_diff < -15) { - current->setSpeedAdjustment(current->getAircraft()-> - getPerformance()->vDescent() * - 0.65); - } else if (time_diff < -5) { - current->setSpeedAdjustment(current->getAircraft()-> - getPerformance()->vDescent() * - 0.8); - } else { - current->clearSpeedAdjustment(); - } - //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff); - } - setDt(getDt() + dt); -} - -/* Search for and erase traffic record with a specific id */ -void FGApproachController::signOff(int id) -{ - // ensure we don't modify activeTraffic during destruction - if (_isDestroying) - return; - - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - - if (i == activeTraffic.end() || activeTraffic.empty()) { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: Aircraft without traffic record is signing off from approach at " << SG_ORIGIN); - } else { - i = activeTraffic.erase(i); - } -} - -/* Periodically check for and remove dead traffic records */ -void FGApproachController::update(double dt) -{ - eraseDeadTraffic(activeTraffic); -} - -bool FGApproachController::hasInstruction(int id) -{ - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - - if (i == activeTraffic.end() || activeTraffic.empty()) { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN); - } else { - return i->hasInstruction(); - } - return false; -} - - -FGATCInstruction FGApproachController::getInstruction(int id) -{ - // Search activeTraffic for a record matching our id - TrafficVectorIterator i = searchActiveTraffic(activeTraffic, id); - - if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { - SG_LOG(SG_ATC, SG_ALERT, - "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN); - } else { - return i->getInstruction(); - } - return FGATCInstruction(); -} - - -ActiveRunway *FGApproachController::getRunway(const string& name) -{ - ActiveRunwayVecIterator rwy = activeRunways.begin(); - if (activeRunways.size()) { - while (rwy != activeRunways.end()) { - if (rwy->getRunwayName() == name) { - break; - } - rwy++; - } - } - if (rwy == activeRunways.end()) { - ActiveRunway aRwy(name, 0); - activeRunways.push_back(aRwy); - rwy = activeRunways.end() - 1; - } - return &(*rwy); -} - -void FGApproachController::render(bool visible) { - // Must be BULK in order to prevent it being called each frame - SG_LOG(SG_ATC, SG_BULK, "FGApproachController::render function not yet implemented"); -} - -string FGApproachController::getName() { - return string(parent->getId() + "-approach"); -} diff --git a/src/ATC/trafficcontrol.hxx b/src/ATC/trafficcontrol.hxx index 7c78ef678..be10cf0d2 100644 --- a/src/ATC/trafficcontrol.hxx +++ b/src/ATC/trafficcontrol.hxx @@ -146,7 +146,8 @@ public: class FGTrafficRecord { private: - int id, waitsForId; + int id; + int waitsForId; int currentPos; int leg; int frequencyId; @@ -365,210 +366,5 @@ public: void printDepartureQueue(); }; -/** - * class FGATCController - * NOTE: this class serves as an abstraction layer for all sorts of ATC controllers. - *************************************************************************************/ -class FGATCController -{ -private: - - -protected: - bool initialized; - bool available; - time_t lastTransmission; - - double dt_count; - osg::Group* group; - - std::string formatATCFrequency3_2(int ); - std::string genTransponderCode(const std::string& fltRules); - bool isUserAircraft(FGAIAircraft*); - - void eraseDeadTraffic(TrafficVector& vec); -public: - typedef enum { - MSG_ANNOUNCE_ENGINE_START, - MSG_REQUEST_ENGINE_START, - MSG_PERMIT_ENGINE_START, - MSG_DENY_ENGINE_START, - MSG_ACKNOWLEDGE_ENGINE_START, - MSG_REQUEST_PUSHBACK_CLEARANCE, - MSG_PERMIT_PUSHBACK_CLEARANCE, - MSG_HOLD_PUSHBACK_CLEARANCE, - MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, - MSG_INITIATE_CONTACT, - MSG_ACKNOWLEDGE_INITIATE_CONTACT, - MSG_REQUEST_TAXI_CLEARANCE, - MSG_ISSUE_TAXI_CLEARANCE, - MSG_ACKNOWLEDGE_TAXI_CLEARANCE, - MSG_HOLD_POSITION, - MSG_ACKNOWLEDGE_HOLD_POSITION, - MSG_RESUME_TAXI, - MSG_ACKNOWLEDGE_RESUME_TAXI, - MSG_REPORT_RUNWAY_HOLD_SHORT, - MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT, - MSG_SWITCH_TOWER_FREQUENCY, - MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY - } AtcMsgId; - - typedef enum { - ATC_AIR_TO_GROUND, - ATC_GROUND_TO_AIR - } AtcMsgDir; - FGATCController(); - virtual ~FGATCController(); - void init(); - - 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 updateAircraftInformation(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, FGAirportDynamics *parent, AtcMsgId msgId, AtcMsgDir msgDir, bool audible); - std::string getGateName(FGAIAircraft *aircraft); - virtual void render(bool) = 0; - virtual std::string getName() = 0; - - virtual void update(double) = 0; - - -protected: - // guard variable to avoid modifying state during destruction - bool _isDestroying = false; - -private: - - AtcMsgDir lastTransmissionDirection; -}; - -/****************************************************************************** - * class FGTowerControl - *****************************************************************************/ -class FGTowerController : public FGATCController -{ -private: - TrafficVector activeTraffic; - ActiveRunwayVec activeRunways; - FGAirportDynamics *parent; - -public: - FGTowerController(FGAirportDynamics *parent); - 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 updateAircraftInformation(int id, double lat, double lon, - double heading, double speed, double alt, double dt); - virtual bool hasInstruction(int id); - virtual FGATCInstruction getInstruction(int id); - - virtual void render(bool); - virtual std::string getName(); - virtual void update(double dt); - bool hasActiveTraffic() { - return ! activeTraffic.empty(); - }; - TrafficVector &getActiveTraffic() { - return activeTraffic; - }; -}; - -/****************************************************************************** - * class FGStartupController - * handle - *****************************************************************************/ - -class FGStartupController : public FGATCController -{ -private: - TrafficVector activeTraffic; - //ActiveRunwayVec activeRunways; - FGAirportDynamics *parent; - -public: - FGStartupController(FGAirportDynamics *parent); - 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 updateAircraftInformation(int id, double lat, double lon, - double heading, double speed, double alt, double dt); - virtual bool hasInstruction(int id); - virtual FGATCInstruction getInstruction(int id); - - virtual void render(bool); - virtual std::string getName(); - virtual void update(double dt); - - bool hasActiveTraffic() { - return ! activeTraffic.empty(); - }; - TrafficVector &getActiveTraffic() { - return activeTraffic; - }; - - // Hpoefully, we can move this function to the base class, but I need to verify what is needed for the other controllers before doing so. - bool checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId, - AtcMsgDir msgDir); - -}; - -/****************************************************************************** - * class FGTowerControl - *****************************************************************************/ -class FGApproachController : public FGATCController -{ -private: - TrafficVector activeTraffic; - ActiveRunwayVec activeRunways; - FGAirportDynamics *parent; - -public: - FGApproachController(FGAirportDynamics * parent); - virtual ~FGApproachController(); - - 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 updateAircraftInformation(int id, double lat, double lon, - double heading, double speed, double alt, double dt); - virtual bool hasInstruction(int id); - virtual FGATCInstruction getInstruction(int id); - - virtual void render(bool); - virtual std::string getName(); - virtual void update(double dt); - - ActiveRunway* getRunway(const std::string& name); - - bool hasActiveTraffic() { - return ! activeTraffic.empty(); - }; - TrafficVector &getActiveTraffic() { - return activeTraffic; - }; -}; - #endif // _TRAFFIC_CONTROL_HXX diff --git a/src/Airports/dynamics.hxx b/src/Airports/dynamics.hxx index c05edcb23..670f2a52d 100644 --- a/src/Airports/dynamics.hxx +++ b/src/Airports/dynamics.hxx @@ -28,7 +28,10 @@ #include #include +#include +#include #include +#include #include "airports_fwd.hxx" #include "parking.hxx"