diff --git a/configure.ac b/configure.ac index 8897c4e72..295578d3a 100644 --- a/configure.ac +++ b/configure.ac @@ -649,7 +649,7 @@ AC_CONFIG_FILES([ \ src/Include/config.h-msvc8 \ src/Aircraft/Makefile \ src/Airports/Makefile \ - src/ATC/Makefile \ + src/ATCDCL/Makefile \ src/Autopilot/Makefile \ src/Cockpit/Makefile \ src/Cockpit/built_in/Makefile \ diff --git a/src/ATC/AIEntity.cxx b/src/ATC/AIEntity.cxx deleted file mode 100644 index 41baf4a05..000000000 --- a/src/ATC/AIEntity.cxx +++ /dev/null @@ -1,79 +0,0 @@ -// FGAIEntity - abstract base class an artificial intelligence entity -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -/***************************************************************** -* -* WARNING - Curt has some ideas about AI traffic so anything in here -* may get rewritten or scrapped. Contact Curt http://www.flightgear.org/~curt -* before spending any time or effort on this code!!! -* -******************************************************************/ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include
-#include -#include -#include -#include -#include -#include - -#include "AIEntity.hxx" - -FGAIEntity::FGAIEntity() { -} - -FGAIEntity::~FGAIEntity() { - //cout << "FGAIEntity dtor called..." << endl; - //cout << "Removing model from scene graph..." << endl; - globals->get_scenery()->get_scene_graph()->removeChild(_aip.getSceneGraph()); - - //cout << "Done!" << endl; -} - -void FGAIEntity::SetModel(osg::Node* model) { - _model = model; - _aip.init(_model.get()); - _aip.setVisible(false); - globals->get_scenery()->get_scene_graph()->addChild(_aip.getSceneGraph()); - -} - -void FGAIEntity::Update(double dt) { -} - -const string &FGAIEntity::GetCallsign() { - static string s = ""; - return(s); -} - -void FGAIEntity::RegisterTransmission(int code) { -} - -// Run the internal calculations -//void FGAIEntity::Update() { -void FGAIEntity::Transform() { - _aip.setPosition(_pos.lon(), _pos.lat(), _pos.elev() * SG_METER_TO_FEET); - _aip.setOrientation(_roll, _pitch, _hdg); - _aip.update(); -} diff --git a/src/ATC/AIEntity.hxx b/src/ATC/AIEntity.hxx deleted file mode 100644 index 1b184a833..000000000 --- a/src/ATC/AIEntity.hxx +++ /dev/null @@ -1,72 +0,0 @@ -// FGAIEntity - abstract base class an artificial intelligence entity -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifndef _FG_AIEntity_HXX -#define _FG_AIEntity_HXX - -#include -#include - - -/***************************************************************** -* -* FGAIEntity - this class implements the minimum requirement -* for any AI entity - a position, an orientation, an associated -* 3D model, and the ability to be moved. It does nothing useful -* and all AI entities are expected to be derived from it. -* -******************************************************************/ -class FGAIEntity { - -public: - - FGAIEntity(); - virtual ~FGAIEntity(); - - // Set the 3D model to use (Must be called) - void SetModel(osg::Node* model); - - // Run the internal calculations - virtual void Update(double dt)=0; - - // Send a transmission *TO* the AIEntity. - // FIXME int code is a hack - eventually this will receive Alexander's coded messages. - virtual void RegisterTransmission(int code)=0; - - inline const Point3D& GetPos() const { return(_pos); } - - virtual const string& GetCallsign()=0; - -protected: - - Point3D _pos; // WGS84 lat & lon in degrees, elev above sea-level in meters - double _hdg; //True heading in degrees - double _roll; //degrees - double _pitch; //degrees - - char* _model_path; //Path to the 3D model - osg::ref_ptr _model; // Pointer to the model - SGModelPlacement _aip; - - void Transform(); -}; - -#endif // _FG_AIEntity_HXX - diff --git a/src/ATC/AIGAVFRTraffic.cxx b/src/ATC/AIGAVFRTraffic.cxx deleted file mode 100644 index 6941a2489..000000000 --- a/src/ATC/AIGAVFRTraffic.cxx +++ /dev/null @@ -1,463 +0,0 @@ -// FGAILocalTraffic - AIEntity derived class with enough logic to -// fly and interact with the traffic pattern. -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include
-#include -#include -#include - -SG_USING_STD(string); - -#include "ATC.hxx" -#include "ATCmgr.hxx" -#include "AILocalTraffic.hxx" -#include "AIGAVFRTraffic.hxx" -#include "ATCutils.hxx" -#include "tower.hxx" - -FGAIGAVFRTraffic::FGAIGAVFRTraffic() { - ATC = globals->get_ATC_mgr(); - _towerContactedIncoming = false; - _clearedStraightIn = false; - _clearedDownwindEntry = false; - _incoming = false; - _straightIn = false; - _downwindEntry = false; - _climbout = false; - _local = false; - _established = false; - _e45 = false; - _entering = false; - _turning = false; - _cruise_climb_ias = 90.0; - _cruise_ias = 110.0; - patternDirection = -1.0; - - // TESTING - REMOVE OR COMMENT OUT BEFORE COMMIT!!! - //_towerContactPrinted = false; -} - -FGAIGAVFRTraffic::~FGAIGAVFRTraffic() { -} - -// We should never need to Init FGAIGAVFRTraffic in the pattern since that implies arrivel -// and we can just use an FGAILocalTraffic instance for that instead. - -// Init en-route to destID at point pt. -// TODO - no idea what to do if pt is above planes ceiling due mountains!! -bool FGAIGAVFRTraffic::Init(const Point3D& pt, const string& destID, const string& callsign) { - FGAILocalTraffic::Init(callsign, destID, EN_ROUTE); - // TODO FIXME - to get up and running we're going to ignore elev and get FGAIMgr to - // pass in known good values for the test location. Need to fix this!!! (or at least canonically decide who has responsibility for setting elev). - _enroute = true; - _destID = destID; - _pos = pt; - _destPos = fgGetAirportPos(destID); // TODO - check if we are within the tower catchment area already. - _cruise_alt = (_destPos.elev() + 2500.0) * SG_FEET_TO_METER; // TODO look at terrain elevation as well - _pos.setelev(_cruise_alt); - // initially set waypoint as airport location - _wp = _destPos; - // Set the initial track - track = GetHeadingFromTo(_pos, _wp); - // And set the plane to keep following it. - SetTrack(GetHeadingFromTo(_pos, _wp)); - _roll = 0.0; - _pitch = 0.0; - slope = 0.0; - // TODO - set climbout if altitude is below normal cruising altitude? - //Transform(); - // Assume it's OK to set the plane visible - _aip.setVisible(true); - //cout << "Setting visible true\n"; - Transform(); - return(true); -} - -// Init at srcID to fly to destID -bool FGAIGAVFRTraffic::Init(const string& srcID, const string& destID, const string& callsign, OperatingState state) { - _enroute = false; - FGAILocalTraffic::Init(callsign, srcID, PARKED); - return(true); -} - -void FGAIGAVFRTraffic::Update(double dt) { - if(_enroute) { - //cout << "_enroute\n"; - //cout << "e" << flush; - FlyPlane(dt); - //cout << "f" << flush; - Transform(); - //cout << "g" << flush; - FGAIPlane::Update(dt); - //cout << "h" << flush; - responseCounter += dt; - - // we shouldn't really need this since there's a LOD of 10K on the whole plane anyway I think. - // There are two _aip.setVisible statements set when _local = true that can be removed if the below is removed. - if(dclGetHorizontalSeparation(_pos, Point3D(fgGetDouble("/position/longitude-deg"), fgGetDouble("/position/latitude-deg"), 0.0)) > 8000) _aip.setVisible(false); - else _aip.setVisible(true); - - } else if(_local) { - //cout << "L"; - //cout << "_local\n"; - FGAILocalTraffic::Update(dt); - } -} - -void FGAIGAVFRTraffic::FlyPlane(double dt) { - if(_climbout) { - // Check whether to level off - if(_pos.elev() >= _cruise_alt) { - slope = 0.0; - _pitch = 0.0; - IAS = _cruise_ias; // FIXME - use smooth transistion to new speed and attitude. - _climbout = false; - } else { - slope = 4.0; - _pitch = 5.0; - IAS = _cruise_climb_ias; - } - } else { - // TESTING - /* - if(dclGetHorizontalSeparation(_destPos, _pos) / 1600.0 < 8.1) { - if(!_towerContactPrinted) { - if(airportID == "KSQL") { - cout << "****************************************************************\n"; - cout << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"; - cout << "****************************************************************\n"; - } - _towerContactPrinted = true; - } - } - */ - - // if distance to destination is less than 6 - 9 miles contact tower - // and prepare to become _incoming after response. - // Possibly check whether to start descent before this? - //cout << "." << flush; - //cout << "sep = " << dclGetHorizontalSeparation(_destPos, _pos) / 1600.0 << '\n'; - if(dclGetHorizontalSeparation(_destPos, _pos) / 1600.0 < 8.0) { - //cout << "-" << flush; - if(!_towerContactedIncoming) { - //cout << "_" << flush; - GetAirportDetails(airportID); - //cout << "L" << flush; - if(_controlled) { - freq = (double)tower->get_freq() / 100.0; - tuned_station = tower; - } else { - freq = 122.8; // TODO - need to get the correct CTAF/Unicom frequency if no tower - tuned_station = NULL; - } - //cout << "freq = " << freq << endl; - GetRwyDetails(airportID); - //"@AP Tower @CS @MI miles @CD of the airport for full stop with ATIS" - // At the bare minimum we ought to make sure it goes the right way at dual parallel rwy airports! - if(rwy.rwyID.size() == 3) { - patternDirection = (rwy.rwyID.substr(2,1) == "R" ? 1 : -1); - } - if(_controlled) { - pending_transmission = tower->get_name(); - pending_transmission += " Tower "; - } else { - pending_transmission = "Traffic "; - // TODO - find some way of getting uncontrolled airport name - } - pending_transmission += plane.callsign; - //char buf[10]; - int dist_miles = (int)dclGetHorizontalSeparation(_pos, _destPos) / 1600; - //sprintf(buf, " %i ", dist_miles); - pending_transmission += " "; - pending_transmission += ConvertNumToSpokenDigits(dist_miles); - if(dist_miles > 1) pending_transmission += " miles "; - else pending_transmission += " mile "; - pending_transmission += GetCompassDirection(GetHeadingFromTo(_destPos, _pos)); - pending_transmission += " of the airport for full stop with ATIS"; - //cout << pending_transmission << endl; - Transmit(14); // 14 is the callback code, NOT the timeout! - responseCounter = 0; - _towerContactedIncoming = true; - } else { - //cout << "?" << flush; - if(_clearedStraightIn && responseCounter > 5.5) { - //cout << "5 " << flush; - _clearedStraightIn = false; - _straightIn = true; - _incoming = true; - _wp = GetPatternApproachPos(); - //_hdg = GetHeadingFromTo(_pos, _wp); // TODO - turn properly! - SetTrack(GetHeadingFromTo(_pos, _wp)); - slope = atan((_wp.elev() - _pos.elev()) / dclGetHorizontalSeparation(_wp, _pos)) * DCL_RADIANS_TO_DEGREES; - double thesh_offset = 0.0; - Point3D opos = ortho.ConvertToLocal(_pos); - double angToApt = atan((_pos.elev() - fgGetAirportElev(airportID)) / (opos.y() - thesh_offset)) * DCL_RADIANS_TO_DEGREES; - //cout << "angToApt = " << angToApt << ' '; - slope = (angToApt > -5.0 ? 0.0 : angToApt); - //cout << "slope = " << slope << '\n'; - pending_transmission = "Straight-in "; - pending_transmission += ConvertRwyNumToSpokenString(rwy.rwyID); - pending_transmission += " "; - pending_transmission += plane.callsign; - //cout << pending_transmission << '\n'; - ConditionalTransmit(4); - } else if(_clearedDownwindEntry && responseCounter > 5.5) { - //cout << "6" << flush; - _clearedDownwindEntry = false; - _downwindEntry = true; - _incoming = true; - _wp = GetPatternApproachPos(); - SetTrack(GetHeadingFromTo(_pos, _wp)); - slope = atan((_wp.elev() - _pos.elev()) / dclGetHorizontalSeparation(_wp, _pos)) * DCL_RADIANS_TO_DEGREES; - //cout << "slope = " << slope << '\n'; - pending_transmission = "Report "; - pending_transmission += (patternDirection == 1 ? "right downwind " : "left downwind "); - pending_transmission += ConvertRwyNumToSpokenString(rwy.rwyID); - pending_transmission += " "; - pending_transmission += plane.callsign; - //cout << pending_transmission << '\n'; - ConditionalTransmit(4); - } - } - if(_pos.elev() < (fgGetAirportElev(airportID) + (1000.0 * SG_FEET_TO_METER))) slope = 0.0; - } - } - if(_incoming) { - //cout << "i" << '\n'; - Point3D orthopos = ortho.ConvertToLocal(_pos); - // TODO - Check whether to start descent - // become _local after the 3 mile report. - if(_pos.elev() < (fgGetAirportElev(airportID) + (1000.0 * SG_FEET_TO_METER))) slope = 0.0; - // TODO - work out why I needed to add the above line to stop the plane going underground!!! - // (Although it's worth leaving it in as a robustness check anyway). - if(_straightIn) { - //cout << "A " << flush; - if(fabs(orthopos.x()) < 10.0 && !_established) { - SetTrack(rwy.hdg); - _established = true; - //cout << "Established at " << orthopos << '\n'; - } - double thesh_offset = 30.0; - //cout << "orthopos.y = " << orthopos.y() << " alt = " << _pos.elev() - fgGetAirportElev(airportID) << '\n'; - if(_established && (orthopos.y() > -5400.0)) { - slope = atan((_pos.elev() - fgGetAirportElev(airportID)) / (orthopos.y() - thesh_offset)) * DCL_RADIANS_TO_DEGREES; - //cout << "slope0 = " << slope << '\n'; - } - //cout << "slope1 = " << slope << '\n'; - if(slope > -5.5) slope = 0.0; // ie we're too low. - //cout << "slope2 = " << slope << '\n'; - slope += 0.001; // To avoid yo-yoing with the above. - //if(_established && (orthopos.y() > -5400.0)) slope = -5.5; - if(_established && (orthopos.y() > -4800.0)) { - pending_transmission = "3 mile final Runway "; - pending_transmission += ConvertRwyNumToSpokenString(rwy.rwyID); - pending_transmission += " "; - pending_transmission += plane.callsign; - //cout << pending_transmission << '\n'; - ConditionalTransmit(35); - _local = true; - _aip.setVisible(true); // HACK - _enroute = false; - StraightInEntry(true); - } - } else if(_downwindEntry) { - //cout << "B" << flush; - if(_entering) { - //cout << "C" << flush; - if(_turning) { - if(fabs(_hdg - (rwy.hdg + 180)) < 2.0) { // TODO - use track instead of _hdg? - //cout << "Going Local...\n"; - leg = DOWNWIND; - _local = true; - _aip.setVisible(true); // HACK - _enroute = false; - _entering = false; - _turning = false; - DownwindEntry(); - } - } - if(fabs(orthopos.x() - (patternDirection == 1 ? 1000 : -1000)) < (_e45 ? 175 : 550)) { // Caution - hardwired turn clearances. - //cout << "_turning...\n"; - _turning = true; - SetTrack(rwy.hdg + 180.0); - } // TODO - need to check for other traffic in the pattern and enter much more integilently than that!!! - } else { - //cout << "D" << flush; - //cout << '\n' << dclGetHorizontalSeparation(_wp, _pos) << '\n'; - //cout << ortho.ConvertToLocal(_pos); - //cout << ortho.ConvertToLocal(_wp); - if(dclGetHorizontalSeparation(_wp, _pos) < 100.0) { - pending_transmission = "2 miles out for "; - pending_transmission += (patternDirection == 1 ? "right " : "left "); - pending_transmission += "downwind Runway "; - pending_transmission += ConvertRwyNumToSpokenString(rwy.rwyID); - pending_transmission += " "; - pending_transmission += plane.callsign; - //cout << pending_transmission << '\n'; - // TODO - are we at pattern altitude?? - slope = 0.0; - ConditionalTransmit(30); - if(_e45) { - SetTrack(patternDirection == 1 ? rwy.hdg - 135.0 : rwy.hdg + 135.0); - } else { - SetTrack(patternDirection == 1 ? rwy.hdg + 90.0 : rwy.hdg - 90.0); - } - //if(_hdg < 0.0) _hdg += 360.0; - _entering = true; - } else { - SetTrack(GetHeadingFromTo(_pos, _wp)); - } - } - } - } else { - // !_incoming - slope = 0.0; - } - // FIXME - lots of hackery in the next six lines!!!! - //double track = _hdg; - double crab = 0.0; // This is a placeholder for when we take wind into account. - _hdg = track + crab; - double vel = _cruise_ias; - double dist = vel * 0.514444 * dt; - _pos = dclUpdatePosition(_pos, track, slope, dist); -} - -void FGAIGAVFRTraffic::RegisterTransmission(int code) { - switch(code) { - case 1: // taxi request cleared - FGAILocalTraffic::RegisterTransmission(code); - break; - case 2: // contact tower - FGAILocalTraffic::RegisterTransmission(code); - break; - case 3: // Cleared to line up - FGAILocalTraffic::RegisterTransmission(code); - break; - case 4: // cleared to take-off - FGAILocalTraffic::RegisterTransmission(code); - break; - case 5: // contact ground - FGAILocalTraffic::RegisterTransmission(code); - break; - case 6: // taxi to the GA parking - FGAILocalTraffic::RegisterTransmission(code); - break; - case 7: // Cleared to land - FGAILocalTraffic::RegisterTransmission(code); - break; - case 13: // Go around! - FGAILocalTraffic::RegisterTransmission(code); - break; - case 14: // VFR approach for straight-in - responseCounter = 0; - _clearedStraightIn = true; - break; - case 15: // VFR approach for downwind entry - responseCounter = 0; - _clearedDownwindEntry = true; - break; - default: - SG_LOG(SG_ATC, SG_WARN, "FGAIGAVFRTraffic::RegisterTransmission(...) called with unknown code " << code); - FGAILocalTraffic::RegisterTransmission(code); - break; - } -} - -// Callback handler -// TODO - Really should enumerate these coded values. -void FGAIGAVFRTraffic::ProcessCallback(int code) { - // 1 - Request Departure from ground - // 2 - Report at hold short - // 10 - report crosswind - // 11 - report downwind - // 12 - report base - // 13 - report final - // 14 - Contact Tower for VFR arrival - // 99 - Remove self - if(code < 14) { - FGAILocalTraffic::ProcessCallback(code); - } else if(code == 14) { - if(_controlled) { - tower->VFRArrivalContact(plane, this, FULL_STOP); - } - // TODO else possibly announce arrival intentions at uncontrolled airport? - } else if(code == 99) { - // Might handle this different in future - hence separated from the other codes to pass to AILocalTraffic. - FGAILocalTraffic::ProcessCallback(code); - } -} - -// Return an appropriate altitude to fly at based on the desired altitude and direction -// whilst respecting the quadrangle rule. -int FGAIGAVFRTraffic::GetQuadrangleAltitude(int dir, int des_alt) { - return(8888); - // TODO - implement me! -} - -// Calculates the position needed to set up for either pattern entry or straight in approach. -// Currently returns one of three positions dependent on initial position wrt threshold of active rwy. -// 1/ A few miles out on extended centreline for straight-in. -// 2/ At an appropriate point on circuit side of rwy for a 45deg entry to downwind. -// 3/ At and appropriate point on non-circuit side of rwy at take-off end for perpendicular entry to circuit overflying end-of-rwy. -Point3D FGAIGAVFRTraffic::GetPatternApproachPos() { - //cout << "\n\n"; - //cout << "Calculating pattern approach pos for " << plane.callsign << '\n'; - Point3D orthopos = ortho.ConvertToLocal(_pos); - Point3D tmp; - //cout << "patternDirection = " << patternDirection << '\n'; - if(orthopos.y() >= -1000.0) { // Note that this has to be set the same as the calculation in tower.cxx - at the moment approach type is not transmitted properly between the two. - //cout << "orthopos.x = " << orthopos.x() << '\n'; - if((orthopos.x() * patternDirection) > 0.0) { // 45 deg entry - tmp.setx(2000 * patternDirection); - tmp.sety((rwy.end2ortho.y() / 2.0) + 2000); - tmp.setelev(fgGetAirportElev(airportID) + (1000 * SG_FEET_TO_METER)); - _e45 = true; - //cout << "45 deg entry... "; - } else { - tmp.setx(1000 * patternDirection * -1); - tmp.sety(rwy.end2ortho.y()); - tmp.setelev(fgGetAirportElev(airportID) + (1000 * SG_FEET_TO_METER)); - _e45 = false; - //cout << "90 deg entry... "; - } - } else { - tmp.setx(0); - tmp.sety(-5400); - tmp.setelev((5400.0 / 6.0) + fgGetAirportElev(airportID) + 10.0); - //cout << "Straight in... "; - } - //cout << "Waypoint is " << tmp << '\n'; - //cout << ortho.ConvertFromLocal(tmp) << '\n'; - //cout << '\n'; - //exit(-1); - return ortho.ConvertFromLocal(tmp); -} - -//FGAIGAVFRTraffic:: - -//FGAIGAVFRTraffic:: - -//FGAIGAVFRTraffic:: diff --git a/src/ATC/AIGAVFRTraffic.hxx b/src/ATC/AIGAVFRTraffic.hxx deleted file mode 100644 index 9aabd8746..000000000 --- a/src/ATC/AIGAVFRTraffic.hxx +++ /dev/null @@ -1,126 +0,0 @@ -// FGAILocalTraffic - AIEntity derived class with enough logic to -// fly and interact with the traffic pattern. -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifndef _FG_AIGAVFRTraffic_HXX -#define _FG_AIGAVFRTraffic_HXX - -#include -#include
- -#include "AILocalTraffic.hxx" - -#include -SG_USING_STD(string); - -class FGAIGAVFRTraffic : public FGAILocalTraffic { - -public: - - FGAIGAVFRTraffic(); - ~FGAIGAVFRTraffic(); - - // Init en-route to destID at point pt. (lat, lon, elev) (elev in meters, lat and lon in degrees). - bool Init(const Point3D& pt, const string& destID, const string& callsign); - // Init at srcID to fly to destID - bool Init(const string& srcID, const string& destID, const string& callsign, OperatingState state = PARKED); - - // Run the internal calculations - void Update(double dt); - - // Return what type of landing we're doing on this circuit - //LandingType GetLandingOption(); - - void RegisterTransmission(int code); - - // Process callbacks sent by base class - // (These codes are not related to the codes above) - void ProcessCallback(int code); - -protected: - - // Do what is necessary to land and parkup at home airport - void ReturnToBase(double dt); - - //void GetRwyDetails(string id); - - -private: - FGATCMgr* ATC; - // This is purely for synactic convienience to avoid writing globals->get_ATC_mgr()-> all through the code! - - // High-level stuff - OperatingState operatingState; - bool touchAndGo; //True if circuits should be flown touch and go, false for full stop - - // Performance characteristics of the plane in knots and ft/min - some of this might get moved out into FGAIPlane - double best_rate_of_climb_speed; - double best_rate_of_climb; - double nominal_climb_speed; - double nominal_climb_rate; - double nominal_cruise_speed; - double nominal_circuit_speed; - double nominal_descent_rate; - double nominal_approach_speed; - double nominal_final_speed; - double stall_speed_landing_config; - - // environment - some of this might get moved into FGAIPlane - SGPropertyNode_ptr wind_from_hdg; //degrees - SGPropertyNode_ptr wind_speed_knots; //knots - - atc_type changeFreqType; // the service we need to change to - - void CalculateSoD(double base_leg_pos, double downwind_leg_pos, bool pattern_direction); - - // GA VFR specific - bool _towerContactedIncoming; - bool _straightIn; - bool _clearedStraightIn; - bool _downwindEntry; - bool _clearedDownwindEntry; - Point3D _wp; // Next waypoint (ie. the one we're currently heading for) - bool _enroute; - string _destID; - bool _climbout; - double _cruise_alt; - double _cruise_ias; - double _cruise_climb_ias; - Point3D _destPos; - bool _local; - bool _incoming; - bool _established; - bool _e45; - bool _entering; - bool _turning; - - //ssgBranch* _model; - - int GetQuadrangleAltitude(int dir, int des_alt); - - Point3D GetPatternApproachPos(); - - void FlyPlane(double dt); - - // HACK for testing - remove or comment out before CVS commit!!! - //bool _towerContactPrinted; -}; - -#endif // _FG_AILocalTraffic_HXX diff --git a/src/ATC/AILocalTraffic.cxx b/src/ATC/AILocalTraffic.cxx deleted file mode 100644 index 15eb6a3d3..000000000 --- a/src/ATC/AILocalTraffic.cxx +++ /dev/null @@ -1,1596 +0,0 @@ -// FGAILocalTraffic - AIEntity derived class with enough logic to -// fly and interact with the traffic pattern. -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -/*========================================================== - -TODO list. - -Should get pattern direction from tower. - -Need to continually monitor and adjust deviation from glideslope -during descent to avoid occasionally landing short or long. - -============================================================*/ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include - -#include -#include
-#include
-#include -#include -#include -#include -#include -#include -#include - -SG_USING_STD(string); - -#include "ATCmgr.hxx" -#include "AILocalTraffic.hxx" -#include "ATCutils.hxx" -#include "AIMgr.hxx" - -FGAILocalTraffic::FGAILocalTraffic() { - /*ssgBranch *model = sgLoad3DModel( globals->get_fg_root(), - planepath.c_str(), - globals->get_props(), - globals->get_sim_time_sec() ); - *//* - _model = model; - _aip.init(_model); - */ - //SetModel(model); - - ATC = globals->get_ATC_mgr(); - - // TODO - unhardwire this - plane.type = GA_SINGLE; - - _roll = 0.0; - _pitch = 0.0; - _hdg = 270.0; - - //Hardwire initialisation for now - a lot of this should be read in from config eventually - Vr = 70.0; - best_rate_of_climb_speed = 70.0; - //best_rate_of_climb; - //nominal_climb_speed; - //nominal_climb_rate; - //nominal_circuit_speed; - //min_circuit_speed; - //max_circuit_speed; - nominal_descent_rate = 500.0; - nominal_final_speed = 65.0; - //nominal_approach_speed; - //stall_speed_landing_config; - nominalTaxiSpeed = 7.5; - taxiTurnRadius = 8.0; - wheelOffset = 1.45; // Warning - hardwired to the C172 - we need to read this in from file. - elevInitGood = false; - // Init the property nodes - wind_from_hdg = fgGetNode("/environment/wind-from-heading-deg", true); - wind_speed_knots = fgGetNode("/environment/wind-speed-kt", true); - circuitsToFly = 0; - liningUp = false; - taxiRequestPending = false; - taxiRequestCleared = false; - holdingShort = false; - clearedToLineUp = false; - clearedToTakeOff = false; - _clearedToLand = false; - reportReadyForDeparture = false; - contactTower = false; - contactGround = false; - _taxiToGA = false; - _removeSelf = false; - - descending = false; - targetDescentRate = 0.0; - goAround = false; - goAroundCalled = false; - - transmitted = false; - - freeTaxi = false; - _savedSlope = 0.0; - - _controlled = false; - - _invisible = false; -} - -FGAILocalTraffic::~FGAILocalTraffic() { -} - -void FGAILocalTraffic::GetAirportDetails(const string& id) { - AirportATC a; - if(ATC->GetAirportATCDetails(airportID, &a)) { - if(a.tower_freq) { // Has a tower - TODO - check the opening hours!!! - tower = (FGTower*)ATC->GetATCPointer(airportID, TOWER); - if(tower == NULL) { - // Something has gone wrong - abort or carry on with un-towered operation? - SG_LOG(SG_ATC, SG_ALERT, "ERROR - can't get a tower pointer from tower control for " << airportID << " in FGAILocalTraffic::GetAirportDetails() :-("); - _controlled = false; - } else { - _controlled = true; - } - if(tower) { - ground = tower->GetGroundPtr(); - if(ground == NULL) { - // Something has gone wrong :-( - SG_LOG(SG_ATC, SG_ALERT, "ERROR - can't get a ground pointer from tower control in FGAILocalTraffic::GetAirportDetails() :-("); - } - } - } else { - _controlled = false; - // TODO - Check CTAF, unicom etc - } - } else { - SG_LOG(SG_ATC, SG_ALERT, "Unable to find airport details in for " << airportID << " in FGAILocalTraffic::GetAirportDetails() :-("); - _controlled = false; - } - // Get the airport elevation - aptElev = fgGetAirportElev(airportID.c_str()); - //cout << "Airport elev in AILocalTraffic = " << aptElev << '\n'; - // WARNING - we use this elev for the whole airport - some assumptions in the code - // might fall down with very slopey airports. -} - -// Get details of the active runway -// It is assumed that by the time this is called the tower control and airport code will have been set up. -void FGAILocalTraffic::GetRwyDetails(const string& id) { - //cout << "GetRwyDetails called" << endl; - - if(_controlled) { - rwy.rwyID = tower->GetActiveRunway(); - } else { - // TODO - get a proper runway ID from uncontrolled airports - rwy.rwyID = "00"; - } - - // Now we need to get the threshold position and rwy heading - - FGRunway runway; - bool rwyGood = globals->get_runways()->search(id, rwy.rwyID, &runway); - if(rwyGood) { - double hdg = runway._heading; - double other_way = hdg - 180.0; - while(other_way <= 0.0) { - other_way += 360.0; - } - - // move to the +l end/center of the runway - //cout << "Runway center is at " << runway._lon << ", " << runway._lat << '\n'; - Point3D origin = Point3D(runway._lon, runway._lat, aptElev); - Point3D ref = origin; - double tshlon, tshlat, tshr; - double tolon, tolat, tor; - rwy.length = runway._length * SG_FEET_TO_METER; - rwy.width = runway._width * SG_FEET_TO_METER; - geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), other_way, - rwy.length / 2.0 - 25.0, &tshlat, &tshlon, &tshr ); - geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), hdg, - rwy.length / 2.0 - 25.0, &tolat, &tolon, &tor ); - // Note - 25 meters in from the runway end is a bit of a hack to put the plane ahead of the user. - // now copy what we need out of runway into rwy - rwy.threshold_pos = Point3D(tshlon, tshlat, aptElev); - Point3D takeoff_end = Point3D(tolon, tolat, aptElev); - //cout << "Threshold position = " << tshlon << ", " << tshlat << ", " << aptElev << '\n'; - //cout << "Takeoff position = " << tolon << ", " << tolat << ", " << aptElev << '\n'; - rwy.hdg = hdg; - // Set the projection for the local area - //cout << "Initing ortho for airport " << id << '\n'; - ortho.Init(rwy.threshold_pos, rwy.hdg); - rwy.end1ortho = ortho.ConvertToLocal(rwy.threshold_pos); // should come out as zero - rwy.end2ortho = ortho.ConvertToLocal(takeoff_end); - } else { - SG_LOG(SG_ATC, SG_ALERT, "Help - can't get good runway at airport " << id << " in FGAILocalTraffic!!"); - } -} - - -/* -There are two possible scenarios during initialisation: -The first is that the user is flying towards the airport, and hence the traffic -could be initialised anywhere, as long as the AI planes are consistent with -each other. -The second is that the user has started the sim at or close to the airport, and -hence the traffic must be initialised with respect to the user as well as each other. -To a certain extent it's FGAIMgr that has to worry about this, but we need to provide -sufficient initialisation functionality within the plane classes to allow the manager -to initially position them where and how required. -*/ -bool FGAILocalTraffic::Init(const string& callsign, const string& ICAO, OperatingState initialState, PatternLeg initialLeg) { - //cout << "FGAILocalTraffic.Init(...) called" << endl; - airportID = ICAO; - - plane.callsign = callsign; - - if(initialState == EN_ROUTE) return(true); - - // Get the ATC pointers and airport elev - GetAirportDetails(airportID); - - // Get the active runway details (and copy them into rwy) - GetRwyDetails(airportID); - //cout << "Runway is " << rwy.rwyID << '\n'; - - // FIXME TODO - pattern direction is still hardwired - patternDirection = -1; // Left - // At the bare minimum we ought to make sure it goes the right way at dual parallel rwy airports! - if(rwy.rwyID.size() == 3) { - patternDirection = (rwy.rwyID.substr(2,1) == "R" ? 1 : -1); - } - - if(_controlled) { - if((initialState == PARKED) || (initialState == TAXIING)) { - freq = (double)ground->get_freq() / 100.0; - } else { - freq = (double)tower->get_freq() / 100.0; - } - } else { - freq = 122.8; - // TODO - find the proper freq if CTAF or unicom or after-hours. - } - - //cout << "In Init(), initialState = " << initialState << endl; - operatingState = initialState; - Point3D orthopos; - switch(operatingState) { - case PARKED: - tuned_station = ground; - ourGate = ground->GetGateNode(); - if(ourGate == NULL) { - // Implies no available gates - what shall we do? - // For now just vanish the plane - possibly we can make this more elegant in the future - SG_LOG(SG_ATC, SG_ALERT, "No gate found by FGAILocalTraffic whilst attempting Init at " << airportID << '\n'); - return(false); - } - _pitch = 0.0; - _roll = 0.0; - vel = 0.0; - slope = 0.0; - _pos = ourGate->pos; - _pos.setelev(aptElev); - _hdg = ourGate->heading; - Transform(); - - // Now we've set the position we can do the ground elev - elevInitGood = false; - inAir = false; - DoGroundElev(); - - break; - case TAXIING: - //tuned_station = ground; - // FIXME - implement this case properly - // For now we'll assume that the plane should start at the hold short in this case - // and that we're working without ground network elements. Ie. an airport with no facility file. - if(_controlled) { - tuned_station = tower; - } else { - tuned_station = NULL; - } - freeTaxi = true; - // Set a position and orientation in an approximate place for hold short. - //cout << "rwy.width = " << rwy.width << '\n'; - orthopos = Point3D((rwy.width / 2.0 + 10.0) * -1.0, 0.0, 0.0); - // TODO - set the x pos to be +ve if a RH parallel rwy. - _pos = ortho.ConvertFromLocal(orthopos); - _pos.setelev(aptElev); - _hdg = rwy.hdg + 90.0; - // TODO - reset the heading if RH rwy. - _pitch = 0.0; - _roll = 0.0; - vel = 0.0; - slope = 0.0; - elevInitGood = false; - inAir = false; - Transform(); - DoGroundElev(); - //Transform(); - - responseCounter = 0.0; - contactTower = false; - changeFreq = true; - holdingShort = true; - clearedToLineUp = false; - changeFreqType = TOWER; - - break; - case IN_PATTERN: - // For now we'll always start the in_pattern case on the threshold ready to take-off - // since we've got the implementation for this case already. - // TODO - implement proper generic in_pattern startup. - - // 18/10/03 - adding the ability to start on downwind (mainly to speed testing of the go-around code!!) - - //cout << "Starting in pattern...\n"; - - if(_controlled) { - tuned_station = tower; - } else { - tuned_station = NULL; - } - - circuitsToFly = 0; // ie just fly this circuit and then stop - touchAndGo = false; - - if(initialLeg == DOWNWIND) { - _pos = ortho.ConvertFromLocal(Point3D(1000*patternDirection, 800, 0.0)); - _pos.setelev(rwy.threshold_pos.elev() + 1000 * SG_FEET_TO_METER); - _hdg = rwy.hdg + 180.0; - leg = DOWNWIND; - elevInitGood = false; - inAir = true; - SetTrack(rwy.hdg - (180 * patternDirection)); - slope = 0.0; - _pitch = 0.0; - _roll = 0.0; - IAS = 90.0; - descending = false; - _aip.setVisible(true); - if(_controlled) { - tower->RegisterAIPlane(plane, this, CIRCUIT, DOWNWIND); - } - Transform(); - } else { - // Default to initial position on threshold for now - _pos.setlat(rwy.threshold_pos.lat()); - _pos.setlon(rwy.threshold_pos.lon()); - _pos.setelev(rwy.threshold_pos.elev()); - _hdg = rwy.hdg; - - // Now we've set the position we can do the ground elev - // This might not always be necessary if we implement in-air start - elevInitGood = false; - inAir = false; - - _pitch = 0.0; - _roll = 0.0; - leg = TAKEOFF_ROLL; - vel = 0.0; - slope = 0.0; - - Transform(); - DoGroundElev(); - } - - operatingState = IN_PATTERN; - - break; - case EN_ROUTE: - // This implies we're being init'd by AIGAVFRTraffic - simple return now - return(true); - default: - SG_LOG(SG_ATC, SG_ALERT, "Attempt to set unknown operating state in FGAILocalTraffic.Init(...)\n"); - return(false); - } - - - return(true); -} - - -// Set up downwind state - this is designed to be called from derived classes who are already tuned to tower -void FGAILocalTraffic::DownwindEntry() { - circuitsToFly = 0; // ie just fly this circuit and then stop - touchAndGo = false; - operatingState = IN_PATTERN; - leg = DOWNWIND; - elevInitGood = false; - inAir = true; - SetTrack(rwy.hdg - (180 * patternDirection)); - slope = 0.0; - _pitch = 0.0; - _roll = 0.0; - IAS = 90.0; - descending = false; -} - -void FGAILocalTraffic::StraightInEntry(bool des) { - //cout << "************ STRAIGHT-IN ********************\n"; - circuitsToFly = 0; // ie just fly this circuit and then stop - touchAndGo = false; - operatingState = IN_PATTERN; - leg = FINAL; - elevInitGood = false; - inAir = true; - SetTrack(rwy.hdg); - transmitted = true; // TODO - fix this hack. - // TODO - set up the next 5 properly for a descent! - slope = -5.5; - _pitch = 0.0; - _roll = 0.0; - IAS = 90.0; - descending = des; -} - - -// Return what type of landing we're doing on this circuit -LandingType FGAILocalTraffic::GetLandingOption() { - //cout << "circuitsToFly = " << circuitsToFly << '\n'; - if(circuitsToFly) { - return(touchAndGo ? TOUCH_AND_GO : STOP_AND_GO); - } else { - return(FULL_STOP); - } -} - - -// Commands to do something from higher level logic -void FGAILocalTraffic::FlyCircuits(int numCircuits, bool tag) { - //cout << "FlyCircuits called" << endl; - - switch(operatingState) { - case IN_PATTERN: - circuitsToFly += numCircuits; - return; - break; - case TAXIING: - // HACK - assume that we're taxiing out for now - circuitsToFly += numCircuits; - touchAndGo = tag; - break; - case PARKED: - circuitsToFly = numCircuits; // Note that one too many circuits gets flown because we only test and decrement circuitsToFly after landing - // thus flying one too many circuits. TODO - Need to sort this out better! - touchAndGo = tag; - break; - case EN_ROUTE: - break; - } -} - -// Run the internal calculations -void FGAILocalTraffic::Update(double dt) { - //cout << "U" << flush; - - // we shouldn't really need this since there's a LOD of 10K on the whole plane anyway I think. - // At the moment though I need to to avoid DList overflows - the whole plane LOD obviously isn't getting picked up. - if(!_invisible) { - if(dclGetHorizontalSeparation(_pos, Point3D(fgGetDouble("/position/longitude-deg"), fgGetDouble("/position/latitude-deg"), 0.0)) > 8000) _aip.setVisible(false); - else _aip.setVisible(true); - } else { - _aip.setVisible(false); - } - - //double responseTime = 10.0; // seconds - this should get more sophisticated at some point - responseCounter += dt; - if((contactTower) && (responseCounter >= 8.0)) { - // Acknowledge request before changing frequency so it gets rendered if the user is on the same freq - string trns = "Tower "; - double f = globals->get_ATC_mgr()->GetFrequency(airportID, TOWER) / 100.0; - char buf[10]; - sprintf(buf, "%.2f", f); - trns += buf; - trns += " "; - trns += plane.callsign; - pending_transmission = trns; - ConditionalTransmit(30.0); - responseCounter = 0.0; - contactTower = false; - changeFreq = true; - changeFreqType = TOWER; - } - - if((contactGround) && (responseCounter >= 8.0)) { - // Acknowledge request before changing frequency so it gets rendered if the user is on the same freq - string trns = "Ground "; - double f = globals->get_ATC_mgr()->GetFrequency(airportID, GROUND) / 100.0; - char buf[10]; - sprintf(buf, "%.2f", f); - trns += buf; - trns += " "; - trns += "Good Day"; - pending_transmission = trns; - ConditionalTransmit(5.0); - responseCounter = 0.0; - contactGround = false; - changeFreq = true; - changeFreqType = GROUND; - } - - if((_taxiToGA) && (responseCounter >= 8.0)) { - // Acknowledge request before changing frequency so it gets rendered if the user is on the same freq - string trns = "GA Parking, Thank you and Good Day"; - //double f = globals->get_ATC_mgr()->GetFrequency(airportID, GROUND) / 100.0; - pending_transmission = trns; - ConditionalTransmit(5.0, 99); - _taxiToGA = false; - if(_controlled) { - tower->DeregisterAIPlane(plane.callsign); - } - // NOTE - we can't delete this instance yet since then the frequency won't get release when the message display finishes. - } - - if((_removeSelf) && (responseCounter >= 8.0)) { - _removeSelf = false; - // MEGA HACK - check if we are at a simple airport or not first instead of simply hardwiring KEMT as the only non-simple airport. - // TODO FIXME TODO FIXME !!!!!!! - if(airportID != "KEMT") globals->get_AI_mgr()->ScheduleRemoval(plane.callsign); - } - - if((changeFreq) && (responseCounter > 8.0)) { - switch(changeFreqType) { - case TOWER: - if(!tower) { - SG_LOG(SG_ATC, SG_ALERT, "ERROR: Trying to change frequency to tower in FGAILocalTraffic, but tower is NULL!!!"); - break; - } - tuned_station = tower; - freq = (double)tower->get_freq() / 100.0; - //Transmit("DING!"); - // Contact the tower, even if only virtually - pending_transmission = plane.callsign; - pending_transmission += " at hold short for runway "; - pending_transmission += ConvertRwyNumToSpokenString(rwy.rwyID); - pending_transmission += " traffic pattern "; - if(circuitsToFly) { - pending_transmission += ConvertNumToSpokenDigits(circuitsToFly + 1); - pending_transmission += " circuits touch and go"; - } else { - pending_transmission += " one circuit to full stop"; - } - Transmit(2); - break; - case GROUND: - if(!tower) { - SG_LOG(SG_ATC, SG_ALERT, "ERROR: Trying to change frequency to ground in FGAILocalTraffic, but tower is NULL!!!"); - break; - } - if(!ground) { - SG_LOG(SG_ATC, SG_ALERT, "ERROR: Trying to change frequency to ground in FGAILocalTraffic, but ground is NULL!!!"); - break; - } - tower->DeregisterAIPlane(plane.callsign); - tuned_station = ground; - freq = (double)ground->get_freq() / 100.0; - break; - // And to avoid compiler warnings... - case APPROACH: break; - case ATIS: break; - case ENROUTE: break; - case DEPARTURE: break; - case INVALID: break; - } - changeFreq = false; - } - - //cout << "," << flush; - - switch(operatingState) { - case IN_PATTERN: - //cout << "In IN_PATTERN\n"; - if(!inAir) { - DoGroundElev(); - if(!elevInitGood) { - if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) { - _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset); - //cout << "TAKEOFF_ROLL, POS = " << pos.lon() << ", " << pos.lat() << ", " << pos.elev() << '\n'; - //Transform(); - _aip.setVisible(true); - //cout << "Making plane visible!\n"; - elevInitGood = true; - } - } - } - FlyTrafficPattern(dt); - Transform(); - break; - case TAXIING: - //cout << "In TAXIING\n"; - //cout << "*" << flush; - if(!elevInitGood) { - //DoGroundElev(); - if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) { - _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset); - //Transform(); - _aip.setVisible(true); - //Transform(); - //cout << "Making plane visible!\n"; - elevInitGood = true; - } - } - DoGroundElev(); - //cout << "~" << flush; - if(!((holdingShort) && (!clearedToLineUp))) { - //cout << "|" << flush; - Taxi(dt); - } - //cout << ";" << flush; - if((clearedToTakeOff) && (responseCounter >= 8.0)) { - // possible assumption that we're at the hold short here - may not always hold - // TODO - sort out the case where we're cleared to line-up first and then cleared to take-off on the rwy. - taxiState = TD_LINING_UP; - //cout << "A" << endl; - path = ground->GetPath(holdShortNode, rwy.rwyID); - //cout << "B" << endl; - if(!path.size()) { // Assume no facility file so we'll just taxi to a point on the runway near the threshold - //cout << "C" << endl; - node* np = new node; - np->struct_type = NODE; - np->pos = ortho.ConvertFromLocal(Point3D(0.0, 10.0, 0.0)); - path.push_back(np); - } else { - //cout << "D" << endl; - } - /* - cout << "path returned was:" << endl; - for(unsigned int i=0; istruct_type) { - case NODE: - cout << "NODE " << ((node*)(path[i]))->nodeID << endl; - break; - case ARC: - cout << "ARC\n"; - break; - } - } - */ - clearedToTakeOff = false; // We *are* still cleared - this simply stops the response recurring!! - holdingShort = false; - string trns = "Cleared for take-off "; - trns += plane.callsign; - pending_transmission = trns; - Transmit(); - StartTaxi(); - } - //cout << "^" << flush; - Transform(); - break; - case PARKED: - //cout << "In PARKED\n"; - if(!elevInitGood) { - DoGroundElev(); - if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) { - _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset); - //Transform(); - _aip.setVisible(true); - //Transform(); - //cout << "Making plane visible!\n"; - elevInitGood = true; - } - } - - if(circuitsToFly) { - if((taxiRequestPending) && (taxiRequestCleared)) { - //cout << "&" << flush; - // Get the active runway details (in case they've changed since init) - GetRwyDetails(airportID); - - // Get the takeoff node for the active runway, get a path to it and start taxiing - path = ground->GetPathToHoldShort(ourGate, rwy.rwyID); - if(path.size() < 2) { - // something has gone wrong - SG_LOG(SG_ATC, SG_ALERT, "Invalid path from gate to theshold in FGAILocalTraffic::FlyCircuits\n"); - return; - } - /* - cout << "path returned was:\n"; - for(unsigned int i=0; istruct_type) { - case NODE: - cout << "NODE " << ((node*)(path[i]))->nodeID << endl; - break; - case ARC: - cout << "ARC\n"; - break; - } - } - */ - path.erase(path.begin()); // pop the gate - we're here already! - taxiState = TD_OUTBOUND; - taxiRequestPending = false; - holdShortNode = (node*)(*(path.begin() + path.size())); - StartTaxi(); - } else if(!taxiRequestPending) { - //cout << "(" << flush; - // Do some communication - // airport name + tower + airplane callsign + location + request taxi for + operation type + ? - string trns = ""; - if(_controlled) { - trns += tower->get_name(); - trns += " tower "; - } else { - trns += "Traffic "; - // TODO - get the airport name somehow if uncontrolled - } - trns += plane.callsign; - trns += " on apron parking request taxi for traffic pattern"; - //cout << "trns = " << trns << endl; - pending_transmission = trns; - Transmit(1); - taxiRequestCleared = false; - taxiRequestPending = true; - } - } - - //cout << "!" << flush; - - // Maybe the below should be set when we get to the threshold and prepare for TO? - // FIXME TODO - pattern direction is still hardwired - patternDirection = -1; // Left - // At the bare minimum we ought to make sure it goes the right way at dual parallel rwy airports! - if(rwy.rwyID.size() == 3) { - patternDirection = (rwy.rwyID.substr(2,1) == "R" ? 1 : -1); - } - // Do nothing - Transform(); - //cout << ")" << flush; - break; - default: - break; - } - //cout << "I " << flush; - - //cout << "Update _pos = " << _pos << ", vis = " << _aip.getVisible() << '\n'; - - // Convienience output for AI debugging using the property logger - //fgSetDouble("/AI/Local1/ortho-x", (ortho.ConvertToLocal(_pos)).x()); - //fgSetDouble("/AI/Local1/ortho-y", (ortho.ConvertToLocal(_pos)).y()); - //fgSetDouble("/AI/Local1/elev", _pos.elev() * SG_METER_TO_FEET); - - // And finally, call parent. - FGAIPlane::Update(dt); -} - -void FGAILocalTraffic::RegisterTransmission(int code) { - switch(code) { - case 1: // taxi request cleared - taxiRequestCleared = true; - SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " cleared to taxi..."); - break; - case 2: // contact tower - responseCounter = 0; - contactTower = true; - SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " told to contact tower..."); - break; - case 3: // Cleared to line up - responseCounter = 0; - clearedToLineUp = true; - SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " cleared to line-up..."); - break; - case 4: // cleared to take-off - responseCounter = 0; - clearedToTakeOff = true; - SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " cleared to take-off..."); - break; - case 5: // contact ground - responseCounter = 0; - contactGround = true; - SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " told to contact ground..."); - break; - // case 6 is a temporary mega-hack for controlled airports without separate ground control - case 6: // taxi to the GA parking - responseCounter = 0; - _taxiToGA = true; - SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " told to taxi to the GA parking..."); - break; - case 7: // Cleared to land (also implies cleared for the option - _clearedToLand = true; - SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " cleared to land..."); - break; - case 13: // Go around! - responseCounter = 0; - goAround = true; - _clearedToLand = false; - SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " told to go-around!!"); - break; - default: - break; - } -} - -// Fly a traffic pattern -// FIXME - far too much of the mechanics of turning, rolling, accellerating, descending etc is in here. -// Move it out to FGAIPlane and have FlyTrafficPattern just specify what to do, not the implementation. -void FGAILocalTraffic::FlyTrafficPattern(double dt) { - // Need to differentiate between in-air (IAS governed) and on-ground (vel governed) - // Take-off is an interesting case - we are on the ground but takeoff speed is IAS governed. - - // WIND - // Wind has two effects - a mechanical one in that IAS translates to a different vel, and the hdg != track, - // but also a piloting effect, in that the AI must be able to descend at a different rate in order to hit the threshold. - - //cout << "dt = " << dt << '\n'; - double dist = 0; - // ack - I can't remember how long a rate 1 turn is meant to take. - double turn_time = 60.0; // seconds - TODO - check this guess - double turn_circumference; - double turn_radius; - Point3D orthopos = ortho.ConvertToLocal(_pos); // ortho position of the plane - //cout << "runway elev = " << rwy.threshold_pos.elev() << ' ' << rwy.threshold_pos.elev() * SG_METER_TO_FEET << '\n'; - //cout << "elev = " << _pos.elev() << ' ' << _pos.elev() * SG_METER_TO_FEET << '\n'; - - // HACK FOR TESTING - REMOVE - //cout << "Calling ExitRunway..." << endl; - //ExitRunway(orthopos); - //return; - // END HACK - - //wind - double wind_from = wind_from_hdg->getDoubleValue(); - double wind_speed = wind_speed_knots->getDoubleValue(); - - double dveldt; - - switch(leg) { - case TAKEOFF_ROLL: - //inAir = false; - track = rwy.hdg; - if(vel < 80.0) { - double dveldt = 5.0; - vel += dveldt * dt; - } - if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) { - _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset); - } - IAS = vel + (cos((_hdg - wind_from) * DCL_DEGREES_TO_RADIANS) * wind_speed); - if(IAS >= 70) { - leg = CLIMBOUT; - SetTrack(rwy.hdg); // Hands over control of turning to AIPlane - _pitch = 10.0; - IAS = best_rate_of_climb_speed; - //slope = 7.0; - slope = 6.0; // Reduced it slightly since it's climbing a lot steeper than I can in the JSBSim C172. - inAir = true; - } - break; - case CLIMBOUT: - // Turn to crosswind if above 700ft AND if other traffic allows - // (decided in FGTower and accessed through GetCrosswindConstraint(...)). - // According to AIM, traffic should climb to within 300ft of pattern altitude before commencing crosswind turn. - // TODO - At hot 'n high airports this may be 500ft AGL though - need to make this a variable. - if((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 700) { - double cc = 0.0; - if(tower->GetCrosswindConstraint(cc)) { - if(orthopos.y() > cc) { - //cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n'; - leg = TURN1; - } - } else if(orthopos.y() > 1500.0) { // Added this constraint as a hack to prevent turning too early when going around. - // TODO - We should be doing it as a distance from takeoff end, not theshold end though. - //cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n'; - leg = TURN1; - } - } - // Need to check for levelling off in case we can't turn crosswind as soon - // as we would like due to other traffic. - if((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) { - slope = 0.0; - _pitch = 0.0; - IAS = 80.0; // FIXME - use smooth transistion to new speed and attitude. - } - if(goAround && !goAroundCalled) { - if(responseCounter > 5.5) { - pending_transmission = plane.callsign; - pending_transmission += " going around"; - Transmit(); - goAroundCalled = true; - } - } - break; - case TURN1: - SetTrack(rwy.hdg + (90.0 * patternDirection)); - if((track < (rwy.hdg - 89.0)) || (track > (rwy.hdg + 89.0))) { - leg = CROSSWIND; - } - break; - case CROSSWIND: - goAround = false; - if((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) { - slope = 0.0; - _pitch = 0.0; - IAS = 80.0; // FIXME - use smooth transistion to new speed - } - // turn 1000m out for now, taking other traffic into accout - if(fabs(orthopos.x()) > 900) { - double dd = 0.0; - if(tower->GetDownwindConstraint(dd)) { - if(fabs(orthopos.x()) > fabs(dd)) { - //cout << "Turning to downwind, distance from centerline = " << fabs(orthopos.x()) << '\n'; - leg = TURN2; - } - } else { - //cout << "Turning to downwind, distance from centerline = " << fabs(orthopos.x()) << '\n'; - leg = TURN2; - } - } - break; - case TURN2: - SetTrack(rwy.hdg - (180 * patternDirection)); - // just in case we didn't make height on crosswind - if((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) { - slope = 0.0; - _pitch = 0.0; - IAS = 80.0; // FIXME - use smooth transistion to new speed - } - if((track < (rwy.hdg - 179.0)) || (track > (rwy.hdg + 179.0))) { - leg = DOWNWIND; - transmitted = false; - } - break; - case DOWNWIND: - // just in case we didn't make height on crosswind - if(((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 995) && ((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET < 1015)) { - slope = 0.0; - _pitch = 0.0; - IAS = 90.0; // FIXME - use smooth transistion to new speed - } - if((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET >= 1015) { - slope = -1.0; - _pitch = -1.0; - IAS = 90.0; // FIXME - use smooth transistion to new speed - } - if((orthopos.y() < 0) && (!transmitted)) { - TransmitPatternPositionReport(); - transmitted = true; - } - if((orthopos.y() < -100) && (!descending)) { - //cout << "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDdddd\n"; - // Maybe we should think about when to start descending. - // For now we're assuming that we aim to follow the same glidepath regardless of wind. - double d1; - double d2; - CalculateSoD((tower->GetBaseConstraint(d1) ? d1 : -1000.0), (tower->GetDownwindConstraint(d2) ? d2 : 1000.0 * patternDirection), (patternDirection ? true : false)); - if(SoD.leg == DOWNWIND) { - descending = (orthopos.y() < SoD.y ? true : false); - } - - } - if(descending) { - slope = -5.5; // FIXME - calculate to descent at 500fpm and hit the desired point on the runway (taking wind into account as well!!) - _pitch = -3.0; - IAS = 85.0; - } - - // Try and arrange to turn nicely onto base - turn_circumference = IAS * 0.514444 * turn_time; - //Hmmm - this is an interesting one - ground vs airspeed in relation to turn radius - //We'll leave it as a hack with IAS for now but it needs revisiting. - turn_radius = turn_circumference / (2.0 * DCL_PI); - if(orthopos.y() < -1000.0 + turn_radius) { - //if(orthopos.y() < -980) { - double bb = 0.0; - if(tower->GetBaseConstraint(bb)) { - if(fabs(orthopos.y()) > fabs(bb)) { - //cout << "Turning to base, distance from threshold = " << fabs(orthopos.y()) << '\n'; - leg = TURN3; - transmitted = false; - IAS = 80.0; - } - } else { - //cout << "Turning to base, distance from threshold = " << fabs(orthopos.y()) << '\n'; - leg = TURN3; - transmitted = false; - IAS = 80.0; - } - } - break; - case TURN3: - SetTrack(rwy.hdg - (90 * patternDirection)); - if(fabs(rwy.hdg - track) < 91.0) { - leg = BASE; - } - break; - case BASE: - if(!transmitted) { - // Base report should only be transmitted at uncontrolled airport - not towered. - if(!_controlled) TransmitPatternPositionReport(); - transmitted = true; - } - - if(!descending) { - double d1; - // Make downwind leg position artifically large to avoid any chance of SoD being returned as - // on downwind when we are already on base. - CalculateSoD((tower->GetBaseConstraint(d1) ? d1 : -1000.0), (10000.0 * patternDirection), (patternDirection ? true : false)); - if(SoD.leg == BASE) { - descending = (fabs(orthopos.y()) < fabs(SoD.y) ? true : false); - } - - } - if(descending) { - slope = -5.5; // FIXME - calculate to descent at 500fpm and hit the threshold (taking wind into account as well!!) - _pitch = -4.0; - IAS = 70.0; - } - - // Try and arrange to turn nicely onto final - turn_circumference = IAS * 0.514444 * turn_time; - //Hmmm - this is an interesting one - ground vs airspeed in relation to turn radius - //We'll leave it as a hack with IAS for now but it needs revisiting. - turn_radius = turn_circumference / (2.0 * DCL_PI); - if(fabs(orthopos.x()) < (turn_radius + 50)) { - leg = TURN4; - transmitted = false; - //roll = -20; - } - break; - case TURN4: - SetTrack(rwy.hdg); - if(fabs(track - rwy.hdg) < 0.6) { - leg = FINAL; - vel = nominal_final_speed; - } - break; - case FINAL: - if(goAround && responseCounter > 2.0) { - leg = CLIMBOUT; - _pitch = 8.0; - IAS = best_rate_of_climb_speed; - slope = 5.0; // A bit less steep than the initial climbout. - inAir = true; - goAroundCalled = false; - descending = false; - break; - } - LevelWings(); - if(!transmitted) { - if((!_controlled) || (!_clearedToLand)) TransmitPatternPositionReport(); - transmitted = true; - } - if(!descending) { - // Make base leg position artifically large to avoid any chance of SoD being returned as - // on base or downwind when we are already on final. - CalculateSoD(-10000.0, (1000.0 * patternDirection), (patternDirection ? true : false)); - if(SoD.leg == FINAL) { - descending = (fabs(orthopos.y()) < fabs(SoD.y) ? true : false); - } - - } - if(descending) { - if(orthopos.y() < -50.0) { - double thesh_offset = 30.0; - slope = atan((_pos.elev() - fgGetAirportElev(airportID)) / (orthopos.y() - thesh_offset)) * DCL_RADIANS_TO_DEGREES; - //cout << "slope = " << slope << ", elev = " << _pos.elev() << ", apt_elev = " << fgGetAirportElev(airportID) << ", op.y = " << orthopos.y() << '\n'; - if(slope < -10.0) slope = -10.0; - _savedSlope = slope; - _pitch = -4.0; - IAS = 70.0; - } else { - if(_pos.elev() < (rwy.threshold_pos.elev()+10.0+wheelOffset)) { - if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) { - if(_pos.elev() < (_aip.getSGLocation()->get_cur_elev_m() + wheelOffset + 1.0)) { - slope = -2.0; - _pitch = 1.0; - IAS = 55.0; - } else if(_pos.elev() < (_aip.getSGLocation()->get_cur_elev_m() + wheelOffset + 5.0)) { - slope = -4.0; - _pitch = -2.0; - IAS = 60.0; - } else { - slope = _savedSlope; - _pitch = -3.0; - IAS = 65.0; - } - } else { - // Elev not determined - slope = _savedSlope; - _pitch = -3.0; - IAS = 65.0; - } - } else { - slope = _savedSlope; - _pitch = -3.0; - IAS = 65.0; - } - } - } - // Try and track the extended centreline - SetTrack(rwy.hdg - (0.2 * orthopos.x())); - //cout << "orthopos.x() = " << orthopos.x() << " hdg = " << hdg << '\n'; - if(_pos.elev() < (rwy.threshold_pos.elev()+20.0+wheelOffset)) { - DoGroundElev(); // Need to call it here expicitly on final since it's only called - // for us in update(...) when the inAir flag is false. - } - if(_pos.elev() < (rwy.threshold_pos.elev()+10.0+wheelOffset)) { - //slope = -1.0; - //_pitch = 1.0; - if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) { - if((_aip.getSGLocation()->get_cur_elev_m() + wheelOffset) > _pos.elev()) { - slope = 0.0; - _pitch = 0.0; - leg = LANDING_ROLL; - inAir = false; - LevelWings(); - ClearTrack(); // Take over explicit track handling since AIPlane currently always banks when changing course - } - } // else need a fallback position based on arpt elev in case ground elev determination fails? - } else { - // TODO - } - break; - case LANDING_ROLL: - //inAir = false; - descending = false; - if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) { - _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset); - } - track = rwy.hdg; - dveldt = -5.0; - vel += dveldt * dt; - // FIXME - differentiate between touch and go and full stops - if(vel <= 15.0) { - //cout << "Vel <= 15.0, circuitsToFly = " << circuitsToFly << endl; - if(circuitsToFly <= 0) { - //cout << "Calling ExitRunway..." << endl; - ExitRunway(orthopos); - return; - } else { - //cout << "Taking off again..." << endl; - leg = TAKEOFF_ROLL; - --circuitsToFly; - } - } - break; - case LEG_UNKNOWN: - break; - } - - if(inAir) { - // FIXME - at the moment this is a bit screwy - // The velocity correction is applied based on the relative headings. - // Then the heading is changed based on the velocity. - // Which comes first, the chicken or the egg? - // Does it really matter? - - // Apply wind to ground-relative velocity if in the air - vel = IAS - (cos((_hdg - wind_from) * DCL_DEGREES_TO_RADIANS) * wind_speed); - //crab = f(track, wind, vel); - // The vector we need to fly is our desired vector minus the wind vector - // TODO - we probably ought to use plib's built in vector types and operations for this - // ie. There's almost *certainly* a better way to do this! - double gxx = vel * sin(track * DCL_DEGREES_TO_RADIANS); // Plane desired velocity x component wrt ground - double gyy = vel * cos(track * DCL_DEGREES_TO_RADIANS); // Plane desired velocity y component wrt ground - double wxx = wind_speed * sin((wind_from + 180.0) * DCL_DEGREES_TO_RADIANS); // Wind velocity x component - double wyy = wind_speed * cos((wind_from + 180.0) * DCL_DEGREES_TO_RADIANS); // Wind velocity y component - double axx = gxx - wxx; // Plane in-air velocity x component - double ayy = gyy - wyy; // Plane in-air velocity y component - // Now we want the angle between gxx and axx (which is the crab) - double maga = sqrt(axx*axx + ayy*ayy); - double magg = sqrt(gxx*gxx + gyy*gyy); - crab = acos((axx*gxx + ayy*gyy) / (maga * magg)); - // At this point this works except we're getting the modulus of the angle - //cout << "crab = " << crab << '\n'; - - // Make sure both headings are in the 0->360 circle in order to get sane differences - dclBoundHeading(wind_from); - dclBoundHeading(track); - if(track > wind_from) { - if((track - wind_from) <= 180) { - crab *= -1.0; - } - } else { - if((wind_from - track) >= 180) { - crab *= -1.0; - } - } - } else { // on the ground - crab dosen't apply - crab = 0.0; - } - - //cout << "X " << orthopos.x() << " Y " << orthopos.y() << " SLOPE " << slope << " elev " << _pos.elev() * SG_METER_TO_FEET << '\n'; - - _hdg = track + crab; - dist = vel * 0.514444 * dt; - _pos = dclUpdatePosition(_pos, track, slope, dist); -} - -// Pattern direction is true for right, false for left -void FGAILocalTraffic::CalculateSoD(double base_leg_pos, double downwind_leg_pos, bool pattern_direction) { - // For now we'll ignore wind and hardwire the glide angle. - double ga = 5.5; //degrees - double pa = 1000.0 * SG_FEET_TO_METER; // pattern altitude in meters - // FIXME - get glideslope angle and pattern altitude agl from airport details if available - - // For convienience, we'll have +ve versions of the input distances - double blp = fabs(base_leg_pos); - double dlp = fabs(downwind_leg_pos); - - //double turn_allowance = 150.0; // Approximate distance in meters that a 90deg corner is shortened by turned in a light plane. - - double stod = pa / tan(ga * DCL_DEGREES_TO_RADIANS); // distance in meters from touchdown point to start descent - //cout << "Descent to start = " << stod << " meters out\n"; - if(stod < blp) { // Start descending on final - SoD.leg = FINAL; - SoD.y = stod * -1.0; - SoD.x = 0.0; - } else if(stod < (blp + dlp)) { // Start descending on base leg - SoD.leg = BASE; - SoD.y = blp * -1.0; - SoD.x = (pattern_direction ? (stod - dlp) : (stod - dlp) * -1.0); - } else { // Start descending on downwind leg - SoD.leg = DOWNWIND; - SoD.x = (pattern_direction ? dlp : dlp * -1.0); - SoD.y = (blp - (stod - (blp + dlp))) * -1.0; - } -} - -void FGAILocalTraffic::TransmitPatternPositionReport(void) { - // airport name + "traffic" + airplane callsign + pattern direction + pattern leg + rwy + ? - string trns = ""; - int code = 0; - - trns += tower->get_name(); - trns += " Traffic "; - trns += plane.callsign; - if(patternDirection == 1) { - trns += " right "; - } else { - trns += " left "; - } - - // We could probably get rid of this whole switch statement and just pass a string containing the leg from the FlyPattern function. - switch(leg) { // We'll assume that transmissions in turns are intended for next leg - do pilots ever call out that they are in the turn? - case TURN1: - // Fall through to CROSSWIND - case CROSSWIND: // I don't think this case will be used here but it can't hurt to leave it in - trns += "crosswind "; - break; - case TURN2: - // Fall through to DOWNWIND - case DOWNWIND: - trns += "downwind "; - code = 11; - break; - case TURN3: - // Fall through to BASE - case BASE: - trns += "base "; - break; - case TURN4: - // Fall through to FINAL - case FINAL: // maybe this should include long/short final if appropriate? - trns += "final "; - code = 13; - break; - default: // Hopefully this won't be used - trns += "pattern "; - break; - } - trns += ConvertRwyNumToSpokenString(rwy.rwyID); - - trns += " "; - - // And add the airport name again - trns += tower->get_name(); - - pending_transmission = trns; - ConditionalTransmit(60.0, code); // Assume a report of this leg will be invalid if we can't transmit within a minute. -} - -// Callback handler -// TODO - Really should enumerate these coded values. -void FGAILocalTraffic::ProcessCallback(int code) { - // 1 - Request Departure from ground - // 2 - Report at hold short - // 3 - Report runway vacated - // 10 - report crosswind - // 11 - report downwind - // 12 - report base - // 13 - report final - if(code == 1) { - ground->RequestDeparture(plane, this); - } else if(code == 2) { - tower->ContactAtHoldShort(plane, this, CIRCUIT); - } else if(code == 3) { - tower->ReportRunwayVacated(plane.callsign); - } else if(code == 11) { - tower->ReportDownwind(plane.callsign); - } else if(code == 13) { - tower->ReportFinal(plane.callsign); - } else if(code == 99) { // Flag this instance for deletion - responseCounter = 0; - _removeSelf = true; - SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " delete instance callback called."); - } -} - -void FGAILocalTraffic::ExitRunway(const Point3D& orthopos) { - //cout << "In ExitRunway" << endl; - //cout << "Runway ID is " << rwy.ID << endl; - - _clearedToLand = false; - - node_array_type exitNodes = ground->GetExits(rwy.rwyID); //I suppose we ought to have some fallback for rwy with no defined exits? - /* - cout << "Node ID's of exits are "; - for(unsigned int i=0; inodeID << ' '; - } - cout << endl; - */ - if(exitNodes.size()) { - //Find the next exit from orthopos.y - double d; - double dist = 100000; //ie. longer than any runway in existance - double backdist = 100000; - node_array_iterator nItr = exitNodes.begin(); - node* rwyExit = *(exitNodes.begin()); - //int gateID; //This might want to be more persistant at some point - while(nItr != exitNodes.end()) { - d = ortho.ConvertToLocal((*nItr)->pos).y() - ortho.ConvertToLocal(_pos).y(); //FIXME - consider making orthopos a class variable - if(d > 0.0) { - if(d < dist) { - dist = d; - rwyExit = *nItr; - } - } else { - if(fabs(d) < backdist) { - backdist = d; - //TODO - need some logic here that if we don't get a forward exit we turn round and store the backwards one - } - } - ++nItr; - } - ourGate = ground->GetGateNode(); - if(ourGate == NULL) { - // Implies no available gates - what shall we do? - // For now just vanish the plane - possibly we can make this more elegant in the future - SG_LOG(SG_ATC, SG_ALERT, "No gate found by FGAILocalTraffic whilst landing at " << airportID << '\n'); - //_aip.setVisible(false); - //cout << "Setting visible false\n"; - operatingState = PARKED; - return; - } - path = ground->GetPath(rwyExit, ourGate); - /* - cout << "path returned was:" << endl; - for(unsigned int i=0; istruct_type) { - case NODE: - cout << "NODE " << ((node*)(path[i]))->nodeID << endl; - break; - case ARC: - cout << "ARC\n"; - break; - } - } - */ - taxiState = TD_INBOUND; - StartTaxi(); - } else { - // Something must have gone wrong with the ground network file - or there is only a rwy here and no exits defined - SG_LOG(SG_ATC, SG_INFO, "No exits found by FGAILocalTraffic from runway " << rwy.rwyID << " at " << airportID << '\n'); - //if(airportID == "KRHV") cout << "No exits found by " << plane.callsign << " from runway " << rwy.rwyID << " at " << airportID << '\n'; - // What shall we do - just remove the plane from sight? - _aip.setVisible(false); - _invisible = true; - //cout << "Setting visible false\n"; - //tower->ReportRunwayVacated(plane.callsign); - string trns = "Clear of the runway "; - trns += plane.callsign; - pending_transmission = trns; - Transmit(3); - operatingState = PARKED; - } -} - -// Set the class variable nextTaxiNode to the next node in the path -// and update taxiPathPos, the class variable path iterator position -// TODO - maybe should return error codes to the calling function if we fail here -void FGAILocalTraffic::GetNextTaxiNode() { - //cout << "GetNextTaxiNode called " << endl; - //cout << "taxiPathPos = " << taxiPathPos << endl; - ground_network_path_iterator pathItr = path.begin() + taxiPathPos; - if(pathItr == path.end()) { - SG_LOG(SG_ATC, SG_ALERT, "ERROR IN AILocalTraffic::GetNextTaxiNode - no more nodes in path\n"); - } else { - if((*pathItr)->struct_type == NODE) { - //cout << "ITS A NODE" << endl; - //*pathItr = new node; - nextTaxiNode = (node*)*pathItr; - ++taxiPathPos; - //delete pathItr; - } else { - //cout << "ITS NOT A NODE" << endl; - //The first item in found must have been an arc - //Assume for now that it was straight - pathItr++; - taxiPathPos++; - if(pathItr == path.end()) { - SG_LOG(SG_ATC, SG_ALERT, "ERROR IN AILocalTraffic::GetNextTaxiNode - path ended with an arc\n"); - } else if((*pathItr)->struct_type == NODE) { - nextTaxiNode = (node*)*pathItr; - ++taxiPathPos; - } else { - //OOPS - two non-nodes in a row - that shouldn't happen ATM - SG_LOG(SG_ATC, SG_ALERT, "ERROR IN AILocalTraffic::GetNextTaxiNode - two non-nodes in sequence\n"); - } - } - } -} - -// StartTaxi - set up the taxiing state - call only at the start of taxiing -void FGAILocalTraffic::StartTaxi() { - //cout << "StartTaxi called" << endl; - operatingState = TAXIING; - - taxiPathPos = 0; - - //Set the desired heading - //Assume we are aiming for first node on path - //Eventually we may need to consider the fact that we might start on a curved arc and - //not be able to head directly for the first node. - GetNextTaxiNode(); // sets the class variable nextTaxiNode to the next taxi node! - desiredTaxiHeading = GetHeadingFromTo(_pos, nextTaxiNode->pos); - //cout << "First taxi heading is " << desiredTaxiHeading << endl; -} - -// speed in knots, headings in degrees, radius in meters. -static double TaxiTurnTowardsHeading(double current_hdg, double desired_hdg, double speed, double radius, double dt) { - // wrap heading - this prevents a logic bug where the plane would just go round in circles!! - while(current_hdg < 0.0) { - current_hdg += 360.0; - } - while(current_hdg > 360.0) { - current_hdg -= 360.0; - } - if(fabs(current_hdg - desired_hdg) > 0.1) { - // Which is the quickest direction to turn onto heading? - if(desired_hdg > current_hdg) { - if((desired_hdg - current_hdg) <= 180) { - // turn right - current_hdg += ((speed * 0.514444 * dt) / (radius * DCL_PI)) * 180.0; - // TODO - check that increments are less than the delta that we check for the right direction - // Probably need to reduce convergence speed as convergence is reached - } else { - current_hdg -= ((speed * 0.514444 * dt) / (radius * DCL_PI)) * 180.0; - } - } else { - if((current_hdg - desired_hdg) <= 180) { - // turn left - current_hdg -= ((speed * 0.514444 * dt) / (radius * DCL_PI)) * 180.0; - // TODO - check that increments are less than the delta that we check for the right direction - // Probably need to reduce convergence speed as convergence is reached - } else { - current_hdg += ((speed * 0.514444 * dt) / (radius * DCL_PI)) * 180.0; - } - } - } - return(current_hdg); -} - -void FGAILocalTraffic::Taxi(double dt) { - //cout << "Taxi called" << endl; - // Logic - if we are further away from next point than turn radius then head for it - // If we have reached turning point then get next point and turn onto that heading - // Look out for the finish!! - - //Point3D orthopos = ortho.ConvertToLocal(pos); // ortho position of the plane - desiredTaxiHeading = GetHeadingFromTo(_pos, nextTaxiNode->pos); - - bool lastNode = (taxiPathPos == path.size() ? true : false); - if(lastNode) { - //cout << "LAST NODE\n"; - } - - // HACK ALERT! - for now we will taxi at constant speed for straights and turns - - // Remember that hdg is always equal to track when taxiing so we don't have to consider them both - double dist_to_go = dclGetHorizontalSeparation(_pos, nextTaxiNode->pos); // we may be able to do this more cheaply using orthopos - //cout << "dist_to_go = " << dist_to_go << endl; - if((nextTaxiNode->type == GATE) && (dist_to_go <= 0.1)) { - // This might be more robust to outward paths starting with a gate if we check for either - // last node or TD_INBOUND ? - // park up - operatingState = PARKED; - } else if(((dist_to_go > taxiTurnRadius) || (nextTaxiNode->type == GATE)) && (!liningUp)){ - // if the turn radius is r, and speed is s, then in a time dt we turn through - // ((s.dt)/(PI.r)) x 180 degrees - // or alternatively (s.dt)/r radians - //cout << "hdg = " << hdg << " desired taxi heading = " << desiredTaxiHeading << '\n'; - _hdg = TaxiTurnTowardsHeading(_hdg, desiredTaxiHeading, nominalTaxiSpeed, taxiTurnRadius, dt); - double vel = nominalTaxiSpeed; - //cout << "vel = " << vel << endl; - double dist = vel * 0.514444 * dt; - //cout << "dist = " << dist << endl; - double track = _hdg; - //cout << "track = " << track << endl; - double slope = 0.0; - _pos = dclUpdatePosition(_pos, track, slope, dist); - //cout << "Updated position...\n"; - if(_aip.getSGLocation()->get_cur_elev_m() > -9990) { - _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset); - } // else don't change the elev until we get a valid ground elev again! - } else if(lastNode) { - if(taxiState == TD_LINING_UP) { - if((!liningUp) && (dist_to_go <= taxiTurnRadius)) { - liningUp = true; - } - if(liningUp) { - _hdg = TaxiTurnTowardsHeading(_hdg, rwy.hdg, nominalTaxiSpeed, taxiTurnRadius, dt); - double vel = nominalTaxiSpeed; - //cout << "vel = " << vel << endl; - double dist = vel * 0.514444 * dt; - //cout << "dist = " << dist << endl; - double track = _hdg; - //cout << "track = " << track << endl; - double slope = 0.0; - _pos = dclUpdatePosition(_pos, track, slope, dist); - //cout << "Updated position...\n"; - if(_aip.getSGLocation()->get_cur_elev_m() > -9990) { - _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset); - } // else don't change the elev until we get a valid ground elev again! - if(fabs(_hdg - rwy.hdg) <= 1.0) { - operatingState = IN_PATTERN; - leg = TAKEOFF_ROLL; - inAir = false; - liningUp = false; - } - } - } else if(taxiState == TD_OUTBOUND) { - // Pause awaiting further instructions - // and for now assume we've reached the hold-short node - holdingShort = true; - } // else at the moment assume TD_INBOUND always ends in a gate in which case we can ignore it - } else { - // Time to turn (we've already checked it's not the end we're heading for). - // set the target node to be the next node which will prompt automatically turning onto - // the right heading in the stuff above, with the usual provisos applied. - GetNextTaxiNode(); - // For now why not just recursively call this function? - Taxi(dt); - } -} - - -// Warning - ground elev determination is CPU intensive -// Either this function or the logic of how often it is called -// will almost certainly change. -void FGAILocalTraffic::DoGroundElev() { - // It would be nice if we could set the correct tile center here in order to get a correct - // answer with one call to the function, but what I tried in the two commented-out lines - // below only intermittently worked, and I haven't quite groked why yet. - //SGBucket buck(pos.lon(), pos.lat()); - //aip.getSGLocation()->set_tile_center(Point3D(buck.get_center_lon(), buck.get_center_lat(), 0.0)); - - // Only do the proper hitlist stuff if we are within visible range of the viewer. - double visibility_meters = fgGetDouble("/environment/visibility-m"); - FGViewer* vw = globals->get_current_view(); - if(dclGetHorizontalSeparation(_pos, Point3D(vw->getLongitude_deg(), vw->getLatitude_deg(), 0.0)) > visibility_meters) { - _aip.getSGLocation()->set_cur_elev_m(aptElev); - return; - } - - // FIXME: make shure the pos.lat/pos.lon values are in degrees ... - double range = 500.0; - double lat = _aip.getSGLocation()->getLatitude_deg(); - double lon = _aip.getSGLocation()->getLongitude_deg(); - if (!globals->get_tile_mgr()->scenery_available(lat, lon, range)) { - // Try to shedule tiles for that position. - globals->get_tile_mgr()->update( _aip.getSGLocation(), range ); - } - - // FIXME: make shure the pos.lat/pos.lon values are in degrees ... - double alt; - if (globals->get_scenery()->get_elevation_m(lat, lon, 20000.0, alt, 0)) - _aip.getSGLocation()->set_cur_elev_m(alt); -} - diff --git a/src/ATC/AILocalTraffic.hxx b/src/ATC/AILocalTraffic.hxx deleted file mode 100644 index a4e1505f6..000000000 --- a/src/ATC/AILocalTraffic.hxx +++ /dev/null @@ -1,233 +0,0 @@ -// FGAILocalTraffic - AIEntity derived class with enough logic to -// fly and interact with the traffic pattern. -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifndef _FG_AILocalTraffic_HXX -#define _FG_AILocalTraffic_HXX - -#include -#include
- -#include "AIPlane.hxx" -#include "ATCProjection.hxx" -#include "ground.hxx" - -class FGGround; -class FGTower; -struct Gate; - -#include -SG_USING_STD(string); - -enum TaxiState { - TD_INBOUND, - TD_OUTBOUND, - TD_NONE, - TD_LINING_UP -}; - -enum OperatingState { - IN_PATTERN, - TAXIING, - PARKED, - EN_ROUTE -}; - -struct StartOfDescent { - PatternLeg leg; - double x; // Runway aligned orthopos - double y; // ditto -}; - -class FGAILocalTraffic : public FGAIPlane { - -public: - - // At the moment we expect the expanded short form callsign - eventually we will just want the reg + type. - FGAILocalTraffic(); - ~FGAILocalTraffic(); - - // Initialise - bool Init(const string& callsign, const string& ICAO, OperatingState initialState = PARKED, PatternLeg initialLeg = DOWNWIND); - - // Run the internal calculations - void Update(double dt); - - // Go out and practice circuits - void FlyCircuits(int numCircuits, bool tag); - - // Return what type of landing we're doing on this circuit - LandingType GetLandingOption(); - - // TODO - this will get more complex and moved into the main class - // body eventually since the position approved to taxi to will have - // to be passed. - inline void ApproveTaxiRequest() {taxiRequestCleared = true;} - - inline void DenyTaxiRequest() {taxiRequestCleared = false;} - - void RegisterTransmission(int code); - - // Process callbacks sent by base class - // (These codes are not related to the codes above) - void ProcessCallback(int code); - - // This is a hack and will probably go eventually - inline bool AtHoldShort() {return holdingShort;} - -protected: - - // Attempt to enter the traffic pattern in a reasonably intelligent manner - void EnterTrafficPattern(double dt); - - // Set up the internal state to be consistent for a downwind entry. - void DownwindEntry(); - - // Ditto for straight-in - void StraightInEntry(bool des = false); - - // Do what is necessary to land and parkup at home airport - void ReturnToBase(double dt); - - // Airport/runway/pattern details - string airportID; // The ICAO code of the airport that we're operating around - double aptElev; // Airport elevation - FGGround* ground; // A pointer to the ground control. - FGTower* tower; // A pointer to the tower control. - bool _controlled; // Set true if we find tower control working for the airport, false otherwise. - RunwayDetails rwy; - double patternDirection; // 1 for right, -1 for left (This is double because we multiply/divide turn rates - // with it to get RH/LH turns - DON'T convert it to int under ANY circumstances!! - double glideAngle; // Assumed to be visual glidepath angle for FGAILocalTraffic - can be found at www.airnav.com - // Its conceivable that patternDirection and glidePath could be moved into the RunwayDetails structure. - - // Its possible that this might be moved out to the ground/airport class at some point. - FGATCAlignedProjection ortho; // Orthogonal mapping of the local area with the threshold at the origin - // and the runway aligned with the y axis. - - void GetAirportDetails(const string& id); - - void GetRwyDetails(const string& id); - - double responseCounter; // timer in seconds to allow response to requests to be a little while after them - // Will almost certainly get moved to FGAIPlane. - -private: - FGATCMgr* ATC; - // This is purely for synactic convienience to avoid writing globals->get_ATC_mgr()-> all through the code! - - // High-level stuff - OperatingState operatingState; - int circuitsToFly; //Number of circuits still to do in this session NOT INCLUDING THE CURRENT ONE - bool touchAndGo; //True if circuits should be flown touch and go, false for full stop - bool transmitted; // Set true when a position report for the current leg has been transmitted. - - // Performance characteristics of the plane in knots and ft/min - some of this might get moved out into FGAIPlane - double Vr; - double best_rate_of_climb_speed; - double best_rate_of_climb; - double nominal_climb_speed; - double nominal_climb_rate; - double nominal_circuit_speed; - double min_circuit_speed; - double max_circuit_speed; - double nominal_descent_rate; - double nominal_approach_speed; - double nominal_final_speed; - double stall_speed_landing_config; - double nominal_taxi_speed; - - // Physical/rendering stuff - double wheelOffset; // Height above ground at which we need to render the plane whilst taxiing - bool elevInitGood; // We have had at least one good elev reading - bool inAir; // True when off the ground - - // environment - some of this might get moved into FGAIPlane - SGPropertyNode_ptr wind_from_hdg; //degrees - SGPropertyNode_ptr wind_speed_knots; //knots - - // Pattern details that (may) change - int numInPattern; // Number of planes in the pattern (this might get more complicated if high performance GA aircraft fly a higher pattern eventually) - int numAhead; // More importantly - how many of them are ahead of us? - double distToNext; // And even more importantly, how near are we getting to the one immediately ahead? - //PatternLeg leg; // Our current position in the pattern - now moved to FGAIPlane - StartOfDescent SoD; // Start of descent calculated wrt wind, pattern size & altitude, glideslope etc - bool descending; // We're in the coming down phase of the pattern - double targetDescentRate; // m/s - - // Taxiing details - // At the moment this assumes that all taxiing in is to gates (a loose term that includes - // any permitted parking spot) and that all taxiing out is to runways. - bool parked; - bool taxiing; - bool taxiRequestPending; - bool taxiRequestCleared; - TaxiState taxiState; - double desiredTaxiHeading; - double taxiTurnRadius; - double nominalTaxiSpeed; - Gate* ourGate; - ground_network_path_type path; // a path through the ground network for the plane to taxi - unsigned int taxiPathPos; // position of iterator in taxi path when applicable - node* nextTaxiNode; // next node in taxi path - node* holdShortNode; - //Runway out_dest; //FIXME - implement this - bool holdingShort; - bool reportReadyForDeparture; // set true when ATC has requested that the plane report when ready for departure - bool clearedToLineUp; - bool clearedToTakeOff; - bool _clearedToLand; // also implies cleared for the option. - bool liningUp; // Set true when the turn onto the runway heading is commenced when taxiing out - bool goAround; // Set true if need to go-around - bool goAroundCalled; // Set true during go-around only after we have called our go-around on the radio - bool contactTower; // we have been told to contact tower - bool contactGround; // we have been told to contact ground - bool changeFreq; // true when we need to change frequency - bool _taxiToGA; // Temporary mega-hack indicating we are to taxi to the GA parking and disconnect from tower control. - bool _removeSelf; // Indicates that we wish to remove this instance. The use of a variable is a hack to allow time for messages to purge before removal, due to the fagility of the current dialog system. - atc_type changeFreqType; // the service we need to change to - bool freeTaxi; // False if the airport has a facilities file with a logical taxi network defined, true if we need to calculate our own taxiing points. - - // Hack for getting close to the runway when atan can go pear-shaped - double _savedSlope; - - void FlyTrafficPattern(double dt); - - // TODO - need to add something to define what option we are flying - Touch and go / Stop and go / Landing properly / others? - - void TransmitPatternPositionReport(); - - void CalculateSoD(double base_leg_pos, double downwind_leg_pos, bool pattern_direction); - - void ExitRunway(const Point3D& orthopos); - - void StartTaxi(); - - void Taxi(double dt); - - void GetNextTaxiNode(); - - void DoGroundElev(); - - // Set when the plane should be invisible *regardless of distance from user*. - bool _invisible; -}; - -#endif // _FG_AILocalTraffic_HXX diff --git a/src/ATC/AIMgr.cxx b/src/ATC/AIMgr.cxx deleted file mode 100644 index 23d44bbf1..000000000 --- a/src/ATC/AIMgr.cxx +++ /dev/null @@ -1,624 +0,0 @@ -// AIMgr.cxx - implementation of FGAIMgr -// - a global management class for FlightGear generated AI traffic -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include
-#include
-#include -#include -#include -#include - -#ifdef _MSC_VER -# include -#else -# include // for directory reading -# include // for directory reading -#endif - -#include -#include - -#include "AIMgr.hxx" -#include "AILocalTraffic.hxx" -#include "AIGAVFRTraffic.hxx" -#include "ATCutils.hxx" -#include "commlist.hxx" - -SG_USING_STD(list); -SG_USING_STD(cout); - -using namespace simgear; - -FGAIMgr::FGAIMgr() { - ATC = globals->get_ATC_mgr(); - initDone = false; - ai_callsigns_used["GFS"] = 1; // so we don't inadvertently use this - // TODO - use the proper user callsign when it becomes user settable. - removalList.clear(); - activated.clear(); - _havePiperModel = true; -} - -FGAIMgr::~FGAIMgr() { - for (ai_list_itr = ai_list.begin(); ai_list_itr != ai_list.end(); ai_list_itr++) { - delete (*ai_list_itr); - } -} - -void FGAIMgr::init() { - //cout << "AIMgr::init called..." << endl; - - // Pointers to user's position - lon_node = fgGetNode("/position/longitude-deg", true); - lat_node = fgGetNode("/position/latitude-deg", true); - elev_node = fgGetNode("/position/altitude-ft", true); - - lon = lon_node->getDoubleValue(); - lat = lat_node->getDoubleValue(); - elev = elev_node->getDoubleValue(); - - // Load up models at the start to avoid pausing later - // Hack alert - Hardwired paths!! - string planepath = "Aircraft/c172p/Models/c172p.xml"; - bool _loadedDefaultOK = true; - try { - _defaultModel = SGModelLib::loadPagedModel(planepath.c_str(), globals->get_props()); - } catch(sg_exception&) { - _loadedDefaultOK = false; - } - - if(!_loadedDefaultOK ) { - // Just load the same 3D model as the default user plane - that's *bound* to exist! - // TODO - implement robust determination of availability of GA AI aircraft models - planepath = "Aircraft/c172p/Models/c172p.ac"; - _defaultModel = SGModelLib::loadPagedModel(planepath.c_str(), globals->get_props()); - } - - planepath = "Aircraft/pa28-161/Models/pa28-161.ac"; - try { - _piperModel = SGModelLib::loadPagedModel(planepath.c_str(), globals->get_props()); - } catch(sg_exception&) { - _havePiperModel = false; - } - - // go through the $FG_ROOT/ATC directory and find all *.taxi files - SGPath path(globals->get_fg_root()); - path.append("ATC/"); - string dir = path.dir(); - string ext; - string file, f_ident; - int pos; - - ulDir *d; - struct ulDirEnt *de; - - if ( (d = ulOpenDir( dir.c_str() )) == NULL ) { - SG_LOG(SG_ATC, SG_WARN, "cannot open directory " << dir); - } else { - // load all .taxi files - while ( (de = ulReadDir(d)) != NULL ) { - file = de->d_name; - pos = file.find("."); - ext = file.substr(pos + 1); - if(ext == "taxi") { - f_ident = file.substr(0, pos); - const FGAirport *a = fgFindAirportID( f_ident); - if(a){ - SGBucket sgb(a->getLongitude(), a->getLatitude()); - int idx = sgb.gen_index(); - if(facilities.find(idx) != facilities.end()) { - facilities[idx]->push_back(f_ident); - } else { - ID_list_type* apts = new ID_list_type; - apts->push_back(f_ident); - facilities[idx] = apts; - } - SG_LOG(SG_ATC, SG_BULK, "Mapping " << f_ident << " to bucket " << idx); - } - } - } - ulCloseDir(d); - } - - // See if are in range at startup and activate if necessary - SearchByPos(15.0); - - initDone = true; - - //cout << "AIMgr::init done..." << endl; - - /* - // TESTING - FGATCAlignedProjection ortho; - ortho.Init(fgGetAirportPos("KEMT"), 205.0); // Guess of rwy19 heading - //Point3D ip = ortho.ConvertFromLocal(Point3D(6000, 1000, 1000)); // 90 deg entry - //Point3D ip = ortho.ConvertFromLocal(Point3D(-7000, 3000, 1000)); // 45 deg entry - Point3D ip = ortho.ConvertFromLocal(Point3D(1000, -7000, 1000)); // straight-in - ATC->AIRegisterAirport("KEMT"); - FGAIGAVFRTraffic* p = new FGAIGAVFRTraffic(); - p->SetModel(_defaultModel); - p->Init(ip, "KEMT", GenerateShortForm(GenerateUniqueCallsign())); - ai_list.push_back(p); - traffic[ident].push_back(p); - activated["KEMT"] = 1; - */ -} - -void FGAIMgr::bind() { -} - -void FGAIMgr::unbind() { -} - -void FGAIMgr::update(double dt) { - if(!initDone) { - init(); - SG_LOG(SG_ATC, SG_WARN, "Warning - AIMgr::update(...) called before AIMgr::init()"); - } - - //cout << activated.size() << '\n'; - - Point3D userPos = Point3D(lon_node->getDoubleValue(), lat_node->getDoubleValue(), elev_node->getDoubleValue()); - - // TODO - make these class variables!! - static int i = 0; - static int j = 0; - - // Don't update any planes for first 50 runs through - this avoids some possible initialisation anomalies - // Might not need it now we have fade-in though? - if(i < 50) { - ++i; - return; - } - - if(j == 215) { - SearchByPos(25.0); - j = 0; - } else if(j == 200) { - // Go through the list of activated airports and remove those out of range - //cout << "The following airports have been activated by the AI system:\n"; - ai_activated_map_iterator apt_itr = activated.begin(); - while(apt_itr != activated.end()) { - //cout << "FIRST IS " << (*apt_itr).first << '\n'; - if(dclGetHorizontalSeparation(userPos, fgGetAirportPos((*apt_itr).first)) > (35.0 * 1600.0)) { - // Then get rid of it and make sure the iterator is left pointing to the next one! - string s = (*apt_itr).first; - if(traffic.find(s) != traffic.end()) { - //cout << "s = " << s << ", traffic[s].size() = " << traffic[s].size() << '\n'; - if(!traffic[s].empty()) { - apt_itr++; - } else { - //cout << "Erasing " << (*apt_itr).first << " and traffic" << '\n'; - activated.erase(apt_itr); - apt_itr = activated.upper_bound(s); - traffic.erase(s); - } - } else { - //cout << "Erasing " << (*apt_itr).first << ' ' << (*apt_itr).second << '\n'; - activated.erase(apt_itr); - apt_itr = activated.upper_bound(s); - } - } else { - apt_itr++; - } - } - } else if(j == 180) { - // Go through the list of activated airports and do the random airplane generation - ai_traffic_map_iterator it = traffic.begin(); - while(it != traffic.end()) { - string s = (*it).first; - //cout << "s = " << s << " size = " << (*it).second.size() << '\n'; - // Only generate extra traffic if within a certain distance of the user, - // TODO - maybe take users's tuned freq into account as well. - double d = dclGetHorizontalSeparation(userPos, fgGetAirportPos(s)); - if(d < (15.0 * 1600.0)) { - double cd = 0.0; - bool gen = false; - //cout << "Size of list is " << (*it).second.size() << " at " << s << '\n'; - if((*it).second.size()) { - FGAIEntity* e = *((*it).second.rbegin()); // Get the last airplane currently scheduled to arrive at this airport. - cd = dclGetHorizontalSeparation(e->GetPos(), fgGetAirportPos(s)); - if(cd < (d < 5000 ? 10000 : d + 5000)) { - gen = true; - } - } else { - gen = true; - cd = 0.0; - } - if(gen) { - //cout << "Generating extra traffic at airport " << s << ", at least " << cd << " meters out\n"; - //GenerateSimpleAirportTraffic(s, cd); - GenerateSimpleAirportTraffic(s, cd + 3000.0); // The random seems a bit wierd - traffic could get far too bunched without the +3000. - // TODO - make the anti-random constant variable depending on the ai-traffic level. - } - } - ++it; - } - } - - ++j; - - //cout << "Size of AI list is " << ai_list.size() << '\n'; - - // TODO - need to add a check of if any activated airports have gone out of range - - string rs; // plane to be removed, if one. - if(removalList.size()) { - rs = *(removalList.begin()); - removalList.pop_front(); - } else { - rs = ""; - } - - // Traverse the list of active planes and run all their update methods - // TODO - spread the load - not all planes should need updating every frame. - // Note that this will require dt to be calculated for each plane though - // since they rely on it to calculate distance travelled. - ai_list_itr = ai_list.begin(); - while(ai_list_itr != ai_list.end()) { - FGAIEntity *e = *ai_list_itr; - if(rs.size() && e->GetCallsign() == rs) { - //cout << "Removing " << rs << " from ai_list\n"; - ai_list_itr = ai_list.erase(ai_list_itr); - delete e; - // This is a hack - we should deref this plane from the airport count! - } else { - e->Update(dt); - ++ai_list_itr; - } - } - - //cout << "Size of AI list is " << ai_list.size() << '\n'; -} - -void FGAIMgr::ScheduleRemoval(const string& s) { - //cout << "Scheduling removal of plane " << s << " from AIMgr\n"; - removalList.push_back(s); -} - -// Activate AI traffic at an airport -void FGAIMgr::ActivateAirport(const string& ident) { - ATC->AIRegisterAirport(ident); - // TODO - need to start the traffic more randomly - FGAILocalTraffic* local_traffic = new FGAILocalTraffic; - local_traffic->SetModel(_defaultModel.get()); // currently hardwired to cessna. - //local_traffic->Init(ident, IN_PATTERN, TAKEOFF_ROLL); - local_traffic->Init(GenerateShortForm(GenerateUniqueCallsign()), ident); - local_traffic->FlyCircuits(1, true); // Fly 2 circuits with touch & go in between - ai_list.push_back(local_traffic); - traffic[ident].push_back(local_traffic); - //cout << "******** ACTIVATING AIRPORT, ident = " << ident << '\n'; - activated[ident] = 1; -} - -// Hack - Generate AI traffic at an airport with no facilities file -void FGAIMgr::GenerateSimpleAirportTraffic(const string& ident, double min_dist) { - // Ugly hack - don't let VFR Cessnas operate at a hardwired list of major airports - // This will go eventually once airport .xml files specify the traffic profile - if(ident == "KSFO" || ident == "KDFW" || ident == "EGLL" || ident == "KORD" || ident == "KJFK" - || ident == "KMSP" || ident == "KLAX" || ident == "KBOS" || ident == "KEDW" - || ident == "KSEA" || ident == "EHAM") { - return; - } - - /* - // TODO - check for military airports - this should be in the current data. - // UGGH - there's no point at the moment - everything is labelled civil in basic.dat! - FGAirport a = fgFindAirportID(ident, &a); - if(a) { - cout << "CODE IS " << a.code << '\n'; - } else { - // UG - can't find the airport! - return; - } - */ - - Point3D aptpos = fgGetAirportPos(ident); // TODO - check for elev of -9999 - //cout << "ident = " << ident << ", elev = " << aptpos.elev() << '\n'; - - // Operate from airports at 3000ft and below only to avoid the default cloud layers and since we don't degrade AI performance with altitude. - if(aptpos.elev() > 3000) { - //cout << "High alt airports not yet supported - returning\n"; - return; - } - - // Rough hack for plane type - make 70% of the planes cessnas, the rest pipers. - bool cessna = true; - - // Get the time and only operate VFR in the (approximate) daytime. - struct tm *t = globals->get_time_params()->getGmt(); - int loc_time = t->tm_hour + int(aptpos.lon() / (360.0 / 24.0)); - while (loc_time < 0) - loc_time += 24; - while (loc_time >= 24) - loc_time -= 24; - - //cout << "loc_time = " << loc_time << '\n'; - if(loc_time < 7 || loc_time > 19) return; - - // Check that the visibility is OK for IFR operation. - double visibility; - FGEnvironment stationweather = - ((FGEnvironmentMgr *)globals->get_subsystem("environment")) - ->getEnvironment(aptpos.lat(), aptpos.lon(), aptpos.elev()); // TODO - check whether this should take ft or m for elev. - visibility = stationweather.get_visibility_m(); - // Technically we can do VFR down to 1 mile (1600m) but that's pretty murky! - //cout << "vis = " << visibility << '\n'; - if(visibility < 3000) return; - - ATC->AIRegisterAirport(ident); - - // Next - get the distance from user to the airport. - Point3D userpos = Point3D(lon_node->getDoubleValue(), lat_node->getDoubleValue(), elev_node->getDoubleValue()); - double d = dclGetHorizontalSeparation(userpos, aptpos); // in meters - - int lev = fgGetInt("/sim/ai-traffic/level"); - if(lev < 1) - return; - if (lev > 3) - lev = 3; - if(visibility < 6000) lev = 1; - //cout << "level = " << lev << '\n'; - - // Next - generate any local / circuit traffic - - /* - // --------------------------- THIS BLOCK IS JUST FOR TESTING - COMMENT OUT BEFORE RELEASE --------------- - // Finally - generate VFR approaching traffic - //if(d > 2000) { - if(ident == "KPOC") { - double ad = 2000.0; - double avd = 3000.0; // average spacing of arriving traffic in meters - relate to airport business and AI density setting one day! - //while(ad < (d < 10000 ? 12000 : d + 2000)) { - for(int i=0; i<8; ++i) { - double dd = sg_random() * avd; - // put a minimum spacing in for now since I don't think tower will cope otherwise! - if(dd < 1500) dd = 1500; - //ad += dd; - ad += dd; - double dir = int(sg_random() * 36); - if(dir == 36) dir--; - dir *= 10; - //dir = 180; - if(sg_random() < 0.3) cessna = false; - else cessna = true; - string s = GenerateShortForm(GenerateUniqueCallsign(), (cessna ? "Cessna-" : "Piper-")); - FGAIGAVFRTraffic* t = new FGAIGAVFRTraffic(); - t->SetModel(cessna ? _defaultModel : _piperModel); - //cout << "Generating VFR traffic " << s << " inbound to " << ident << " " << ad << " meters out from " << dir << " degrees\n"; - Point3D tpos = dclUpdatePosition(aptpos, dir, 6.0, ad); - if(tpos.elev() > (aptpos.elev() + 3000.0)) tpos.setelev(aptpos.elev() + 3000.0); - t->Init(tpos, ident, s); - ai_list.push_back(t); - } - } - activated[ident] = 1; - return; - //--------------------------------------------------------------------------------------------------- - */ - - double ad; // Minimum distance out of first arriving plane in meters. - double mind; // Minimum spacing of traffic in meters - double avd; // average spacing of arriving traffic in meters - relate to airport business and AI density setting one day! - // Finally - generate VFR approaching traffic - //if(d > 2000) { - if(1) { - if(lev == 3) { - ad = 5000.0; - mind = 2000.0; - avd = 6000.0; - } else if(lev == 2) { - ad = 8000.0; - mind = 4000.0; - avd = 10000.0; - } else { - ad = 9000.0; // Start the first aircraft at least 9K out for now. - mind = 6000.0; - avd = 15000.0; - } - /* - // Check if there is already arriving traffic at this airport - cout << "BING A " << ident << '\n'; - if(traffic.find(ident) != traffic.end()) { - cout << "BING B " << ident << '\n'; - ai_list_type lst = traffic[ident]; - cout << "BING C " << ident << '\n'; - if(lst.size()) { - cout << "BING D " << ident << '\n'; - double cd = dclGetHorizontalSeparation(aptpos, (*lst.rbegin())->GetPos()); - cout << "ident = " << ident << ", cd = " << cd << '\n'; - if(cd > ad) ad = cd; - } - } - */ - if(min_dist != 0) ad = min_dist; - //cout << "ident = " << ident << ", ad = " << ad << '\n'; - while(ad < (d < 5000 ? 15000 : d + 10000)) { - double dd = mind + (sg_random() * (avd - mind)); - ad += dd; - double dir = int(sg_random() * 36); - if(dir == 36) dir--; - dir *= 10; - - if(sg_random() < 0.3) cessna = false; - else cessna = true; - string s = GenerateShortForm(GenerateUniqueCallsign(), (cessna ? "Cessna-" : "Piper-")); - FGAIGAVFRTraffic* t = new FGAIGAVFRTraffic(); - t->SetModel(cessna ? _defaultModel.get() : (_havePiperModel ? _piperModel.get() : _defaultModel.get())); - //cout << "Generating VFR traffic " << s << " inbound to " << ident << " " << ad << " meters out from " << dir << " degrees\n"; - Point3D tpos = dclUpdatePosition(aptpos, dir, 6.0, ad); - if(tpos.elev() > (aptpos.elev() + 3000.0)) tpos.setelev(aptpos.elev() + 3000.0); // FEET yuk :-( - t->Init(tpos, ident, s); - ai_list.push_back(t); - traffic[ident].push_back(t); - } - } -} - -/* -// Generate a VFR arrival at airport apt, at least distance d (meters) out. -void FGAIMgr::GenerateVFRArrival(const string& apt, double d) { -} -*/ - -// Search for valid airports in the vicinity of the user and activate them if necessary -void FGAIMgr::SearchByPos(double range) { - //cout << "In SearchByPos(...)" << endl; - - // get bucket number for plane position - lon = lon_node->getDoubleValue(); - lat = lat_node->getDoubleValue(); - elev = elev_node->getDoubleValue() * SG_FEET_TO_METER; - SGBucket buck(lon, lat); - - // get neigboring buckets - int bx = (int)( range*SG_NM_TO_METER / buck.get_width_m() / 2); - //cout << "bx = " << bx << endl; - int by = (int)( range*SG_NM_TO_METER / buck.get_height_m() / 2 ); - //cout << "by = " << by << endl; - - // Search for airports with facitities files -------------------------- - // loop over bucket range - for ( int i=-bx; i<=bx; i++) { - //cout << "i loop\n"; - for ( int j=-by; j<=by; j++) { - //cout << "j loop\n"; - buck = sgBucketOffset(lon, lat, i, j); - long int bucket = buck.gen_index(); - //cout << "bucket is " << bucket << endl; - if(facilities.find(bucket) != facilities.end()) { - ID_list_type* apts = facilities[bucket]; - ID_list_iterator current = apts->begin(); - ID_list_iterator last = apts->end(); - - //cout << "Size of apts is " << apts->size() << endl; - - //double rlon = lon * SGD_DEGREES_TO_RADIANS; - //double rlat = lat * SGD_DEGREES_TO_RADIANS; - //Point3D aircraft = sgGeodToCart( Point3D(rlon, rlat, elev) ); - //Point3D airport; - for(; current != last; ++current) { - //cout << "Found " << *current << endl;; - if(activated.find(*current) == activated.end()) { - //cout << "Activating " << *current << endl; - //FGAirport a; - //if(dclFindAirportID(*current, &a)) { - // // We can do something here based on distance from the user if we wish. - //} - //string s = *current; - //cout << "s = " << s << '\n'; - ActivateAirport(*current); - //ActivateSimpleAirport(*current); // TODO - put this back to ActivateAirport when that code is done. - //cout << "Activation done" << endl; - } else { - //cout << *current << " already activated" << endl; - } - } - } - } - } - //------------------------------------------------------------- - - // Search for any towered airports in the vicinity ------------ - comm_list_type towered; - comm_list_iterator twd_itr; - - int num_twd = current_commlist->FindByPos(lon, lat, elev, range, &towered, TOWER); - if (num_twd != 0) { - double closest = 1000000; - string s = ""; - for(twd_itr = towered.begin(); twd_itr != towered.end(); twd_itr++) { - // Only activate the closest airport not already activated each time. - if(activated.find(twd_itr->ident) == activated.end()) { - double sep = dclGetHorizontalSeparation(Point3D(lon, lat, elev), fgGetAirportPos(twd_itr->ident)); - if(sep < closest) { - closest = sep; - s = twd_itr->ident; - } - - } - } - if(s.size()) { - // TODO - find out why empty strings come through here when all in-range airports done. - GenerateSimpleAirportTraffic(s); - //cout << "**************ACTIVATING SIMPLE AIRPORT, ident = " << s << '\n'; - activated[s] = 1; - } - } -} - -string FGAIMgr::GenerateCallsign() { - // For now we'll just generate US callsigns until we can regionally identify airports. - string s = "N"; - // Add 3 to 5 numbers and make up to 5 with letters. - //sg_srandom_time(); - double d = sg_random(); - int n = int(d * 3); - if(n == 3) --n; - //cout << "First n, n = " << n << '\n'; - int j = 3 + n; - //cout << "j = " << j << '\n'; - for(int i=0; i0; --i) { - char c = callsign[callsign.size() - i]; - //cout << c << '\n'; - string tmp = ""; - tmp += c; - if(isalpha(c)) s += GetPhoneticIdent(c); - else s += ConvertNumToSpokenDigits(tmp); - if(i > 1) s += '-'; - } - return(s); -} diff --git a/src/ATC/AIMgr.hxx b/src/ATC/AIMgr.hxx deleted file mode 100644 index 6bc81a10b..000000000 --- a/src/ATC/AIMgr.hxx +++ /dev/null @@ -1,139 +0,0 @@ -// AIMgr.hxx - definition of FGAIMgr -// - a global management class for FlightGear generated AI traffic -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifndef _FG_AIMGR_HXX -#define _FG_AIMGR_HXX - -#include - -#include
- -#include - -#include "ATCmgr.hxx" -#include "AIEntity.hxx" - -SG_USING_STD(list); - - -class FGAIMgr : public SGSubsystem -{ - -private: - FGATCMgr* ATC; - // This is purely for synactic convienience to avoid writing globals->get_ATC_mgr()-> all through the code! - - // A list of pointers to all currently active AI stuff - typedef list ai_list_type; - typedef ai_list_type::iterator ai_list_iterator; - typedef ai_list_type::const_iterator ai_list_const_iterator; - - // Everything put in this list should be created dynamically - // on the heap and ***DELETED WHEN REMOVED!!!!!*** - ai_list_type ai_list; - ai_list_iterator ai_list_itr; - // Any member function of FGATCMgr is permitted to leave this iterator pointing - // at any point in or at the end of the list. - // Hence any new access must explicitly first check for atc_list.end() before dereferencing. - - // A list of airport or airplane ID's - typedef list < string > ID_list_type; - typedef ID_list_type::iterator ID_list_iterator; - - // Temporary storage of ID of planes scheduled for removeal - ID_list_type removalList; - - // A map of airport-IDs that have taxiway network files against bucket number - typedef map < int, ID_list_type* > ai_apt_map_type; - typedef ai_apt_map_type::iterator ai_apt_map_iterator; - ai_apt_map_type facilities; - - // A map of airport ID's that we've activated AI traffic at - typedef map < string, int > ai_activated_map_type; - typedef ai_activated_map_type::iterator ai_activated_map_iterator; - ai_activated_map_type activated; - - // AI traffic lists mapped by airport - typedef map < string, ai_list_type > ai_traffic_map_type; - typedef ai_traffic_map_type::iterator ai_traffic_map_iterator; - ai_traffic_map_type traffic; - - // A map of callsigns that we have used (eg CFGFS or N0546D - the code will generate Cessna-four-six-delta from this later) - typedef map < string, int > ai_callsigns_map_type; - typedef ai_callsigns_map_type::iterator ai_callsigns_map_iterator; - ai_callsigns_map_type ai_callsigns_used; - - // Position of the Users Aircraft - double lon; - double lat; - double elev; - // Pointers to current users position - SGPropertyNode_ptr lon_node; - SGPropertyNode_ptr lat_node; - SGPropertyNode_ptr elev_node; - -public: - - FGAIMgr(); - ~FGAIMgr(); - - void init(); - - void bind(); - - void unbind(); - - void update(double dt); - - // Signal that it is OK to remove a plane of callsign s - // (To be called by the plane itself). - void ScheduleRemoval(const string& s); - -private: - - osg::ref_ptr _defaultModel; // Cessna 172! - osg::ref_ptr _piperModel; // pa28-161 - - bool initDone; // Hack - guard against update getting called before init - - // Remove a class from the ai_list and delete it from memory - //void RemoveFromList(const char* id, atc_type tp); - - // Activate AI traffic at an airport - void ActivateAirport(const string& ident); - - // Hack - Generate AI traffic at an airport with no facilities file, with the first plane being at least min_dist out. - void GenerateSimpleAirportTraffic(const string& ident, double min_dist = 0.0); - - // Search for valid airports in the vicinity of the user and activate them if necessary - void SearchByPos(double range); - - string GenerateCallsign(); - - string GenerateUniqueCallsign(); - - string GenerateShortForm(const string& callsign, const string& plane_str = "Cessna-", bool local = false); - - // TODO - implement a proper robust system for registering and loading AI GA aircraft models - bool _havePiperModel; -}; - -#endif // _FG_AIMGR_HXX diff --git a/src/ATC/AIPlane.cxx b/src/ATC/AIPlane.cxx deleted file mode 100644 index 84006294d..000000000 --- a/src/ATC/AIPlane.cxx +++ /dev/null @@ -1,272 +0,0 @@ -// FGAIPlane - abstract base class for an AI plane -// -// Written by David Luff, started 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -#include
-#include
-#include -#include -#include -#include -#include -SG_USING_STD(string); - - -#include "AIPlane.hxx" - -FGAIPlane::FGAIPlane() { - leg = LEG_UNKNOWN; - tuned_station = NULL; - pending_transmission = ""; - _timeout = 0; - _pending = false; - _callback_code = 0; - _transmit = false; - _transmitting = false; - voice = false; - playing = false; - voiceOK = false; - vPtr = NULL; - track = 0.0; - _tgtTrack = 0.0; - _trackSet = false; - _tgtRoll = 0.0; - _rollSuspended = false; -} - -FGAIPlane::~FGAIPlane() { -} - -void FGAIPlane::Update(double dt) { - if(_pending) { - if(tuned_station) { - if(tuned_station->GetFreqClear()) { - //cout << "TUNED STATION FREQ CLEAR\n"; - tuned_station->SetFreqInUse(); - _pending = false; - _transmit = true; - _transmitting = false; - } else { - if(_timeout > 0.0) { // allows count down to be avoided by initially setting it to zero - _timeout -= dt; - if(_timeout <= 0.0) { - _timeout = 0.0; - _pending = false; - // timed out - don't render. - if(_callback_code == 99) { - // MEGA-HACK - 99 is the remove self callback - currently this *does* need to be run even if the transmission isn't made. - ProcessCallback(_callback_code); - } - } - } - } - } else { - // Not tuned to ATC - Just go ahead and transmit - //cout << "NOT TUNED TO ATC\n"; - _pending = false; - _transmit = true; - _transmitting = false; - } - } - - // This turns on rendering if on the same freq as the user - // TODO - turn it off if user switches to another freq - keep track of where in message we are etc. - if(_transmit) { - //cout << "transmit\n"; - double user_freq0 = fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz"); - double user_freq1 = fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz"); - _counter = 0.0; - _max_count = 5.0; // FIXME - hardwired length of message - need to calculate it! - - //cout << "Transmission = " << pending_transmission << '\n'; - - // The radios dialog seems to set slightly imprecise freqs, eg 118.099998 - // The eplison stuff below is a work-around - double eps0 = fabs(freq - user_freq0); - double eps1 = fabs(freq - user_freq1); - if(eps0 < 0.002 || eps1 < 0.002) { - //cout << "Transmitting..." << endl; - // we are on the same frequency, so check distance to the user plane - if(1) { - // For now assume in range !!! - // TODO - implement range checking - Render(plane.callsign, false); - } - } - // Run the callback regardless of whether on same freq as user or not. - if(_callback_code) { - ProcessCallback(_callback_code); - } - _transmit = false; - _transmitting = true; - } else if(_transmitting) { - if(_counter >= _max_count) { - NoRender(plane.callsign); - _transmitting = false; - // For now we'll let ATC decide whether to respond - //if(tuned_station) tuned_station->SetResponseReqd(plane.callsign); - //if(tuned_station->get_ident() == "KRHV") cout << "Notifying transmission finished" << endl; - if(tuned_station) tuned_station->NotifyTransmissionFinished(plane.callsign); - } - _counter += dt; - } - - // Fly the plane if necessary - if(_trackSet) { - while((_tgtTrack - track) > 180.0) track += 360.0; - while((track - _tgtTrack) > 180.0) track -= 360.0; - double turn_time = 60.0; - track += (360.0 / turn_time) * dt * (_tgtTrack > track ? 1.0 : -1.0); - // TODO - bank a bit less for small turns. - Bank(25.0 * (_tgtTrack > track ? 1.0 : -1.0)); - if(fabs(track - _tgtTrack) < 2.0) { // TODO - might need to optimise the delta there - it's on the large (safe) side atm. - track = _tgtTrack; - LevelWings(); - } - } - - if(!_rollSuspended) { - if(fabs(_roll - _tgtRoll) > 0.6) { - // This *should* bank us smoothly to any angle - _roll -= ((_roll - _tgtRoll)/fabs(_roll - _tgtRoll)); - } else { - _roll = _tgtRoll; - } - } -} - -void FGAIPlane::Transmit(int callback_code) { - SG_LOG(SG_ATC, SG_INFO, "Transmit called for plane " << plane.callsign << ", msg = " << pending_transmission); - _pending = true; - _callback_code = callback_code; - _timeout = 0.0; -} - -void FGAIPlane::ConditionalTransmit(double timeout, int callback_code) { - SG_LOG(SG_ATC, SG_INFO, "Timed transmit called for plane " << plane.callsign << ", msg = " << pending_transmission); - _pending = true; - _callback_code = callback_code; - _timeout = timeout; -} - -void FGAIPlane::ImmediateTransmit(int callback_code) { - Render(plane.callsign, false); - if(callback_code) { - ProcessCallback(callback_code); - } -} - -// Derived classes should override this. -void FGAIPlane::ProcessCallback(int code) { -} - -// Render a transmission -// Outputs the transmission either on screen or as audio depending on user preference -// The refname is a string to identify this sample to the sound manager -// The repeating flag indicates whether the message should be repeated continuously or played once. -void FGAIPlane::Render(const string& refname, bool repeating) { - fgSetString("/sim/messages/ai-plane", pending_transmission.c_str()); -#ifdef ENABLE_AUDIO_SUPPORT - voice = (voiceOK && fgGetBool("/sim/sound/voice")); - if(voice) { - int len; - unsigned char* buf = vPtr->WriteMessage((char*)pending_transmission.c_str(), len, voice); - if(voice) { - SGSoundSample* simple = new SGSoundSample(buf, len, 8000); - // TODO - at the moment the volume is always set off comm1 - // and can't be changed after the transmission has started. - simple->set_volume(5.0 * fgGetDouble("/instrumentation/comm[0]/volume")); - globals->get_soundmgr()->add(simple, refname); - if(repeating) { - globals->get_soundmgr()->play_looped(refname); - } else { - globals->get_soundmgr()->play_once(refname); - } - } - delete[] buf; - } -#endif // ENABLE_AUDIO_SUPPORT - if(!voice) { - // first rip the underscores and the pause hints out of the string - these are for the convienience of the voice parser - for(unsigned int i = 0; i < pending_transmission.length(); ++i) { - if((pending_transmission.substr(i,1) == "_") || (pending_transmission.substr(i,1) == "/")) { - pending_transmission[i] = ' '; - } - } - } - playing = true; -} - - -// Cease rendering a transmission. -void FGAIPlane::NoRender(const string& refname) { - if(playing) { - if(voice) { -#ifdef ENABLE_AUDIO_SUPPORT - globals->get_soundmgr()->stop(refname); - globals->get_soundmgr()->remove(refname); -#endif - } - playing = false; - } -} - -/* - -*/ - -void FGAIPlane::RegisterTransmission(int code) { -} - - -// Return what type of landing we're doing on this circuit -LandingType FGAIPlane::GetLandingOption() { - return(FULL_STOP); -} - - -ostream& operator << (ostream& os, PatternLeg pl) { - switch(pl) { - case(TAKEOFF_ROLL): return(os << "TAKEOFF ROLL"); - case(CLIMBOUT): return(os << "CLIMBOUT"); - case(TURN1): return(os << "TURN1"); - case(CROSSWIND): return(os << "CROSSWIND"); - case(TURN2): return(os << "TURN2"); - case(DOWNWIND): return(os << "DOWNWIND"); - case(TURN3): return(os << "TURN3"); - case(BASE): return(os << "BASE"); - case(TURN4): return(os << "TURN4"); - case(FINAL): return(os << "FINAL"); - case(LANDING_ROLL): return(os << "LANDING ROLL"); - case(LEG_UNKNOWN): return(os << "UNKNOWN"); - } - return(os << "ERROR - Unknown switch in PatternLeg operator << "); -} - - -ostream& operator << (ostream& os, LandingType lt) { - switch(lt) { - case(FULL_STOP): return(os << "FULL STOP"); - case(STOP_AND_GO): return(os << "STOP AND GO"); - case(TOUCH_AND_GO): return(os << "TOUCH AND GO"); - case(AIP_LT_UNKNOWN): return(os << "UNKNOWN"); - } - return(os << "ERROR - Unknown switch in LandingType operator << "); -} - diff --git a/src/ATC/AIPlane.hxx b/src/ATC/AIPlane.hxx deleted file mode 100644 index b29726c39..000000000 --- a/src/ATC/AIPlane.hxx +++ /dev/null @@ -1,165 +0,0 @@ -// FGAIPlane - abstract base class for an AI plane -// -// Written by David Luff, started 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifndef _FG_AI_PLANE_HXX -#define _FG_AI_PLANE_HXX - -#include - -#include "AIEntity.hxx" -#include "ATC.hxx" - -enum PatternLeg { - TAKEOFF_ROLL, - CLIMBOUT, - TURN1, - CROSSWIND, - TURN2, - DOWNWIND, - TURN3, - BASE, - TURN4, - FINAL, - LANDING_ROLL, - LEG_UNKNOWN -}; - -ostream& operator << (ostream& os, PatternLeg pl); - -enum LandingType { - FULL_STOP, - STOP_AND_GO, - TOUCH_AND_GO, - AIP_LT_UNKNOWN -}; - -ostream& operator << (ostream& os, LandingType lt); - -/***************************************************************** -* -* FGAIPlane - this class is derived from FGAIEntity and adds the -* practical requirement for an AI plane - the ability to send radio -* communication, and simple performance details for the actual AI -* implementation to use. The AI implementation is expected to be -* in derived classes - this class does nothing useful on its own. -* -******************************************************************/ -class FGAIPlane : public FGAIEntity { - -public: - - FGAIPlane(); - virtual ~FGAIPlane(); - - // Run the internal calculations - void Update(double dt); - - // Send a transmission *TO* the AIPlane. - // FIXME int code is a hack - eventually this will receive Alexander's coded messages. - virtual void RegisterTransmission(int code); - - // Return the current pattern leg the plane is flying. - inline PatternLeg GetLeg() {return leg;} - - // Return what type of landing we're doing on this circuit - virtual LandingType GetLandingOption(); - - // Return the callsign - inline const string& GetCallsign() {return plane.callsign;} - -protected: - PlaneRec plane; - - double mag_hdg; // degrees - the heading that the physical aircraft is *pointing* - double track; // track that the physical aircraft is *following* - degrees relative to *true* north - double crab; // Difference between heading and track due to wind. - double mag_var; // degrees - double IAS; // Indicated airspeed in knots - double vel; // velocity along track in knots - double vel_si; // velocity along track in m/s - double slope; // Actual slope that the plane is flying (degrees) - +ve is uphill - double AoA; // degrees - difference between slope and pitch - // We'll assume that the plane doesn't yaw or slip - the track/heading difference is to allow for wind - - double freq; // The comm frequency that we're operating on - - // We need some way for this class to display its radio transmissions if on the - // same frequency and in the vicinity of the user's aircraft - // This may need to be done independently of ATC eg CTAF - // Make radio transmission - this simply sends the transmission for physical rendering if the users - // aircraft is on the same frequency and in range. It is up to the derived classes to let ATC know - // what is going on. - string pending_transmission; // derived classes set this string before calling Transmit(...) - FGATC* tuned_station; // and this if they are tuned to ATC - - // Transmit a message when channel becomes free of other dialog - void Transmit(int callback_code = 0); - - // Transmit a message if channel becomes free within timeout (seconds). timeout of zero implies no limit - void ConditionalTransmit(double timeout, int callback_code = 0); - - // Transmit regardless of other dialog on the channel eg emergency - void ImmediateTransmit(int callback_code = 0); - - inline void SetTrack(double t) { _tgtTrack = t; _trackSet = true; } - inline void ClearTrack() { _trackSet = false; } - - inline void Bank(double r) { _tgtRoll = r; } - inline void LevelWings(void) { _tgtRoll = 0.0; } - - virtual void ProcessCallback(int code); - - PatternLeg leg; - -private: - bool _pending; - double _timeout; - int _callback_code; // A callback code to be notified and processed by the derived classes - // A value of zero indicates no callback required - bool _transmit; // we are to transmit - bool _transmitting; // we are transmitting - double _counter; - double _max_count; - - // Render a transmission (in string pending_transmission) - // Outputs the transmission either on screen or as audio depending on user preference - // The refname is a string to identify this sample to the sound manager - // The repeating flag indicates whether the message should be repeated continuously or played once. - void Render(const string& refname, bool repeating); - - // Cease rendering a transmission. - // Requires the sound manager refname if audio, else "". - void NoRender(const string& refname); - - // Rendering related stuff - bool voice; // Flag - true if we are using voice - bool playing; // Indicates a message in progress - bool voiceOK; // Flag - true if at least one voice has loaded OK - FGATCVoice* vPtr; - - // Navigation - double _tgtTrack; // Track to be following if _trackSet is true - bool _trackSet; // Set true if tgtTrack is to be followed - double _tgtRoll; - bool _rollSuspended; // Set true when a derived class has suspended AIPlane's roll control -}; - -#endif // _FG_AI_PLANE_HXX - diff --git a/src/ATC/ATC.cxx b/src/ATC/ATC.cxx deleted file mode 100644 index 2bc321805..000000000 --- a/src/ATC/ATC.cxx +++ /dev/null @@ -1,291 +0,0 @@ -// Implementation of FGATC - ATC subsystem base class. -// -// Written by David Luff, started February 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include - -#include
-#include
- -#include "ATC.hxx" - -FGATC::FGATC() { - freqClear = true; - receiving = false; - respond = false; - runResponseCounter = false; - _runReleaseCounter = false; - responseID = ""; - responseReqd = false; - _type = INVALID; - _display = false; - _displaying = false; - - // Transmission timing stuff - pending_transmission = ""; - _timeout = 0; - _pending = false; - _callback_code = 0; - _transmit = false; - _transmitting = false; - _counter = 0.0; - _max_count = 5.0; - - _voiceOK = false; -} - -FGATC::~FGATC() { -} - -// Derived classes wishing to use the response counter should call this from their own Update(...). -void FGATC::Update(double dt) { - if(runResponseCounter) { - //cout << responseCounter << '\t' << responseTime << '\n'; - if(responseCounter >= responseTime) { - runResponseCounter = false; - respond = true; - //cout << "RESPOND\n"; - } else { - responseCounter += dt; - } - } - - if(_runReleaseCounter) { - if(_releaseCounter >= _releaseTime) { - freqClear = true; - _runReleaseCounter = false; - } else { - _releaseCounter += dt; - } - } - - // Transmission stuff cribbed from AIPlane.cxx - if(_pending) { - if(GetFreqClear()) { - //cout << "TUNED STATION FREQ CLEAR\n"; - SetFreqInUse(); - _pending = false; - _transmit = true; - _transmitting = false; - } else { - if(_timeout > 0.0) { // allows count down to be avoided by initially setting it to zero - _timeout -= dt; - if(_timeout <= 0.0) { - _timeout = 0.0; - _pending = false; - // timed out - don't render. - } - } - } - } - - if(_transmit) { - _counter = 0.0; - _max_count = 5.0; // FIXME - hardwired length of message - need to calculate it! - - //cout << "Transmission = " << pending_transmission << '\n'; - if(_display) { - //Render(pending_transmission, ident, false); - Render(pending_transmission); - } - // Run the callback regardless of whether on same freq as user or not. - //cout << "_callback_code = " << _callback_code << '\n'; - if(_callback_code) { - ProcessCallback(_callback_code); - } - _transmit = false; - _transmitting = true; - } else if(_transmitting) { - if(_counter >= _max_count) { - //NoRender(plane.callsign); commented out since at the moment NoRender is designed just to stop repeating messages, - // and this will be primarily used on single messages. - _transmitting = false; - //if(tuned_station) tuned_station->NotifyTransmissionFinished(plane.callsign); - // TODO - need to let the plane the transmission is aimed at that it's finished. - // However, for now we'll just release the frequency since if we don't it all goes pear-shaped - _releaseCounter = 0.0; - _releaseTime = 0.9; - _runReleaseCounter = true; - } - _counter += dt; - } -} - -void FGATC::ReceiveUserCallback(int code) { - SG_LOG(SG_ATC, SG_WARN, "WARNING - whichever ATC class was intended to receive callback code " << code << " didn't get it!!!"); -} - -void FGATC::SetResponseReqd(const string& rid) { - receiving = false; - responseReqd = true; - respond = false; // TODO - this ignores the fact that more than one plane could call this before response - // Shouldn't happen with AI only, but user could confuse things?? - responseID = rid; - runResponseCounter = true; - responseCounter = 0.0; - responseTime = 1.8; // TODO - randomize this slightly. -} - -void FGATC::NotifyTransmissionFinished(const string& rid) { - //cout << "Transmission finished, callsign = " << rid << '\n'; - receiving = false; - responseID = rid; - if(responseReqd) { - runResponseCounter = true; - responseCounter = 0.0; - responseTime = 1.2; // TODO - randomize this slightly, and allow it to be dependent on the transmission and how busy the ATC is. - respond = false; // TODO - this ignores the fact that more than one plane could call this before response - // Shouldn't happen with AI only, but user could confuse things?? - } else { - freqClear = true; - } -} - -void FGATC::Transmit(int callback_code) { - SG_LOG(SG_ATC, SG_INFO, "Transmit called by " << ident << " " << _type << ", msg = " << pending_transmission); - _pending = true; - _callback_code = callback_code; - _timeout = 0.0; -} - -void FGATC::ConditionalTransmit(double timeout, int callback_code) { - SG_LOG(SG_ATC, SG_INFO, "Timed transmit called by " << ident << " " << _type << ", msg = " << pending_transmission); - _pending = true; - _callback_code = callback_code; - _timeout = timeout; -} - -void FGATC::ImmediateTransmit(int callback_code) { - SG_LOG(SG_ATC, SG_INFO, "Immediate transmit called by " << ident << " " << _type << ", msg = " << pending_transmission); - if(_display) { - //Render(pending_transmission, ident, false); - Render(pending_transmission); - // At the moment Render doesn't work except for ATIS - } - if(callback_code) { - ProcessCallback(callback_code); - } -} - -// Derived classes should override this. -void FGATC::ProcessCallback(int code) { -} - -void FGATC::AddPlane(const string& pid) { -} - -int FGATC::RemovePlane() { - return 0; -} - -void FGATC::SetData(ATCData* d) { - lon = d->lon; - lat = d->lat; - elev = d->elev; - x = d->x; - y = d->y; - z = d->z; - range = d->range; - ident = d->ident; - name = d->name; - freq = d->freq; -} - -// Render a transmission -// Outputs the transmission either on screen or as audio depending on user preference -// The refname is a string to identify this sample to the sound manager -// The repeating flag indicates whether the message should be repeated continuously or played once. -void FGATC::Render(string& msg, const string& refname, bool repeating) { - if (repeating) - fgSetString("/sim/messages/atis", msg.c_str()); - else - fgSetString("/sim/messages/atc", msg.c_str()); - - #ifdef ENABLE_AUDIO_SUPPORT - _voice = (_voiceOK && fgGetBool("/sim/sound/voice")); - if(_voice) { - int len; - unsigned char* buf = _vPtr->WriteMessage((char*)msg.c_str(), len, _voice); - if(_voice) { - try { - SGSoundSample *simple - = new SGSoundSample(buf, len, 8000); - // TODO - at the moment the volume is always set off comm1 - // and can't be changed after the transmission has started. - simple->set_volume(5.0 * fgGetDouble("/instrumentation/comm[0]/volume")); - globals->get_soundmgr()->add(simple, refname); - if(repeating) { - globals->get_soundmgr()->play_looped(refname); - } else { - globals->get_soundmgr()->play_once(refname); - } - } catch ( sg_io_exception &e ) { - SG_LOG(SG_GENERAL, SG_ALERT, e.getFormattedMessage()); - } - } - delete[] buf; - } - #endif // ENABLE_AUDIO_SUPPORT - if(!_voice) { - // first rip the underscores and the pause hints out of the string - these are for the convienience of the voice parser - for(unsigned int i = 0; i < msg.length(); ++i) { - if((msg.substr(i,1) == "_") || (msg.substr(i,1) == "/")) { - msg[i] = ' '; - } - } - } - _playing = true; -} - - -// Cease rendering a transmission. -void FGATC::NoRender(const string& refname) { - if(_playing) { - if(_voice) { -#ifdef ENABLE_AUDIO_SUPPORT - globals->get_soundmgr()->stop(refname); - globals->get_soundmgr()->remove(refname); -#endif - } - _playing = false; - } -} - -// Generate the text of a message from its parameters and the current context. -string FGATC::GenText(const string& m, int c) { - return(""); -} - -ostream& operator << (ostream& os, atc_type atc) { - switch(atc) { - case(INVALID): return(os << "INVALID"); - case(ATIS): return(os << "ATIS"); - case(GROUND): return(os << "GROUND"); - case(TOWER): return(os << "TOWER"); - case(APPROACH): return(os << "APPROACH"); - case(DEPARTURE): return(os << "DEPARTURE"); - case(ENROUTE): return(os << "ENROUTE"); - } - return(os << "ERROR - Unknown switch in atc_type operator << "); -} diff --git a/src/ATC/ATC.hxx b/src/ATC/ATC.hxx deleted file mode 100644 index 0ea9d9c91..000000000 --- a/src/ATC/ATC.hxx +++ /dev/null @@ -1,308 +0,0 @@ -// FGATC - abstract base class for the various actual atc classes -// such as FGATIS, FGTower etc. -// -// Written by David Luff, started Feburary 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifndef _FG_ATC_HXX -#define _FG_ATC_HXX - -#include -#include -#include -#include -#include - -#include STL_IOSTREAM -#include STL_STRING - -#include "ATCVoice.hxx" - -SG_USING_STD(ostream); -SG_USING_STD(string); -SG_USING_STD(ios); - -enum plane_type { - UNKNOWN, - GA_SINGLE, - GA_HP_SINGLE, - GA_TWIN, - GA_JET, - MEDIUM, - HEAVY, - MIL_JET -}; - -// PlaneRec - a structure holding ATC-centric details of planes under control -// This might move or change eventually -struct PlaneRec { - plane_type type; - string callsign; - int squawkcode; -}; - -// Possible types of ATC type that the radios may be tuned to. -// INVALID implies not tuned in to anything. -enum atc_type { - INVALID, - ATIS, - GROUND, - TOWER, - APPROACH, - DEPARTURE, - ENROUTE -}; - -const int ATC_NUM_TYPES = 7; - -// DCL - new experimental ATC data store -struct ATCData { - atc_type type; - // I've deliberately used float instead of double here to keep the size down - we'll be storing thousands of these in memory. - // In fact, we could probably ditch x, y and z and generate on the fly as needed. - // On the other hand, we'll probably end up reading this data directly from the DAFIF eventually anyway!! - float lon, lat, elev; - float x, y, z; - //int freq; - unsigned short int freq; - //int range; - unsigned short int range; - string ident; - string name; -}; - -// perhaps we could use an FGRunway instead of this. -// That wouldn't cache the orthopos though. -struct RunwayDetails { - Point3D threshold_pos; - Point3D end1ortho; // ortho projection end1 (the threshold ATM) - Point3D end2ortho; // ortho projection end2 (the take off end in the current hardwired scheme) - double hdg; // true runway heading - double length; // In *METERS* - double width; // ditto - string rwyID; - int patternDirection; // -1 for left, 1 for right -}; - -ostream& operator << (ostream& os, atc_type atc); - -class FGATC { - -public: - - FGATC(); - virtual ~FGATC(); - - // Run the internal calculations - // Derived classes should call this method from their own Update methods if they - // wish to use the response timer functionality. - virtual void Update(double dt); - - // Recieve a coded callback from the ATC menu system based on the user's selection - virtual void ReceiveUserCallback(int code); - - // Add plane to a stack - virtual void AddPlane(const string& pid); - - // Remove plane from stack - virtual int RemovePlane(); - - // Indicate that this instance should output to the display if appropriate - inline void SetDisplay() { _display = true; } - - // Indicate that this instance should not output to the display - inline void SetNoDisplay() { _display = false; } - - // Generate the text of a message from its parameters and the current context. - virtual string GenText(const string& m, int c); - - // Returns true if OK to transmit on this frequency - inline bool GetFreqClear() { return freqClear; } - // Indicate that the frequency is in use - inline void SetFreqInUse() { freqClear = false; receiving = true; } - // Transmission to the ATC is finished and a response is required - void SetResponseReqd(const string& rid); - // Transmission finished - let ATC decide if a response is reqd and clear freq if necessary - void NotifyTransmissionFinished(const string& rid); - // Transmission finished and no response required - inline void ReleaseFreq() { freqClear = true; receiving = false; } // TODO - check that the plane releasing the freq is the right one etc. - // The above 3 funcs under development!! - // The idea is that AI traffic or the user ATC dialog box calls FreqInUse() when they begin transmitting, - // and that the tower control sets freqClear back to true following a reply. - // AI traffic should check FreqClear() is true prior to transmitting. - // The user will just have to wait for a gap in dialog as in real life. - - // Return the type of ATC station that the class represents - inline atc_type GetType() { return _type; } - - // Set the core ATC data - void SetData(ATCData* d); - - inline double get_lon() const { return lon; } - inline void set_lon(const double ln) {lon = ln;} - inline double get_lat() const { return lat; } - inline void set_lat(const double lt) {lat = lt;} - inline double get_elev() const { return elev; } - inline void set_elev(const double ev) {elev = ev;} - inline double get_x() const { return x; } - inline void set_x(const double ecs) {x = ecs;} - inline double get_y() const { return y; } - inline void set_y(const double why) {y = why;} - inline double get_z() const { return z; } - inline void set_z(const double zed) {z = zed;} - inline int get_freq() const { return freq; } - inline void set_freq(const int fq) {freq = fq;} - inline int get_range() const { return range; } - inline void set_range(const int rg) {range = rg;} - inline const string& get_ident() { return ident; } - inline void set_ident(const string& id) { ident = id; } - inline const string& get_name() { return name; } - inline void set_name(const string& nm) { name = nm; } - -protected: - - // Render a transmission - // Outputs the transmission either on screen or as audio depending on user preference - // The refname is a string to identify this sample to the sound manager - // The repeating flag indicates whether the message should be repeated continuously or played once. - void Render(string& msg, const string& refname = "", bool repeating = false); - - // Cease rendering all transmission from this station. - // Requires the sound manager refname if audio, else "". - void NoRender(const string& refname); - - // Transmit a message when channel becomes free of other dialog - void Transmit(int callback_code = 0); - - // Transmit a message if channel becomes free within timeout (seconds). timeout of zero implies no limit - void ConditionalTransmit(double timeout, int callback_code = 0); - - // Transmit regardless of other dialog on the channel eg emergency - void ImmediateTransmit(int callback_code = 0); - - virtual void ProcessCallback(int code); - - double lon, lat, elev; - double x, y, z; - int freq; - int range; - string ident; // Code of the airport its at. - string name; // Name transmitted in the broadcast. - atc_type _type; - - // Rendering related stuff - bool _voice; // Flag - true if we are using voice - bool _playing; // Indicates a message in progress - bool _voiceOK; // Flag - true if at least one voice has loaded OK - FGATCVoice* _vPtr; - - string pending_transmission; // derived classes set this string before calling Transmit(...) - bool freqClear; // Flag to indicate if the frequency is clear of ongoing dialog - bool receiving; // Flag to indicate we are receiving a transmission - bool responseReqd; // Flag to indicate we should be responding to a request/report - bool runResponseCounter; // Flag to indicate the response counter should be run - double responseTime; // Time to take from end of request transmission to beginning of response - // The idea is that this will be slightly random. - double responseCounter; // counter to implement the above - string responseID; // ID of the plane to respond to - bool respond; // Flag to indicate now is the time to respond - ie set following the count down of the response timer. - // Derived classes only need monitor this flag, and use the response ID, as long as they call FGATC::Update(...) - bool _runReleaseCounter; // A timer for releasing the frequency after giving the message enough time to display - double _releaseTime; - double _releaseCounter; - - bool _display; // Flag to indicate whether we should be outputting to the ATC display. - bool _displaying; // Flag to indicate whether we are outputting to the ATC display. - -private: - // Transmission timing stuff. - bool _pending; - double _timeout; - int _callback_code; // A callback code to be notified and processed by the derived classes - // A value of zero indicates no callback required - bool _transmit; // we are to transmit - bool _transmitting; // we are transmitting - double _counter; - double _max_count; -}; - -inline istream& -operator >> ( istream& fin, ATCData& a ) -{ - double f; - char ch; - char tp; - - fin >> tp; - - switch(tp) { - case 'I': - a.type = ATIS; - break; - case 'T': - a.type = TOWER; - break; - case 'G': - a.type = GROUND; - break; - case 'A': - a.type = APPROACH; - break; - case '[': - a.type = INVALID; - return fin >> skipeol; - default: - SG_LOG(SG_GENERAL, SG_ALERT, "Warning - unknown type \'" << tp << "\' found whilst reading ATC frequency data!\n"); - a.type = INVALID; - return fin >> skipeol; - } - - fin >> a.lat >> a.lon >> a.elev >> f >> a.range - >> a.ident; - - a.name = ""; - fin >> ch; - if(ch != '"') a.name += ch; - while(1) { - //in >> noskipws - fin.unsetf(ios::skipws); - fin >> ch; - if((ch == '"') || (ch == 0x0A)) { - break; - } // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the " - a.name += ch; - } - fin.setf(ios::skipws); - //cout << "Comm name = " << a.name << '\n'; - - a.freq = (int)(f*100.0 + 0.5); - - // cout << a.ident << endl; - - // generate cartesian coordinates - Point3D geod( a.lon * SGD_DEGREES_TO_RADIANS, a.lat * SGD_DEGREES_TO_RADIANS, a.elev ); - Point3D cart = sgGeodToCart( geod ); - a.x = cart.x(); - a.y = cart.y(); - a.z = cart.z(); - - return fin >> skipeol; -} - - -#endif // _FG_ATC_HXX diff --git a/src/ATC/ATCDialog.cxx b/src/ATC/ATCDialog.cxx deleted file mode 100644 index 2946478da..000000000 --- a/src/ATC/ATCDialog.cxx +++ /dev/null @@ -1,424 +0,0 @@ -// ATCDialog.cxx - Functions and classes to handle the pop-up ATC dialog -// -// Written by Alexander Kappes and David Luff, started February 2003. -// -// Copyright (C) 2003 Alexander Kappes and David Luff -// -// 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. - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -#include - -#include
-#include // mkDialog -#include - -#include "ATCDialog.hxx" -#include "ATC.hxx" -#include "ATCmgr.hxx" -#include "commlist.hxx" -#include "ATCutils.hxx" -#include - -#include - -SG_USING_STD(ostringstream); - -FGATCDialog *current_atcdialog; - -// For the command manager - maybe eventually this should go in the built in command list -static bool do_ATC_dialog(const SGPropertyNode* arg) { - current_atcdialog->PopupDialog(); - return(true); -} - -static bool do_ATC_freq_search(const SGPropertyNode* arg) { - current_atcdialog->FreqDialog(); - return(true); -} - -ATCMenuEntry::ATCMenuEntry() { - stationid = ""; - //stationfr = 0; - transmission = ""; - menuentry = ""; - callback_code = 0; -} - -ATCMenuEntry::~ATCMenuEntry() { -} - -static void atcUppercase(string &s) { - for(unsigned int i=0; i... entry matches 'name' -static SGPropertyNode *getNamedNode(SGPropertyNode *prop, const char *name) { - SGPropertyNode* p; - - for (int i = 0; i < prop->nChildren(); i++) - if ((p = getNamedNode(prop->getChild(i), name))) - return p; - - if (!strcmp(prop->getStringValue("name"), name)) - return prop; - - return 0; -} - - -FGATCDialog::FGATCDialog() { - _callbackPending = false; - _callbackTimer = 0.0; - _callbackWait = 0.0; - _callbackPtr = NULL; - _callbackCode = 0; - _gui = (NewGUI *)globals->get_subsystem("gui"); -} - -FGATCDialog::~FGATCDialog() { -} - -void FGATCDialog::Init() { - // Add ATC-dialog to the command list - globals->get_commands()->addCommand("ATC-dialog", do_ATC_dialog); - // Add ATC-freq-search to the command list - globals->get_commands()->addCommand("ATC-freq-search", do_ATC_freq_search); - - // initialize properties polled in Update() - globals->get_props()->setStringValue("/sim/atc/freq-airport", ""); - globals->get_props()->setIntValue("/sim/atc/transmission-num", -1); -} - -void FGATCDialog::Update(double dt) { - static SGPropertyNode_ptr airport = globals->get_props()->getNode("/sim/atc/freq-airport", true); - string s = airport->getStringValue(); - if (!s.empty()) { - airport->setStringValue(""); - FreqDisplay(s); - } - - static 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); - } - - if(_callbackPending) { - if(_callbackTimer > _callbackWait) { - _callbackPtr->ReceiveUserCallback(_callbackCode); - _callbackPtr->NotifyTransmissionFinished(fgGetString("/sim/user/callsign")); - _callbackPending = false; - } else { - _callbackTimer += dt; - } - } -} - -// Add an entry -void FGATCDialog::add_entry(const string& station, const string& transmission, const string& menutext, atc_type type, int code) { - - ATCMenuEntry a; - - a.stationid = station; - a.transmission = transmission; - a.menuentry = menutext; - a.callback_code = code; - - (available_dialog[type])[station.c_str()].push_back(a); - -} - -void FGATCDialog::remove_entry( const string &station, const string &trans, atc_type type ) { - atcmentry_vec_type* p = &((available_dialog[type])[station]); - atcmentry_vec_iterator current = p->begin(); - while(current != p->end()) { - if(current->transmission == trans) current = p->erase(current); - else ++current; - } -} - -void FGATCDialog::remove_entry( const string &station, int code, atc_type type ) { - atcmentry_vec_type* p = &((available_dialog[type])[station]); - atcmentry_vec_iterator current = p->begin(); - while(current != p->end()) { - if(current->callback_code == code) current = p->erase(current); - else ++current; - } -} - -// query the database whether the transmission is already registered; -bool FGATCDialog::trans_reg( const string &station, const string &trans, atc_type type ) { - atcmentry_vec_type* p = &((available_dialog[type])[station]); - atcmentry_vec_iterator current = p->begin(); - for ( ; current != p->end() ; ++current ) { - if ( current->transmission == trans ) return true; - } - return false; -} - -// query the database whether the transmission is already registered; -bool FGATCDialog::trans_reg( const string &station, int code, atc_type type ) { - atcmentry_vec_type* p = &((available_dialog[type])[station]); - atcmentry_vec_iterator current = p->begin(); - for ( ; current != p->end() ; ++current ) { - if ( current->callback_code == code ) return true; - } - return false; -} - -// Display the ATC popup dialog box with options relevant to the users current situation. -void FGATCDialog::PopupDialog() { - const char *dialog_name = "atc-dialog"; - SGPropertyNode_ptr dlg = _gui->getDialogProperties(dialog_name); - if (!dlg) - return; - - _gui->closeDialog(dialog_name); - - SGPropertyNode_ptr button_group = getNamedNode(dlg, "transmission-choice"); - // remove all transmission buttons - button_group->removeChildren("button", false); - - string label; - FGATC* atcptr = globals->get_ATC_mgr()->GetComm1ATCPointer(); // Hardwired to comm1 at the moment - - if (!atcptr) { - label = "Not currently tuned to any ATC service"; - mkDialog(label.c_str()); - return; - } - - if(atcptr->GetType() == ATIS) { - label = "Tuned to ATIS - no communication possible"; - mkDialog(label.c_str()); - return; - } - - atcmentry_vec_type atcmlist = (available_dialog[atcptr->GetType()])[atcptr->get_ident()]; - atcmentry_vec_iterator current = atcmlist.begin(); - atcmentry_vec_iterator last = atcmlist.end(); - - if(!atcmlist.size()) { - label = "No transmission available"; - mkDialog(label.c_str()); - return; - } - - const int bufsize = 32; - char buf[bufsize]; - // loop over all entries in atcmentrylist - for (int n = 0; n < 10; ++n) { - snprintf(buf, bufsize, "/sim/atc/opt[%d]", n); - fgSetBool(buf, false); - - if (current == last) - continue; - - // add transmission button (modified copy of ) - SGPropertyNode *entry = button_group->getNode("button", n, true); - copyProperties(button_group->getNode("button-template", true), entry); - entry->removeChildren("hide", false); - entry->setStringValue("property", buf); - entry->setIntValue("keynum", '1' + n); - if (n == 0) - entry->setBoolValue("default", true); - - snprintf(buf, bufsize, "%d", n + 1); - string legend = string(buf) + ". " + current->menuentry; - entry->setStringValue("legend", legend.c_str()); - entry->setIntValue("binding/value", n); - current++; - } - - _gui->showDialog(dialog_name); - return; -} - -void FGATCDialog::PopupCallback(int num) { - FGATC* atcptr = globals->get_ATC_mgr()->GetComm1ATCPointer(); // FIXME - Hardwired to comm1 at the moment - - if (!atcptr) - return; - - if (atcptr->GetType() == TOWER) { - //cout << "TOWER " << endl; - //cout << "ident is " << atcptr->get_ident() << endl; - atcmentry_vec_type atcmlist = (available_dialog[TOWER])[atcptr->get_ident()]; - unsigned int size = atcmlist.size(); - if(size && num < size) { - //cout << "Doing callback...\n"; - ATCMenuEntry a = atcmlist[num]; - atcptr->SetFreqInUse(); - string pilot = atcptr->GenText(a.transmission, a.callback_code); - fgSetString("/sim/messages/pilot", pilot.c_str()); - // This is the user's speech getting displayed. - _callbackPending = true; - _callbackTimer = 0.0; - _callbackWait = 5.0; - _callbackPtr = atcptr; - _callbackCode = a.callback_code; - } else { - //cout << "No options available...\n"; - } - //cout << "Donded" << endl; - } -} - -// map() key data type (removes duplicates and sorts by distance) -struct atcdata { - atcdata() {} - atcdata(const string i, const string n, const double d) { - id = i, name = n, distance = d; - } - bool operator<(const atcdata& a) const { - return id != a.id && distance < a.distance; - } - bool operator==(const atcdata& a) const { - return id == a.id && distance == a.distance; - } - string id; - string name; - double distance; -}; - -void FGATCDialog::FreqDialog() { - const char *dialog_name = "atc-freq-search"; - SGPropertyNode_ptr dlg = _gui->getDialogProperties(dialog_name); - if (!dlg) - return; - - _gui->closeDialog(dialog_name); - - SGPropertyNode_ptr button_group = getNamedNode(dlg, "quick-buttons"); - // remove all dynamic airport/ATC buttons - button_group->removeChildren("button", false); - - // Find the ATC stations within a reasonable range - comm_list_type atc_stations; - comm_list_iterator atc_stat_itr; - - double lon = fgGetDouble("/position/longitude-deg"); - double lat = fgGetDouble("/position/latitude-deg"); - double elev = fgGetDouble("/position/altitude-ft"); - Point3D aircraft = sgGeodToCart(Point3D(lon * SGD_DEGREES_TO_RADIANS, - lat * SGD_DEGREES_TO_RADIANS, elev)); - - // search stations in range - int num_stat = current_commlist->FindByPos(lon, lat, elev, 50.0, &atc_stations); - if (num_stat != 0) { - map uniq; - // fill map (sorts by distance and removes duplicates) - comm_list_iterator itr = atc_stations.begin(); - for (; itr != atc_stations.end(); ++itr) { - Point3D station = Point3D(itr->x, itr->y, itr->z); - double distance = aircraft.distance3Dsquared(station); - uniq[atcdata(itr->ident, itr->name, distance)] = true; - } - // create button per map entry (modified copy of ) - map::iterator uit = uniq.begin(); - for (int n = 0; uit != uniq.end() && n < 6; ++uit, ++n) { // max 6 buttons - SGPropertyNode *entry = button_group->getNode("button", n, true); - copyProperties(button_group->getNode("button-template", true), entry); - entry->removeChildren("hide", false); - entry->setStringValue("legend", uit->first.id.c_str()); - entry->setStringValue("binding[0]/value", uit->first.id.c_str()); - } - } - - // (un)hide message saying no things in range - SGPropertyNode_ptr range_error = getNamedNode(dlg, "no-atc-in-range"); - range_error->setBoolValue("hide", num_stat); - - _gui->showDialog(dialog_name); -} - -void FGATCDialog::FreqDisplay(string& ident) { - const char *dialog_name = "atc-freq-display"; - SGPropertyNode_ptr dlg = _gui->getDialogProperties(dialog_name); - if (!dlg) - return; - - _gui->closeDialog(dialog_name); - - SGPropertyNode_ptr freq_group = getNamedNode(dlg, "frequency-list"); - // remove all frequency entries - freq_group->removeChildren("group", false); - - atcUppercase(ident); - string label; - - const FGAirport *a = fgFindAirportID(ident); - if (!a) { - label = "Airport " + ident + " not found in database."; - mkDialog(label.c_str()); - return; - } - - // set title - label = ident + " Frequencies"; - dlg->setStringValue("text/label", label.c_str()); - - int n = 0; // Number of ATC frequencies at this airport - - comm_list_type stations; - int found = current_commlist->FindByPos(a->getLongitude(), a->getLatitude(), a->getElevation(), 20.0, &stations); - if(found) { - ostringstream ostr; - comm_list_iterator itr = stations.begin(); - for (n = 0; itr != stations.end(); ++itr) { - if(itr->ident != ident) - continue; - - if(itr->type == INVALID) - continue; - - // add frequency line (modified copy of ) - SGPropertyNode *entry = freq_group->getNode("group", n, true); - copyProperties(freq_group->getNode("group-template", true), entry); - entry->removeChildren("hide", false); - - ostr << itr->type; - entry->setStringValue("text[0]/label", ostr.str().c_str()); - - char buf[8]; - snprintf(buf, 8, "%.2f", (itr->freq / 100.0)); // Convert from KHz to MHz - if(buf[5] == '3') buf[5] = '2'; - if(buf[5] == '8') buf[5] = '7'; - buf[7] = '\0'; - - entry->setStringValue("text[1]/label", buf); - - ostr.seekp(0); - n++; - } - } - if(n == 0) { - label = "No frequencies found for airport " + ident; - mkDialog(label.c_str()); - return; - } - - _gui->showDialog(dialog_name); -} - diff --git a/src/ATC/ATCDialog.hxx b/src/ATC/ATCDialog.hxx deleted file mode 100644 index 76cf2cc9f..000000000 --- a/src/ATC/ATCDialog.hxx +++ /dev/null @@ -1,115 +0,0 @@ -// ATCDialog.hxx - Functions and classes to handle the pop-up ATC dialog -// -// Written by Alexander Kappes and David Luff, started February 2003. -// -// Copyright (C) 2003 Alexander Kappes and David Luff -// -// 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. - -#ifndef ATC_DIALOG_HXX -#define ATC_DIALOG_HXX - -#include - -#include -#include - -#include "ATC.hxx" - -SG_USING_STD(vector); -SG_USING_STD(map); - -class NewGUI; - -// ATCMenuEntry - an encapsulation of an entry in the ATC dialog -struct ATCMenuEntry { - - string stationid; // ID of transmitting station - //int stationfr; // ? - string transmission; // Actual speech of transmission - string menuentry; // Shortened version for display in the dialog - int callback_code; // This code is supplied by the registering station, and then - // returned to the registering station if that option is chosen. - // The actual value is only understood by the registering station - - // FGATCDialog only stores it and returns it if appropriate. - - ATCMenuEntry(); - ~ATCMenuEntry(); -}; - -typedef vector < ATCMenuEntry > atcmentry_vec_type; -typedef atcmentry_vec_type::iterator atcmentry_vec_iterator; - -typedef map < string, atcmentry_vec_type > atcmentry_map_type; -typedef atcmentry_map_type::iterator atcmentry_map_iterator; - -//void ATCDialogInit(); - -//void ATCDoDialog(atc_type type); - -class FGATCDialog { - -public: - - FGATCDialog(); - ~FGATCDialog(); - - void Init(); - - void Update(double dt); - - void PopupDialog(); - - void PopupCallback(int); - - void add_entry( const string& station, const string& transmission, const string& menutext, atc_type type, int code); - - void remove_entry( const string &station, const string &trans, atc_type type ); - - void remove_entry( const string &station, int code, atc_type type ); - - // query the database whether the transmission is already registered; - bool trans_reg( const string &station, const string &trans, atc_type type ); - - // query the database whether the transmission is already registered; - bool trans_reg( const string &station, int code, atc_type type ); - - // Display a frequency search dialog for nearby stations - void FreqDialog(); - - // Display the comm ATC frequencies for airport ident - // where ident is a valid ICAO code. - void FreqDisplay(string& ident); - -private: - - atcmentry_map_type available_dialog[ATC_NUM_TYPES]; - - int freq; - bool reset; - - bool _callbackPending; - double _callbackTimer; - double _callbackWait; - FGATC* _callbackPtr; - int _callbackCode; - - NewGUI *_gui; -}; - -extern FGATCDialog *current_atcdialog; - -#endif // ATC_DIALOG_HXX - diff --git a/src/ATC/ATCProjection.cxx b/src/ATC/ATCProjection.cxx deleted file mode 100644 index 7028004c8..000000000 --- a/src/ATC/ATCProjection.cxx +++ /dev/null @@ -1,118 +0,0 @@ -// ATCProjection.cxx - A convienience projection class for the ATC/AI system. -// -// Written by David Luff, started 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include "ATCProjection.hxx" -#include -#include - -FGATCProjection::FGATCProjection() { - _origin.setlat(0.0); - _origin.setlon(0.0); - _origin.setelev(0.0); - _correction_factor = cos(_origin.lat() * SG_DEGREES_TO_RADIANS); -} - -FGATCProjection::FGATCProjection(const Point3D& centre) { - _origin = centre; - _correction_factor = cos(_origin.lat() * SG_DEGREES_TO_RADIANS); -} - -FGATCProjection::~FGATCProjection() { -} - -void FGATCProjection::Init(const Point3D& centre) { - _origin = centre; - _correction_factor = cos(_origin.lat() * SG_DEGREES_TO_RADIANS); -} - -Point3D FGATCProjection::ConvertToLocal(const Point3D& pt) { - double delta_lat = pt.lat() - _origin.lat(); - double delta_lon = pt.lon() - _origin.lon(); - - double y = sin(delta_lat * SG_DEGREES_TO_RADIANS) * SG_EQUATORIAL_RADIUS_M; - double x = sin(delta_lon * SG_DEGREES_TO_RADIANS) * SG_EQUATORIAL_RADIUS_M * _correction_factor; - - return(Point3D(x,y,0.0)); -} - -Point3D FGATCProjection::ConvertFromLocal(const Point3D& pt) { - double delta_lat = asin(pt.y() / SG_EQUATORIAL_RADIUS_M) * SG_RADIANS_TO_DEGREES; - double delta_lon = (asin(pt.x() / SG_EQUATORIAL_RADIUS_M) * SG_RADIANS_TO_DEGREES) / _correction_factor; - - return(Point3D(_origin.lon()+delta_lon, _origin.lat()+delta_lat, 0.0)); -} - -/**********************************************************************************/ - -FGATCAlignedProjection::FGATCAlignedProjection() { - _origin.setlat(0.0); - _origin.setlon(0.0); - _origin.setelev(0.0); - _correction_factor = cos(_origin.lat() * SG_DEGREES_TO_RADIANS); -} - -FGATCAlignedProjection::FGATCAlignedProjection(const Point3D& centre, double heading) { - _origin = centre; - _theta = heading * SG_DEGREES_TO_RADIANS; - _correction_factor = cos(_origin.lat() * SG_DEGREES_TO_RADIANS); -} - -FGATCAlignedProjection::~FGATCAlignedProjection() { -} - -void FGATCAlignedProjection::Init(const Point3D& centre, double heading) { - _origin = centre; - _theta = heading * SG_DEGREES_TO_RADIANS; - _correction_factor = cos(_origin.lat() * SG_DEGREES_TO_RADIANS); -} - -Point3D FGATCAlignedProjection::ConvertToLocal(const Point3D& pt) { - // convert from lat/lon to orthogonal - double delta_lat = pt.lat() - _origin.lat(); - double delta_lon = pt.lon() - _origin.lon(); - double y = sin(delta_lat * SG_DEGREES_TO_RADIANS) * SG_EQUATORIAL_RADIUS_M; - double x = sin(delta_lon * SG_DEGREES_TO_RADIANS) * SG_EQUATORIAL_RADIUS_M * _correction_factor; - - // Align - if(_theta != 0.0) { - double xbar = x; - x = x*cos(_theta) - y*sin(_theta); - y = (xbar*sin(_theta)) + (y*cos(_theta)); - } - - return(Point3D(x,y,pt.elev())); -} - -Point3D FGATCAlignedProjection::ConvertFromLocal(const Point3D& pt) { - // de-align - double thi = _theta * -1.0; - double x = pt.x()*cos(thi) - pt.y()*sin(thi); - double y = (pt.x()*sin(thi)) + (pt.y()*cos(thi)); - - // convert from orthogonal to lat/lon - double delta_lat = asin(y / SG_EQUATORIAL_RADIUS_M) * SG_RADIANS_TO_DEGREES; - double delta_lon = (asin(x / SG_EQUATORIAL_RADIUS_M) * SG_RADIANS_TO_DEGREES) / _correction_factor; - - return(Point3D(_origin.lon()+delta_lon, _origin.lat()+delta_lat, pt.elev())); -} diff --git a/src/ATC/ATCProjection.hxx b/src/ATC/ATCProjection.hxx deleted file mode 100644 index faf8eb395..000000000 --- a/src/ATC/ATCProjection.hxx +++ /dev/null @@ -1,73 +0,0 @@ -// ATCProjection.hxx - A convienience projection class for the ATC/AI system. -// -// Written by David Luff, started 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifndef _FG_ATC_PROJECTION_HXX -#define _FG_ATC_PROJECTION_HXX - -#include - -// FGATCProjection - a class to project an area local to an airport onto an orthogonal co-ordinate system -class FGATCProjection { - -public: - FGATCProjection(); - FGATCProjection(const Point3D& centre); - ~FGATCProjection(); - - void Init(const Point3D& centre); - - // Convert a lat/lon co-ordinate (degrees) to the local projection (meters) - Point3D ConvertToLocal(const Point3D& pt); - - // Convert a local projection co-ordinate (meters) to lat/lon (degrees) - Point3D ConvertFromLocal(const Point3D& pt); - -private: - Point3D _origin; // lat/lon of local area origin - double _correction_factor; // Reduction in surface distance per degree of longitude due to latitude. Saves having to do a cos() every call. - -}; - - -// FGATCAlignedProjection - a class to project an area local to a runway onto an orthogonal co-ordinate system -// with the origin at the threshold and the runway aligned with the y axis. -class FGATCAlignedProjection { - -public: - FGATCAlignedProjection(); - FGATCAlignedProjection(const Point3D& centre, double heading); - ~FGATCAlignedProjection(); - - void Init(const Point3D& centre, double heading); - - // Convert a lat/lon co-ordinate (degrees) to the local projection (meters) - Point3D ConvertToLocal(const Point3D& pt); - - // Convert a local projection co-ordinate (meters) to lat/lon (degrees) - Point3D ConvertFromLocal(const Point3D& pt); - -private: - Point3D _origin; // lat/lon of local area origin (the threshold) - double _theta; // the rotation angle for alignment in radians - double _correction_factor; // Reduction in surface distance per degree of longitude due to latitude. Saves having to do a cos() every call. - -}; - -#endif // _FG_ATC_PROJECTION_HXX diff --git a/src/ATC/ATCVoice.cxx b/src/ATC/ATCVoice.cxx deleted file mode 100644 index 682cd24a4..000000000 --- a/src/ATC/ATCVoice.cxx +++ /dev/null @@ -1,191 +0,0 @@ -// FGATCVoice.cxx - a class to encapsulate an ATC voice -// -// Written by David Luff, started November 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// 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. - - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include -#include -#include
- -#include "ATCVoice.hxx" - -#include - -FGATCVoice::FGATCVoice() { - SoundData = 0; - rawSoundData = 0; -} - -FGATCVoice::~FGATCVoice() { - if (rawSoundData) - free( rawSoundData ); - delete SoundData; -} - -// Load the two voice files - one containing the raw sound data (.wav) and one containing the word positions (.vce). -// Return true if successful. -bool FGATCVoice::LoadVoice(const string& voice) { - // FIXME CLO: disabled to try to see if this is causign problemcs - // return false; - - ifstream fin; - - SGPath path = globals->get_fg_root(); - path.append( "ATC" ); - - string file = voice + ".wav"; - - SoundData = new SGSoundSample(); - rawSoundData = (char *)SoundData->load_file(path.c_str(), file.c_str()); - rawDataSize = SoundData->get_size(); - - path = globals->get_fg_root(); - string wordPath = "ATC/" + voice + ".vce"; - path.append(wordPath); - - // Now load the word data - fin.open(path.c_str(), ios::in); - if(!fin) { - SG_LOG(SG_ATC, SG_ALERT, "Unable to open input file " << path.c_str()); - return(false); - } - SG_LOG(SG_ATC, SG_INFO, "Opened word data file " << wordPath << " OK..."); - char numwds[10]; - char wrd[100]; - string wrdstr; - char wrdOffsetStr[20]; - char wrdLengthStr[20]; - unsigned int wrdOffset; // Offset into the raw sound data that the word sample begins - unsigned int wrdLength; // Length of the word sample in bytes - WordData wd; - fin >> numwds; - unsigned int numwords = atoi(numwds); - //cout << numwords << '\n'; - for(unsigned int i=0; i < numwords; ++i) { - fin >> wrd; - wrdstr = wrd; - fin >> wrdOffsetStr; - fin >> wrdLengthStr; - wrdOffset = atoi(wrdOffsetStr); - wrdLength = atoi(wrdLengthStr); - wd.offset = wrdOffset; - wd.length = wrdLength; - wordMap[wrdstr] = wd; - //cout << wrd << "\t\t" << wrdOffset << "\t\t" << wrdLength << '\n'; - //cout << i << '\n'; - } - - fin.close(); - return(true); -} - - -typedef list < string > tokenList_type; -typedef tokenList_type::iterator tokenList_iterator; - -// Given a desired message, return a pointer to the data buffer and write the buffer length into len. -unsigned char* FGATCVoice::WriteMessage(char* message, int& len, bool& dataOK) { - - // What should we do here? - // First - parse the message into a list of tokens. - // Sort the tokens into those we understand and those we don't. - // Add all the raw lengths of the token sound data, allocate enough space, and fill it with the rqd data. - tokenList_type tokenList; - tokenList_iterator tokenListItr; - - // TODO - at the moment we're effectively taking 3 passes through the data. - // There is no need for this - 2 should be sufficient - we can probably ditch the tokenList. - char* token; - char mes[1000]; - int numWords = 0; - strcpy(mes, message); - const char delimiters[] = " \t.,;:\""; - token = strtok(mes, delimiters); - while(token != NULL) { - tokenList.push_back(token); - ++numWords; - //cout << "token = " << token << '\n'; - token = strtok(NULL, delimiters); - } - - WordData* wdptr = new WordData[numWords]; - int word = 0; - unsigned int cumLength = 0; - - tokenListItr = tokenList.begin(); - while(tokenListItr != tokenList.end()) { - if(wordMap.find(*tokenListItr) == wordMap.end()) { - // Oh dear - the token isn't in the sound file - //cout << "word " << *tokenListItr << " not found :-(\n"; - } else { - wdptr[word] = wordMap[*tokenListItr]; - cumLength += wdptr[word].length; - //cout << *tokenListItr << " found at offset " << wdptr[word].offset << " with length " << wdptr[word].length << endl; - word++; - } - ++tokenListItr; - } - - // Check for no tokens found else slScheduler can be crashed - if(!word) { - dataOK = false; - return(NULL); - } - - unsigned char* tmpbuf = new unsigned char[cumLength]; - unsigned char* outbuf = new unsigned char[cumLength]; - len = cumLength; - unsigned int bufpos = 0; - for(int i=0; i rawDataSize) { - SG_LOG(SG_ATC, SG_ALERT, "ERROR - mismatch between ATC .wav and .vce file in ATCVoice.cxx\n"); - SG_LOG(SG_ATC, SG_ALERT, "Offset + length: " << wdptr[i].offset + wdptr[i].length - << " exceeds rawdata size: " << rawDataSize << endl); - delete[] wdptr; - dataOK = false; - return(NULL); - } - memcpy(tmpbuf + bufpos, rawSoundData + wdptr[i].offset, wdptr[i].length); - bufpos += wdptr[i].length; - } - - // tmpbuf now contains the message starting at the beginning - but we want it to start at a random position. - unsigned int offsetIn = (int)(cumLength * sg_random()); - if(offsetIn > cumLength) offsetIn = cumLength; - memcpy(outbuf, tmpbuf + offsetIn, (cumLength - offsetIn)); - memcpy(outbuf + (cumLength - offsetIn), tmpbuf, offsetIn); - - delete[] tmpbuf; - delete[] wdptr; - - dataOK = true; - return(outbuf); -} diff --git a/src/ATC/ATCVoice.hxx b/src/ATC/ATCVoice.hxx deleted file mode 100644 index a5ea59394..000000000 --- a/src/ATC/ATCVoice.hxx +++ /dev/null @@ -1,87 +0,0 @@ -// FGATCVoice.hxx - a class to encapsulate an ATC voice -// -// Written by David Luff, started November 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifndef _FG_ATC_VOICE -#define _FG_ATC_VOICE - -#include - -#if defined( SG_HAVE_STD_INCLUDES ) || defined( __BORLANDC__ ) || (__APPLE__) -# include -# include -#else -# include -# include -#endif - -#include -#include -#include - -#include - -SG_USING_STD(map); -SG_USING_STD(list); -SG_USING_STD(string); - -SG_USING_STD(cout); -SG_USING_STD(ios); -SG_USING_STD(ofstream); -SG_USING_STD(ifstream); - - -struct WordData { - unsigned int offset; // Offset of beginning of word sample into raw sound sample - unsigned int length; // Byte length of word sample -}; - -typedef map < string, WordData > atc_word_map_type; -typedef atc_word_map_type::iterator atc_word_map_iterator; -typedef atc_word_map_type::const_iterator atc_word_map_const_iterator; - -class FGATCVoice { - -public: - - FGATCVoice(); - ~FGATCVoice(); - - // Load the two voice files - one containing the raw sound data (.wav) and one containing the word positions (.vce). - // Return true if successful. - bool LoadVoice(const string& voice); - - // Given a desired message, return a pointer to the data buffer and write the buffer length into len. - // Sets dataOK = true if the returned buffer is valid. - unsigned char* WriteMessage(char* message, int& len, bool& dataOK); - - -private: - - // the sound and word position data - char* rawSoundData; - unsigned int rawDataSize; - SGSoundSample *SoundData; - - // A map of words vs. byte position and length in rawSoundData - atc_word_map_type wordMap; - -}; - -#endif // _FG_ATC_VOICE diff --git a/src/ATC/ATCmgr.cxx b/src/ATC/ATCmgr.cxx deleted file mode 100644 index bbf99dfa6..000000000 --- a/src/ATC/ATCmgr.cxx +++ /dev/null @@ -1,723 +0,0 @@ -// ATCmgr.cxx - Implementation of FGATCMgr - a global Flightgear ATC manager. -// -// Written by David Luff, started February 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include - -#include "ATCmgr.hxx" -#include "commlist.hxx" -#include "ATCDialog.hxx" -#include "ATCutils.hxx" -#include "transmissionlist.hxx" -#include "ground.hxx" - - -/* -// periodic radio station search wrapper -static void fgATCSearch( void ) { - globals->get_ATC_mgr()->Search(); -} -*/ //This wouldn't compile - including Time/event.hxx breaks it :-( - // Is this still true?? -EMH- - -AirportATC::AirportATC() : - lon(0.0), - lat(0.0), - elev(0.0), - atis_freq(0.0), - atis_active(false), - tower_freq(0.0), - tower_active(false), - ground_freq(0.0), - ground_active(false), - set_by_AI(false), - numAI(0) - //airport_atc_map.clear(); -{ - for(int i=0; iget_event_mgr()->add( "fgATCSearch()", fgATCSearch, - // FGEvent::FG_EVENT_READY, 800); - // - // For some reason the above doesn't compile - including Time/event.hxx stops compilation. - // Is this still true after the reorganization of the event managar?? - // -EMH- - - // Initialise the frequency search map - current_commlist = new FGCommList; - SGPath p_comm( globals->get_fg_root() ); - current_commlist->init( p_comm ); - -#ifdef ENABLE_AUDIO_SUPPORT - // Load all available voices. - // For now we'll do one hardwired one - - v1 = new FGATCVoice; - try { - voiceOK = v1->LoadVoice("default"); - voice = true; - } catch ( sg_io_exception & ) { - voiceOK = false; - voice = false; - delete v1; - v1 = 0; - } - - /* I've loaded the voice even if /sim/sound/pause is true - * since I know no way of forcing load of the voice if the user - * subsequently switches /sim/sound/audible to true. - * (which is the right thing to do -- CLO) :-) */ -#else - voice = false; -#endif - - // Initialise the ATC Dialogs - //cout << "Initing Transmissions..." << endl; - SG_LOG(SG_ATC, SG_INFO, " ATC Transmissions"); - current_transmissionlist = new FGTransmissionList; - SGPath p_transmission( globals->get_fg_root() ); - p_transmission.append( "ATC/default.transmissions" ); - current_transmissionlist->init( p_transmission ); - //cout << "Done Transmissions" << endl; - - SG_LOG(SG_ATC, SG_INFO, " ATC Dialog System"); - current_atcdialog = new FGATCDialog; - current_atcdialog->Init(); - - initDone = true; - //cout << "ATCmgr::init done!" << endl; -} - -void FGATCMgr::update(double dt) { - if(!initDone) { - init(); - SG_LOG(SG_ATC, SG_WARN, "Warning - ATCMgr::update(...) called before ATCMgr::init()"); - } - - current_atcdialog->Update(dt); - - //cout << "Entering update..." << endl; - //Traverse the list of active stations. - //Only update one class per update step to avoid the whole ATC system having to calculate between frames. - //Eventually we should only update every so many steps. - //cout << "In FGATCMgr::update - atc_list.size = " << atc_list.size() << endl; - if(atc_list.size()) { - if(atc_list_itr == atc_list.end()) { - atc_list_itr = atc_list.begin(); - } - //cout << "Updating " << (*atc_list_itr)->get_ident() << ' ' << (*atc_list_itr)->GetType() << '\n'; - //cout << "Freq = " << (*atc_list_itr)->get_freq() << '\n'; - (*atc_list_itr)->Update(dt * atc_list.size()); - //cout << "Done ATC update..." << endl; - ++atc_list_itr; - } - - /* - cout << "ATC_LIST: " << atc_list.size() << ' '; - for(atc_list_iterator it = atc_list.begin(); it != atc_list.end(); it++) { - cout << (*it)->get_ident() << ' '; - } - cout << '\n'; - */ - - // Search the tuned frequencies every now and then - this should be done with the event scheduler - static int i = 0; // Very ugly - but there should only ever be one instance of FGATCMgr. - /* - if(i == 7) { - //cout << "About to AreaSearch()" << endl; - AreaSearch(); - } - */ - if(i == 15) { - //cout << "About to search(1)" << endl; - FreqSearch(1); - } - if(i == 30) { - //cout << "About to search(2)" << endl; - FreqSearch(2); - i = 0; - } - ++i; - - //cout << "comm1 type = " << comm_type[0] << '\n'; - //cout << "Leaving update..." << endl; -} - - -// Returns frequency in KHz - should I alter this to return in MHz? -unsigned short int FGATCMgr::GetFrequency(const string& ident, const atc_type& tp) { - ATCData test; - bool ok = current_commlist->FindByCode(ident, test, tp); - return(ok ? test.freq : 0); -} - - -// Register the fact that the AI system wants to activate an airport -// Might need more sophistication in this in the future - eg registration by aircraft call-sign. -bool FGATCMgr::AIRegisterAirport(const string& ident) { - SG_LOG(SG_ATC, SG_BULK, "AI registered airport " << ident << " with the ATC system"); - //cout << "AI registered airport " << ident << " with the ATC system" << '\n'; - if(airport_atc_map.find(ident) != airport_atc_map.end()) { - airport_atc_map[ident]->set_by_AI = true; - airport_atc_map[ident]->numAI++; - return(true); - } else { - const FGAirport *ap = fgFindAirportID(ident); - if (ap) { - //cout << "ident = " << ident << '\n'; - AirportATC *a = new AirportATC; - // I'm not entirely sure that this AirportATC structure business is actually needed - it just duplicates what we can find out anyway! - a->lon = ap->getLongitude(); - a->lat = ap->getLatitude(); - a->elev = ap->getElevation(); - a->atis_freq = GetFrequency(ident, ATIS); - //cout << "ATIS freq = " << a->atis_freq << '\n'; - a->atis_active = false; - a->tower_freq = GetFrequency(ident, TOWER); - //cout << "Tower freq = " << a->tower_freq << '\n'; - a->tower_active = false; - a->ground_freq = GetFrequency(ident, GROUND); - //cout << "Ground freq = " << a->ground_freq << '\n'; - a->ground_active = false; - // TODO - some airports will have a tower/ground frequency but be inactive overnight. - a->set_by_AI = true; - a->numAI = 1; - airport_atc_map[ident] = a; - return(true); - } else { - SG_LOG(SG_ATC, SG_ALERT, "ERROR - can't find airport " << ident << " in AIRegisterAirport(...)"); - } - } - return(false); -} - - -// Register the fact that the comm radio is tuned to an airport -// Channel is zero based -bool FGATCMgr::CommRegisterAirport(const string& ident, int chan, const atc_type& tp) { - SG_LOG(SG_ATC, SG_BULK, "Comm channel " << chan << " registered airport " << ident); - //cout << "Comm channel " << chan << " registered airport " << ident << ' ' << tp << '\n'; - if(airport_atc_map.find(ident) != airport_atc_map.end()) { - //cout << "IN MAP - flagging set by comm..." << endl; - airport_atc_map[ident]->set_by_comm[chan][tp] = true; - if(tp == ATIS) { - airport_atc_map[ident]->atis_active = true; - } else if(tp == TOWER) { - airport_atc_map[ident]->tower_active = true; - } else if(tp == GROUND) { - airport_atc_map[ident]->ground_active = true; - } else if(tp == APPROACH) { - //a->approach_active = true; - } // TODO - there *must* be a better way to do this!!! - return(true); - } else { - //cout << "NOT IN MAP - creating new..." << endl; - const FGAirport *ap = fgFindAirportID(ident); - if (ap) { - AirportATC *a = new AirportATC; - // I'm not entirely sure that this AirportATC structure business is actually needed - it just duplicates what we can find out anyway! - a->lon = ap->getLongitude(); - a->lat = ap->getLatitude(); - a->elev = ap->getElevation(); - a->atis_freq = GetFrequency(ident, ATIS); - a->atis_active = false; - a->tower_freq = GetFrequency(ident, TOWER); - a->tower_active = false; - a->ground_freq = GetFrequency(ident, GROUND); - a->ground_active = false; - if(tp == ATIS) { - a->atis_active = true; - } else if(tp == TOWER) { - a->tower_active = true; - } else if(tp == GROUND) { - a->ground_active = true; - } else if(tp == APPROACH) { - //a->approach_active = true; - } // TODO - there *must* be a better way to do this!!! - // TODO - some airports will have a tower/ground frequency but be inactive overnight. - a->set_by_AI = false; - a->numAI = 0; - a->set_by_comm[chan][tp] = true; - airport_atc_map[ident] = a; - return(true); - } - } - return(false); -} - - -// Remove from list only if not needed by the AI system or the other comm channel -// Note that chan is zero based. -void FGATCMgr::CommRemoveFromList(const string& id, const atc_type& tp, int chan) { - SG_LOG(SG_ATC, SG_BULK, "CommRemoveFromList called for airport " << id << " " << tp << " by channel " << chan); - //cout << "CommRemoveFromList called for airport " << id << " " << tp << " by channel " << chan << '\n'; - if(airport_atc_map.find(id) != airport_atc_map.end()) { - AirportATC* a = airport_atc_map[id]; - //cout << "In CommRemoveFromList, a->ground_freq = " << a->ground_freq << endl; - if(a->set_by_AI && tp != ATIS) { - // Set by AI, so don't remove simply because user isn't tuned in any more - just stop displaying - SG_LOG(SG_ATC, SG_BULK, "In CommRemoveFromList, service was set by AI\n"); - FGATC* aptr = GetATCPointer(id, tp); - switch(chan) { - case 0: - //cout << "chan 1\n"; - a->set_by_comm[0][tp] = false; - if(!a->set_by_comm[1][tp]) { - //cout << "not set by comm2\n"; - if(aptr != NULL) { - //cout << "Got pointer\n"; - aptr->SetNoDisplay(); - //cout << "Setting no display...\n"; - } else { - //cout << "Not got pointer\n"; - } - } - break; - case 1: - a->set_by_comm[1][tp] = false; - if(!a->set_by_comm[0][tp]) { - if(aptr != NULL) { - aptr->SetNoDisplay(); - //cout << "Setting no display...\n"; - } - } - break; - } - //airport_atc_map[id] = a; - return; - } else { - switch(chan) { - case 0: - a->set_by_comm[0][tp] = false; - // Remove only if not also set by the other comm channel - if(!a->set_by_comm[1][tp]) { - a->tower_active = false; - a->ground_active = false; - RemoveFromList(id, tp); - } - break; - case 1: - a->set_by_comm[1][tp] = false; - if(!a->set_by_comm[0][tp]) { - a->tower_active = false; - a->ground_active = false; - RemoveFromList(id, tp); - } - break; - } - } - } -} - - -// Remove from list - should only be called from above or similar -// This function *will* remove it from the list regardless of who else might want it. -void FGATCMgr::RemoveFromList(const string& id, const atc_type& tp) { - //cout << "FGATCMgr::RemoveFromList called..." << endl; - //cout << "Requested type = " << tp << endl; - //cout << "id = " << id << endl; - atc_list_iterator it = atc_list.begin(); - while(it != atc_list.end()) { - //cout << "type = " << (*it)->GetType() << '\n'; - //cout << "Ident = " << (*it)->get_ident() << '\n'; - if( ((*it)->get_ident() == id) - && ((*it)->GetType() == tp) ) { - //Before removing it stop it transmitting!! - //cout << "OBLITERATING FROM LIST!!!\n"; - (*it)->SetNoDisplay(); - (*it)->Update(0.00833); - delete (*it); - atc_list.erase(it); - atc_list_itr = atc_list.begin(); // Reset the persistent itr incase we've left it off the end. - break; - } - ++it; - } -} - - -// Find in list - return a currently active ATC pointer given ICAO code and type -// Return NULL if the given service is not in the list -// - *** THE CALLING FUNCTION MUST CHECK FOR THIS *** -FGATC* FGATCMgr::FindInList(const string& id, const atc_type& tp) { - //cout << "Entering FindInList for " << id << ' ' << tp << endl; - atc_list_iterator it = atc_list.begin(); - while(it != atc_list.end()) { - if( ((*it)->get_ident() == id) - && ((*it)->GetType() == tp) ) { - return(*it); - } - ++it; - } - // If we get here it's not in the list - //cout << "Couldn't find it in the list though :-(" << endl; - return(NULL); -} - -// Returns true if the airport is found in the map -bool FGATCMgr::GetAirportATCDetails(const string& icao, AirportATC* a) { - if(airport_atc_map.find(icao) != airport_atc_map.end()) { - *a = *airport_atc_map[icao]; - return(true); - } else { - return(false); - } -} - - -// Return a pointer to a given sort of ATC at a given airport and activate if necessary -// Returns NULL if service doesn't exist - calling function should check for this. -// We really ought to make this private and call it from the CommRegisterAirport / AIRegisterAirport functions -// - at the moment all these GetATC... functions exposed are just too complicated. -FGATC* FGATCMgr::GetATCPointer(const string& icao, const atc_type& type) { - if(airport_atc_map.find(icao) == airport_atc_map.end()) { - //cout << "Unable to find " << icao << ' ' << type << " in the airport_atc_map" << endl; - return NULL; - } - //cout << "In GetATCPointer, found " << icao << ' ' << type << endl; - AirportATC *a = airport_atc_map[icao]; - //cout << "a->lon = " << a->lon << '\n'; - //cout << "a->elev = " << a->elev << '\n'; - //cout << "a->tower_freq = " << a->tower_freq << '\n'; - switch(type) { - case TOWER: - if(a->tower_active) { - // Get the pointer from the list - return(FindInList(icao, type)); - } else { - ATCData data; - if(current_commlist->FindByFreq(a->lon, a->lat, a->elev, a->tower_freq, &data, TOWER)) { - FGTower* t = new FGTower; - t->SetData(&data); - atc_list.push_back(t); - a->tower_active = true; - airport_atc_map[icao] = a; - //cout << "Initing tower " << icao << " in GetATCPointer()\n"; - t->Init(); - return(t); - } else { - SG_LOG(SG_ATC, SG_ALERT, "ERROR - tower that should exist in FGATCMgr::GetATCPointer for airport " << icao << " not found"); - } - } - break; - case APPROACH: - break; - case ATIS: - SG_LOG(SG_ATC, SG_ALERT, "ERROR - ATIS station should not be requested from FGATCMgr::GetATCPointer"); - break; - case GROUND: - //cout << "IN CASE GROUND" << endl; - if(a->ground_active) { - // Get the pointer from the list - return(FindInList(icao, type)); - } else { - ATCData data; - if(current_commlist->FindByFreq(a->lon, a->lat, a->elev, a->ground_freq, &data, GROUND)) { - FGGround* g = new FGGround; - g->SetData(&data); - atc_list.push_back(g); - a->ground_active = true; - airport_atc_map[icao] = a; - g->Init(); - return(g); - } else { - SG_LOG(SG_ATC, SG_ALERT, "ERROR - ground control that should exist in FGATCMgr::GetATCPointer for airport " << icao << " not found"); - } - } - break; - case INVALID: - break; - case ENROUTE: - break; - case DEPARTURE: - break; - } - - SG_LOG(SG_ATC, SG_ALERT, "ERROR IN FGATCMgr - reached end of GetATCPointer"); - //cout << "ERROR IN FGATCMgr - reached end of GetATCPointer" << endl; - - return(NULL); -} - -// Return a pointer to an appropriate voice for a given type of ATC -// creating the voice if necessary - ie. make sure exactly one copy -// of every voice in use exists in memory. -// -// TODO - in the future this will get more complex and dole out country/airport -// specific voices, and possible make sure that the same voice doesn't get used -// at different airports in quick succession if a large enough selection are available. -FGATCVoice* FGATCMgr::GetVoicePointer(const atc_type& type) { - // TODO - implement me better - maintain a list of loaded voices and other voices!! - if(voice) { - switch(type) { - case ATIS: - if(voiceOK) { - return(v1); - } - case TOWER: - return(NULL); - case APPROACH: - return(NULL); - case GROUND: - return(NULL); - default: - return(NULL); - } - return(NULL); - } else { - return(NULL); - } -} - -// Search for ATC stations by frequency -void FGATCMgr::FreqSearch(int channel) { - int chan = channel - 1; // Convert to zero-based for the arrays - - ATCData data; - double freq = comm_node[chan]->getDoubleValue(); - lon = lon_node->getDoubleValue(); - lat = lat_node->getDoubleValue(); - elev = elev_node->getDoubleValue() * SG_FEET_TO_METER; - - // Query the data store and get the closest match if any - if(current_commlist->FindByFreq(lon, lat, elev, freq, &data)) { - // We have a match - // What's the logic? - // If this channel not previously valid then easy - add ATC to list - // If this channel was valid then - Have we tuned to a different service? - // If so - de-register one and add the other - if(comm_valid[chan]) { - if((comm_ident[chan] == data.ident) && (comm_type[chan] == data.type)) { - // Then we're still tuned into the same service so do nought and return - return; - } else { - // Something's changed - either the location or the service type - // We need to feed the channel in so we're not removing it if we're also tuned in on the other channel - CommRemoveFromList(comm_ident[chan], comm_type[chan], chan); - } - } - // At this point we can assume that we need to add the service. - comm_ident[chan] = data.ident; - comm_type[chan] = data.type; - comm_x[chan] = (double)data.x; - comm_y[chan] = (double)data.y; - comm_z[chan] = (double)data.z; - comm_lon[chan] = (double)data.lon; - comm_lat[chan] = (double)data.lat; - comm_elev[chan] = (double)data.elev; - comm_valid[chan] = true; - - // This was a switch-case statement but the compiler didn't like the new variable creation with it. - if(comm_type[chan] == ATIS) { - CommRegisterAirport(comm_ident[chan], chan, ATIS); - FGATC* app = FindInList(comm_ident[chan], ATIS); - if(app != NULL) { - // The station is already in the ATC list - //cout << "In list - flagging SetDisplay..." << endl; - comm_atc_ptr[chan] = app; - app->SetDisplay(); - } else { - // Generate the station and put in the ATC list - //cout << "Not in list - generating..." << endl; - FGATIS* a = new FGATIS; - a->SetData(&data); - comm_atc_ptr[chan] = a; - a->SetDisplay(); - //a->Init(); - atc_list.push_back(a); - } - } else if (comm_type[chan] == TOWER) { - //cout << "TOWER TOWER TOWER\n"; - CommRegisterAirport(comm_ident[chan], chan, TOWER); - //cout << "Done (TOWER)" << endl; - FGATC* app = FindInList(comm_ident[chan], TOWER); - if(app != NULL) { - // The station is already in the ATC list - SG_LOG(SG_GENERAL, SG_DEBUG, comm_ident[chan] << " is in list - flagging SetDisplay..."); - //cout << comm_ident[chan] << " is in list - flagging SetDisplay...\n"; - comm_atc_ptr[chan] = app; - app->SetDisplay(); - } else { - // Generate the station and put in the ATC list - SG_LOG(SG_GENERAL, SG_DEBUG, comm_ident[chan] << " is not in list - generating..."); - //cout << comm_ident[chan] << " is not in list - generating...\n"; - FGTower* t = new FGTower; - t->SetData(&data); - comm_atc_ptr[chan] = t; - //cout << "Initing tower in FreqSearch()\n"; - t->Init(); - t->SetDisplay(); - atc_list.push_back(t); - } - } else if (comm_type[chan] == GROUND) { - CommRegisterAirport(comm_ident[chan], chan, GROUND); - //cout << "Done (GROUND)" << endl; - FGATC* app = FindInList(comm_ident[chan], GROUND); - if(app != NULL) { - // The station is already in the ATC list - comm_atc_ptr[chan] = app; - app->SetDisplay(); - } else { - // Generate the station and put in the ATC list - FGGround* g = new FGGround; - g->SetData(&data); - comm_atc_ptr[chan] = g; - g->Init(); - g->SetDisplay(); - atc_list.push_back(g); - } - } else if (comm_type[chan] == APPROACH) { - // We have to be a bit more carefull here since approaches are also searched by area - CommRegisterAirport(comm_ident[chan], chan, APPROACH); - //cout << "Done (APPROACH)" << endl; - FGATC* app = FindInList(comm_ident[chan], APPROACH); - if(app != NULL) { - // The station is already in the ATC list - app->AddPlane("Player"); - app->SetDisplay(); - comm_atc_ptr[chan] = app; - } else { - // Generate the station and put in the ATC list - FGApproach* a = new FGApproach; - a->SetData(&data); - comm_atc_ptr[chan] = a; - a->Init(); - a->SetDisplay(); - a->AddPlane("Player"); - atc_list.push_back(a); - } - } - } else { - if(comm_valid[chan]) { - if(comm_type[chan] != APPROACH) { - // Currently approaches are removed by Alexander's out-of-range mechanism - CommRemoveFromList(comm_ident[chan], comm_type[chan], chan); - } - // Note that we *don't* call SetNoDisplay() here because the other comm channel - // might be tuned into the same station - this is handled by CommRemoveFromList(...) - comm_type[chan] = INVALID; - comm_atc_ptr[chan] = NULL; - comm_valid[chan] = false; - } - } -} - -// Search ATC stations by area in order that we appear 'on the radar' -void FGATCMgr::AreaSearch() { - // Search for Approach stations - comm_list_type approaches; - comm_list_iterator app_itr; - - lon = lon_node->getDoubleValue(); - lat = lat_node->getDoubleValue(); - elev = elev_node->getDoubleValue() * SG_FEET_TO_METER; - - // search stations in range - int num_app = current_commlist->FindByPos(lon, lat, elev, 100.0, &approaches, APPROACH); - if (num_app != 0) { - //cout << num_app << " approaches found in radiostack search !!!!" << endl; - - for(app_itr = approaches.begin(); app_itr != approaches.end(); app_itr++) { - - FGATC* app = FindInList(app_itr->ident, app_itr->type); - if(app != NULL) { - // The station is already in the ATC list - //cout << "In list adding player\n"; - app->AddPlane("Player"); - //app->Update(); - } else { - // Generate the station and put in the ATC list - FGApproach* a = new FGApproach; - a->SetData(&(*app_itr)); - //cout << "Adding player\n"; - a->AddPlane("Player"); - //a->Update(); - atc_list.push_back(a); - } - } - } - - // remove planes which are out of range - // TODO - I'm not entirely sure that this belongs here. - atc_list_iterator it = atc_list.begin(); - while(it != atc_list.end()) { - if((*it)->GetType() == APPROACH ) { - int np = (*it)->RemovePlane(); - // if approach has no planes left remove it from ATC list - if ( np == 0) { - //cout << "REMOVING AN APPROACH STATION WITH NO PLANES..." << endl; - (*it)->SetNoDisplay(); - (*it)->Update(0.00833); - delete (*it); - atc_list.erase(it); - atc_list_itr = atc_list.begin(); // Reset the persistent itr incase we've left it off the end. - break; // the other stations will be checked next time - } - } - ++it; - } -} diff --git a/src/ATC/ATCmgr.hxx b/src/ATC/ATCmgr.hxx deleted file mode 100644 index 9e8e1f837..000000000 --- a/src/ATC/ATCmgr.hxx +++ /dev/null @@ -1,212 +0,0 @@ -// ATCMgr.hxx - definition of FGATCMgr -// - a global management class for FlightGear generated ATC -// -// Written by David Luff, started February 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifndef _FG_ATCMGR_HXX -#define _FG_ATCMGR_HXX - -#include - -#include
-#include - -#include -#include -#include - -#include "tower.hxx" -#include "approach.hxx" - -SG_USING_STD(string); -SG_USING_STD(list); -SG_USING_STD(map); - -// Structure for holding details of the ATC frequencies at a given airport, and whether they are in the active list or not. -// These can then be cross referenced with the commlists which are stored by frequency or bucket. -// Non-available services are denoted by a frequency of zero. -// These structures are only intended to be created for in-use airports, and removed when no longer needed. -struct AirportATC { - AirportATC(); - - float lon; - float lat; - float elev; - float atis_freq; - bool atis_active; - float tower_freq; - bool tower_active; - float ground_freq; - bool ground_active; - //float approach_freq; - //bool approach_active; - //float departure_freq; - //bool departure_active; - - // NOTE - the *_active flags determine whether the service is active in atc_list, - // *NOT* whether the tower etc is closed or not!!!! - - // Flags to ensure the stations don't get wrongly deactivated - bool set_by_AI; // true when the AI manager has activated this station - unsigned int numAI; // Ref count of the number of AI planes registered - bool set_by_comm[2][ATC_NUM_TYPES]; // true when the relevant comm_freq has activated this station and type -}; - -class FGATCMgr : public SGSubsystem -{ - -private: - - bool initDone; // Hack - guard against update getting called before init - - // A map of airport ID vs frequencies and ATC provision - typedef map < string, AirportATC* > airport_atc_map_type; - typedef airport_atc_map_type::iterator airport_atc_map_iterator; - typedef airport_atc_map_type::const_iterator airport_atc_map_const_iterator; - - airport_atc_map_type airport_atc_map; - airport_atc_map_iterator airport_atc_map_itr; - - // A list of pointers to all currently active ATC classes - typedef list atc_list_type; - typedef atc_list_type::iterator atc_list_iterator; - typedef atc_list_type::const_iterator atc_list_const_iterator; - - // Everything put in this list should be created dynamically - // on the heap and ***DELETED WHEN REMOVED!!!!!*** - atc_list_type atc_list; - atc_list_iterator atc_list_itr; - // Any member function of FGATCMgr is permitted to leave this iterator pointing - // at any point in or at the end of the list. - // Hence any new access must explicitly first check for atc_list.end() before dereferencing. - - // Position of the Users Aircraft - double lon; - double lat; - double elev; - - // Type of ATC control that the user's radios are tuned to. - atc_type comm_type[2]; - - // Pointer to the ATC station that the user is currently tuned into. - FGATC* comm_atc_ptr[2]; - - double comm_freq[2]; - - // Pointers to users current communication frequencies. - SGPropertyNode_ptr comm_node[2]; - - // Pointers to current users position - SGPropertyNode_ptr lon_node; - SGPropertyNode_ptr lat_node; - SGPropertyNode_ptr elev_node; - - // Position of the ATC that the comm radios are tuned to in order to decide - // whether transmission will be received. - double comm_x[2], comm_y[2], comm_z[2], comm_lon[2], comm_lat[2], comm_elev[2]; - - double comm_range[2], comm_effective_range[2]; - bool comm_valid[2]; - string comm_ident[2]; - //string last_comm_ident[2]; - //string approach_ident; - bool last_in_range; - - //FGATIS atis; - //FGGround ground; - FGTower tower; - FGApproach approach; - //FGDeparture departure; - - // Voice related stuff - bool voice; // Flag - true if we are using voice -#ifdef ENABLE_AUDIO_SUPPORT - bool voiceOK; // Flag - true if at least one voice has loaded OK - FGATCVoice* v1; -#endif - -public: - - FGATCMgr(); - ~FGATCMgr(); - - void init(); - - void bind(); - - void unbind(); - - void update(double dt); - - // Returns true if the airport is found in the map - bool GetAirportATCDetails(const string& icao, AirportATC* a); - - // Return a pointer to a given sort of ATC at a given airport and activate if necessary - // Returns NULL if service doesn't exist - calling function should check for this. - FGATC* GetATCPointer(const string& icao, const atc_type& type); - - // Return a pointer to an appropriate voice for a given type of ATC - // creating the voice if necessary - ie. make sure exactly one copy - // of every voice in use exists in memory. - // - // TODO - in the future this will get more complex and dole out country/airport - // specific voices, and possible make sure that the same voice doesn't get used - // at different airports in quick succession if a large enough selection are available. - FGATCVoice* GetVoicePointer(const atc_type& type); - - atc_type GetComm1ATCType() { return(comm_type[0]); } - FGATC* GetComm1ATCPointer() { return(comm_atc_ptr[0]); } - atc_type GetComm2ATCType() { return(comm_type[1]); } - FGATC* GetComm2ATCPointer() { return(comm_atc_ptr[1]); } - - // Get the frequency of a given service at a given airport - // Returns zero if not found - unsigned short int GetFrequency(const string& ident, const atc_type& tp); - - // Register the fact that the AI system wants to activate an airport - bool AIRegisterAirport(const string& ident); - - // Register the fact that the comm radio is tuned to an airport - bool CommRegisterAirport(const string& ident, int chan, const atc_type& tp); - -private: - - // Remove a class from the atc_list and delete it from memory - // *if* no other comm channel or AI plane is using it. - void CommRemoveFromList(const string& id, const atc_type& tp, int chan); - - // Remove a class from the atc_list and delete it from memory - // Should be called from the above - not directly!! - void RemoveFromList(const string& id, const atc_type& tp); - - // Return a pointer to a class in the list given ICAO code and type - // (external interface to this is through GetATCPointer) - // Return NULL if the given service is not in the list - // - *** THE CALLING FUNCTION MUST CHECK FOR THIS *** - FGATC* FindInList(const string& id, const atc_type& tp); - - // Search the specified channel for stations on the same frequency and in range. - void FreqSearch(int channel); - - // Search ATC stations by area in order that we appear 'on the radar' - void AreaSearch(); - -}; - -#endif // _FG_ATCMGR_HXX diff --git a/src/ATC/ATCutils.cxx b/src/ATC/ATCutils.cxx deleted file mode 100644 index c5a229fee..000000000 --- a/src/ATC/ATCutils.cxx +++ /dev/null @@ -1,314 +0,0 @@ -// ATCutils.cxx - Utility functions for the ATC / AI system -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include - -#include -#include -#include -#include -#include -#include -//#include - -#include -#include
- -#include "ATCutils.hxx" -#include "ATCProjection.hxx" - -static const string nums[10] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "niner"}; - -// Convert any number to spoken digits -string ConvertNumToSpokenDigits(const string &n) { - //cout << "n = " << n << endl; - static const string pt = "decimal"; - string str = ""; - - for(unsigned int i=0; i 36) { - n -= 36; - } - if(n == 0) { - n = 36; // Is this right? - } - - string str = ""; - int index = n/10; - str += nums[index]; - n -= (index * 10); - //str += "-"; - str += " "; //Changed this for the benefit of the voice token parser - prefer the "-" in the visual output though. - str += nums[n]; - return(str); -} - -// Assumes we get a two-digit string optionally appended with L, R or C -// eg 01 07L 29R 36 -// Anything else is not guaranteed to be handled correctly! -string ConvertRwyNumToSpokenString(const string &s) { - if(s.size() < 3) { - return(ConvertRwyNumToSpokenString(atoi(s.c_str()))); - } else { - string r = ConvertRwyNumToSpokenString(atoi(s.substr(0,2).c_str())); - if(s.substr(2,1) == "L") { - r += " left"; - } else if(s.substr(2,1) == "R") { - r += " right"; - } else if(s.substr(2,1) == "C") { - r += " center"; - } else { - SG_LOG(SG_ATC, SG_WARN, "WARNING: Unknown suffix " << s.substr(2,1) << " from runway ID " << s << " in ConvertRwyNumToSpokenString(...)"); - } - return(r); - } -} - - -// Return the phonetic letter of a letter represented as an integer 1->26 -string GetPhoneticIdent(int i) { - // TODO - Check i is between 1 and 26 and wrap if necessary - return(GetPhoneticIdent(char('a' + (i-1)))); -} - -// Return the phonetic letter of a character in the range a-z or A-Z. -// Currently always returns prefixed by lowercase. -string GetPhoneticIdent(char c) { - c = tolower(c); - // TODO - Check c is between a and z and wrap if necessary - switch(c) { - case 'a' : return("alpha"); - case 'b' : return("bravo"); - case 'c' : return("charlie"); - case 'd' : return("delta"); - case 'e' : return("echo"); - case 'f' : return("foxtrot"); - case 'g' : return("golf"); - case 'h' : return("hotel"); - case 'i' : return("india"); - case 'j' : return("juliet"); - case 'k' : return("kilo"); - case 'l' : return("lima"); - case 'm' : return("mike"); - case 'n' : return("november"); - case 'o' : return("oscar"); - case 'p' : return("papa"); - case 'q' : return("quebec"); - case 'r' : return("romeo"); - case 's' : return("sierra"); - case 't' : return("tango"); - case 'u' : return("uniform"); - case 'v' : return("victor"); - case 'w' : return("whiskey"); - case 'x' : return("x-ray"); - case 'y' : return("yankee"); - case 'z' : return("zulu"); - } - // We shouldn't get here - return("Error"); -} - -// Get the compass direction associated with a heading in degrees -// Currently returns 8 direction resolution (N, NE, E etc...) -// Might be modified in future to return 4, 8 or 16 resolution but defaulting to 8. -string GetCompassDirection(double h) { - while(h < 0.0) h += 360.0; - while(h > 360.0) h -= 360.0; - if(h < 22.5 || h > 337.5) { - return("North"); - } else if(h < 67.5) { - return("North-East"); - } else if(h < 112.5) { - return("East"); - } else if(h < 157.5) { - return("South-East"); - } else if(h < 202.5) { - return("South"); - } else if(h < 247.5) { - return("South-West"); - } else if(h < 292.5) { - return("West"); - } else { - return("North-West"); - } -} - -//================================================================================================================ - -// Given two positions (lat & lon in degrees), get the HORIZONTAL separation (in meters) -double dclGetHorizontalSeparation(const Point3D& pos1, const Point3D& pos2) { - double x; //East-West separation - double y; //North-South separation - double z; //Horizontal separation - z = sqrt(x^2 + y^2) - - double lat1 = pos1.lat() * SG_DEGREES_TO_RADIANS; - double lon1 = pos1.lon() * SG_DEGREES_TO_RADIANS; - double lat2 = pos2.lat() * SG_DEGREES_TO_RADIANS; - double lon2 = pos2.lon() * SG_DEGREES_TO_RADIANS; - - y = sin(fabs(lat1 - lat2)) * SG_EQUATORIAL_RADIUS_M; - x = sin(fabs(lon1 - lon2)) * SG_EQUATORIAL_RADIUS_M * (cos((lat1 + lat2) / 2.0)); - z = sqrt(x*x + y*y); - - return(z); -} - -// Given a point and a line, get the HORIZONTAL shortest distance from the point to a point on the line. -// Expects to be fed orthogonal co-ordinates, NOT lat & lon ! -// The units of the separation will be those of the input. -double dclGetLinePointSeparation(double px, double py, double x1, double y1, double x2, double y2) { - double vecx = x2-x1; - double vecy = y2-y1; - double magline = sqrt(vecx*vecx + vecy*vecy); - double u = ((px-x1)*(x2-x1) + (py-y1)*(y2-y1)) / (magline * magline); - double x0 = x1 + u*(x2-x1); - double y0 = y1 + u*(y2-y1); - vecx = px - x0; - vecy = py - y0; - double d = sqrt(vecx*vecx + vecy*vecy); - if(d < 0) { - d *= -1; - } - return(d); -} - -// Given a position (lat/lon/elev), heading and vertical angle (degrees), and distance (meters), calculate the new position. -// This function assumes the world is spherical. If geodetic accuracy is required use the functions is sg_geodesy instead! -// Assumes that the ground is not hit!!! Expects heading and angle in degrees, distance in meters. -Point3D dclUpdatePosition(const Point3D& pos, double heading, double angle, double distance) { - //cout << setprecision(10) << pos.lon() << ' ' << pos.lat() << '\n'; - heading *= DCL_DEGREES_TO_RADIANS; - angle *= DCL_DEGREES_TO_RADIANS; - double lat = pos.lat() * DCL_DEGREES_TO_RADIANS; - double lon = pos.lon() * DCL_DEGREES_TO_RADIANS; - double elev = pos.elev(); - //cout << setprecision(10) << lon*DCL_RADIANS_TO_DEGREES << ' ' << lat*DCL_RADIANS_TO_DEGREES << '\n'; - - double horiz_dist = distance * cos(angle); - double vert_dist = distance * sin(angle); - - double north_dist = horiz_dist * cos(heading); - double east_dist = horiz_dist * sin(heading); - - //cout << distance << ' ' << horiz_dist << ' ' << vert_dist << ' ' << north_dist << ' ' << east_dist << '\n'; - - double delta_lat = asin(north_dist / (double)SG_EQUATORIAL_RADIUS_M); - double delta_lon = asin(east_dist / (double)SG_EQUATORIAL_RADIUS_M) * (1.0 / cos(lat)); // I suppose really we should use the average of the original and new lat but we'll assume that this will be good enough. - //cout << delta_lon*DCL_RADIANS_TO_DEGREES << ' ' << delta_lat*DCL_RADIANS_TO_DEGREES << '\n'; - lat += delta_lat; - lon += delta_lon; - elev += vert_dist; - //cout << setprecision(10) << lon*DCL_RADIANS_TO_DEGREES << ' ' << lat*DCL_RADIANS_TO_DEGREES << '\n'; - - //cout << setprecision(15) << DCL_DEGREES_TO_RADIANS * DCL_RADIANS_TO_DEGREES << '\n'; - - return(Point3D(lon*DCL_RADIANS_TO_DEGREES, lat*DCL_RADIANS_TO_DEGREES, elev)); -} - -// Get a heading in degrees from one lat/lon to another. -// This function assumes the world is spherical. If geodetic accuracy is required use the functions is sg_geodesy instead! -// Warning - at the moment we are not checking for identical points - currently it returns 0 in this instance. -double GetHeadingFromTo(const Point3D& A, const Point3D& B) { - double latA = A.lat() * DCL_DEGREES_TO_RADIANS; - double lonA = A.lon() * DCL_DEGREES_TO_RADIANS; - double latB = B.lat() * DCL_DEGREES_TO_RADIANS; - double lonB = B.lon() * DCL_DEGREES_TO_RADIANS; - double xdist = sin(lonB - lonA) * (double)SG_EQUATORIAL_RADIUS_M * cos((latA+latB)/2.0); - double ydist = sin(latB - latA) * (double)SG_EQUATORIAL_RADIUS_M; - double heading = atan2(xdist, ydist) * DCL_RADIANS_TO_DEGREES; - return heading < 0.0 ? heading + 360 : heading; -} - -// Given a heading (in degrees), bound it from 0 -> 360 -void dclBoundHeading(double &hdg) { - while(hdg < 0.0) { - hdg += 360.0; - } - while(hdg > 360.0) { - hdg -= 360.0; - } -} - -// smallest difference between two angles in degrees -// difference is negative if a1 > a2 and positive if a2 > a1 -double GetAngleDiff_deg( const double &a1, const double &a2) { - - double a3 = a2 - a1; - while (a3 < 180.0) a3 += 360.0; - while (a3 > 180.0) a3 -= 360.0; - - return a3; -} - -// Runway stuff -// Given a Point3D (lon/lat/elev) and an FGRunway struct, determine if the point lies on the runway -bool OnRunway(const Point3D& pt, const FGRunway& rwy) { - FGATCAlignedProjection ortho; - Point3D centre(rwy._lon, rwy._lat, 0.0); // We don't need the elev - ortho.Init(centre, rwy._heading); - - Point3D xyc = ortho.ConvertToLocal(centre); - Point3D xyp = ortho.ConvertToLocal(pt); - - //cout << "Length offset = " << fabs(xyp.y() - xyc.y()) << '\n'; - //cout << "Width offset = " << fabs(xyp.x() - xyc.x()) << '\n'; - - if((fabs(xyp.y() - xyc.y()) < ((rwy._length/2.0) + 5.0)) - && (fabs(xyp.x() - xyc.x()) < (rwy._width/2.0))) { - return(true); - } - - return(false); -} - diff --git a/src/ATC/ATCutils.hxx b/src/ATC/ATCutils.hxx deleted file mode 100644 index 80b155e08..000000000 --- a/src/ATC/ATCutils.hxx +++ /dev/null @@ -1,103 +0,0 @@ -// ATCutils.hxx - Utility functions for the ATC / AI subsytem -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// 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. - -#include -#include - -#include -#include -#include -SG_USING_STD(string); - -// These are defined here because I had a problem with SG_DEGREES_TO_RADIANS -#define DCL_PI 3.1415926535f -#define DCL_DEGREES_TO_RADIANS (DCL_PI/180.0) -#define DCL_RADIANS_TO_DEGREES (180.0/DCL_PI) - -/******************************* -* -* Communication functions -* -********************************/ - -// Convert any number to spoken digits -string ConvertNumToSpokenDigits(const string &n); - -// Convert an integer to spoken digits -string ConvertNumToSpokenDigits(int n); - -// Convert a 2 digit rwy number to a spoken-style string -string ConvertRwyNumToSpokenString(int n); - -// Convert rwy number string to a spoken-style string -// eg "05L" to "zero five left" -// Assumes we get a two-digit string optionally appended with R, L, or C -// eg 01 07L 29R 36 -// Anything else is not guaranteed to be handled correctly! -string ConvertRwyNumToSpokenString(const string &s); - -// Return the phonetic letter of a letter represented as an integer 1->26 -string GetPhoneticIdent(int i); - -// Return the phonetic letter of a character in the range a-z or A-Z. -// Currently always returns prefixed by lowercase. -string GetPhoneticIdent(char c); - -// Get the compass direction associated with a heading in degrees -// Currently returns 8 direction resolution (N, NE, E etc...) -// Might be modified in future to return 4, 8 or 16 resolution but defaulting to 8. -string GetCompassDirection(double h); - -/******************************* -* -* Positional functions -* -********************************/ - -// Given two positions (lat & lon in degrees), get the HORIZONTAL separation (in meters) -double dclGetHorizontalSeparation(const Point3D& pos1, const Point3D& pos2); - -// Given a point and a line, get the HORIZONTAL shortest distance from the point to a point on the line. -// Expects to be fed orthogonal co-ordinates, NOT lat & lon ! -double dclGetLinePointSeparation(double px, double py, double x1, double y1, double x2, double y2); - -// Given a position (lat/lon/elev), heading, vertical angle, and distance, calculate the new position. -// Assumes that the ground is not hit!!! Expects heading and angle in degrees, distance in meters. -Point3D dclUpdatePosition(const Point3D& pos, double heading, double angle, double distance); - -// Get a heading from one lat/lon to another (in degrees) -double GetHeadingFromTo(const Point3D& A, const Point3D& B); - -// Given a heading (in degrees), bound it from 0 -> 360 -void dclBoundHeading(double &hdg); - -// smallest difference between two angles in degrees -// difference is negative if a1 > a2 and positive if a2 > a1 -double GetAngleDiff_deg( const double &a1, const double &a2); - -/**************** -* -* Runways -* -****************/ - -// Given a Point3D (lon/lat/elev) and an FGRunway struct, determine if the point lies on the runway -bool OnRunway(const Point3D& pt, const FGRunway& rwy); - diff --git a/src/ATC/Makefile.am b/src/ATC/Makefile.am deleted file mode 100644 index c560d1643..000000000 --- a/src/ATC/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -noinst_LIBRARIES = libATC.a - -libATC_a_SOURCES = \ - ATC.hxx ATC.cxx \ - atis.hxx atis.cxx \ - tower.hxx tower.cxx \ - approach.hxx approach.cxx \ - ground.hxx ground.cxx \ - commlist.hxx commlist.cxx \ - ATCDialog.hxx ATCDialog.cxx \ - ATCVoice.hxx ATCVoice.cxx \ - ATCmgr.hxx ATCmgr.cxx \ - ATCutils.hxx ATCutils.cxx \ - ATCProjection.hxx ATCProjection.cxx \ - AIMgr.hxx AIMgr.cxx \ - AIEntity.hxx AIEntity.cxx \ - AIPlane.hxx AIPlane.cxx \ - AILocalTraffic.hxx AILocalTraffic.cxx \ - AIGAVFRTraffic.hxx AIGAVFRTraffic.cxx \ - transmission.hxx transmission.cxx \ - transmissionlist.hxx transmissionlist.cxx - -INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/src/ATC/approach.cxx b/src/ATC/approach.cxx deleted file mode 100644 index 44784abf1..000000000 --- a/src/ATC/approach.cxx +++ /dev/null @@ -1,759 +0,0 @@ -// FGApproach - a class to provide approach control at larger airports. -// -// Written by Alexander Kappes, started March 2002. -// -// Copyright (C) 2002 Alexander Kappes -// -// 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. - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include "approach.hxx" -#include "transmission.hxx" -#include "transmissionlist.hxx" -#include "ATCDialog.hxx" - -#include -#include -#include -#include - -#include -#include - - -#include - -//Constructor -FGApproach::FGApproach(){ - comm1_node = fgGetNode("/instrumentation/comm[0]/frequencies/selected-mhz", true); - comm2_node = fgGetNode("/instrumentation/comm[1]/frequencies/selected-mhz", true); - - _type = APPROACH; - - num_planes = 0; - lon_node = fgGetNode("/position/longitude-deg", true); - lat_node = fgGetNode("/position/latitude-deg", true); - elev_node = fgGetNode("/position/altitude-ft", true); - hdg_node = fgGetNode("/orientation/heading-deg", true); - speed_node = fgGetNode("/velocities/airspeed-kt", true); - etime_node = fgGetNode("/sim/time/elapsed-sec", true); - - first = true; - active_runway = ""; - int i; - for ( i=0; igetDoubleValue(); - - //bool DisplayTransmissions = true; - - for (i=0; igen_text(station, code, tpars, false); - transm = current_transmissionlist->gen_text(station, code, tpars, true); - // is the transmission already registered? - if (!current_atcdialog->trans_reg( ident, transm, APPROACH )) { - current_atcdialog->add_entry( ident, transm, mentry, APPROACH, 0 ); - } - } - } - } - } - } - } - - for ( i=0; i 100.0) tpars.VDir = 1; - else if (planes[i].alt-planes[i].aalt < -100.0) tpars.VDir = 3; - else tpars.VDir = 2; - tpars.alt = planes[i].aalt; - message = current_transmissionlist->gen_text(station, code, tpars, true ); - //cout << message << '\n'; - set_message(message); - planes[i].lmc = code; - planes[i].tlm = etime_node->getDoubleValue(); - planes[i].on_crs = true; - planes[i].contact = 1; - } - //} - - //if(1) { - if ( planes[i].contact == 1 ) { - // ========================= - // === update parameters === - // ========================= - update_param( i ); - //cout << planes[i].brg << " " << planes[i].dist << " " << planes[i].wpts[wpn+1][0] - //<< " " << planes[i].wpts[wpn+1][1] << " " << planes[i].wpts[wpn+1][4] - //cout << wpn << " distance to current course = " << planes[i].dcc << endl; - //cout << etime_node->getDoubleValue() << endl; - - // ========================= - // === reached waypoint? === - // ========================= - wpn = planes[i].wpn-2; - adif = angle_diff_deg( planes[i].hdg, planes[i].wpts[wpn][4] ) - * SGD_DEGREES_TO_RADIANS; - datp = 2*sin(fabs(adif)/2.0)*sin(fabs(adif)/2.0) * - planes[i].spd/3600. * planes[i].turn_rate + - planes[i].spd/3600. * 3.0; - //cout << adif/SGD_DEGREES_TO_RADIANS << " " - // << datp << " " << planes[i].dnc << " " << planes[i].dcc < 100.0) tpars.VDir = 1; - else if (planes[i].alt-planes[i].aalt < -100.0) tpars.VDir = 3; - else tpars.VDir = 2; - tpars.alt = planes[i].aalt; - message = current_transmissionlist->gen_text(station, code, tpars, true ); - //cout << "Approach transmitting...\n"; - //cout << message << endl; - set_message(message); - } - else { - code.c1 = 1; - code.c2 = 3; - code.c3 = 0; - tpars.runway = active_runway; - message = current_transmissionlist->gen_text(station, code, tpars, true); - //cout << "Approach transmitting 2 ...\n"; - //cout << message << endl; - set_message(message); - } - planes[i].lmc = code; - planes[i].tlm = etime_node->getDoubleValue(); - planes[i].on_crs = true; - - update_param( i ); - } - - // ========================= - // === come off course ? === - // ========================= - if ( fabs(planes[i].dcc) > 1.0 && - ( !planes[i].wp_change || etime_node->getDoubleValue() - planes[i].tlm > tbm ) ) { - //cout << "Off course!\n"; - if ( planes[i].on_crs ) { - if ( planes[i].dcc < 0) { - planes[i].ahdg += 30.0; - } - else { - planes[i].ahdg -= 30.0; - } - if (planes[i].ahdg > 360.0) planes[i].ahdg -= 360.0; - else if (planes[i].ahdg < 0.0) planes[i].ahdg += 360.0; - } - //cout << planes[i].on_crs << " " - // << angle_diff_deg( planes[i].hdg, planes[i].ahdg) << " " - // << etime_node->getDoubleValue() << " " - // << planes[i].tlm << endl; - // generate the message - if ( planes[i].on_crs || - ( fabs(angle_diff_deg( planes[i].hdg, planes[i].ahdg )) > 30.0 && - etime_node->getDoubleValue() - planes[i].tlm > tbm) ) { - // generate the message - code.c1 = 1; - code.c2 = 4; - code.c3 = 0; - adif = angle_diff_deg( planes[i].hdg, planes[i].ahdg ); - tpars.station = name; - tpars.callsign = "Player"; - tpars.miles = fabs(planes[i].dcc); - if ( adif < 0 ) tpars.tdir = 1; - else tpars.tdir = 2; - tpars.heading = planes[i].ahdg; - message = current_transmissionlist->gen_text(station, code, tpars, true); - //cout << "Approach transmitting 3 ...\n"; - //cout << message << '\n'; - set_message(message); - planes[i].lmc = code; - planes[i].tlm = etime_node->getDoubleValue(); - } - - planes[i].on_crs = false; - } - else if ( !planes[i].on_crs ) { - //cout << "Off course 2!\n"; - wpn = planes[i].wpn-1; - adif = angle_diff_deg( planes[i].hdg, planes[i].wpts[wpn][4] ) - * SGD_DEGREES_TO_RADIANS; - datp = 2*sin(fabs(adif)/2.0)*sin(fabs(adif)/2.0) * - planes[i].spd/3600. * planes[i].turn_rate + - planes[i].spd/3600. * 3.0; - if ( fabs(planes[i].dcc) < datp ) { - planes[i].ahdg = fabs(planes[i].wpts[wpn][4]); - - // generate the message - code.c1 = 1; - code.c2 = 2; - code.c3 = 0; - tpars.station = name; - tpars.callsign = "Player"; - if ( adif < 0 ) tpars.tdir = 1; - else tpars.tdir = 2; - tpars.heading = planes[i].ahdg; - message = current_transmissionlist->gen_text(station, code, tpars, true); - //cout << "Approach transmitting 4 ...\n"; - //cout << message << '\n'; - set_message(message); - planes[i].lmc = code; - planes[i].tlm = etime_node->getDoubleValue(); - - planes[i].on_crs = true; - } - } - else if ( planes[i].wp_change ) { - planes[i].wp_change = false; - } - - // =================================================================== - // === Less than two minutes away from touchdown? -> Contact Tower === - // =================================================================== - if ( planes[i].wpn == 2 && planes[i].dnwp < planes[i].spd/60.*2.0 ) { - - double freq = 121.95; // Hardwired - FIXME - // generate message - code.c1 = 1; - code.c2 = 5; - code.c3 = 0; - tpars.station = name; - tpars.callsign = "Player"; - tpars.freq = freq; - message = current_transmissionlist->gen_text(station, code, tpars, true); - //cout << "Approach transmitting 5 ...\n"; - //cout << message << '\n'; - set_message(message); - planes[i].lmc = code; - planes[i].tlm = etime_node->getDoubleValue(); - - planes[i].contact = 2; - } - } - } -} - - -// ============================================================================ -// update course parameters -// ============================================================================ -void FGApproach::update_param( const int &i ) { - - double course, d; - - int wpn = planes[i].wpn-1; // this is the current waypoint - - planes[i].dcc = calc_psl_dist(planes[i].brg, planes[i].dist, - planes[i].wpts[wpn][0], planes[i].wpts[wpn][1], - planes[i].wpts[wpn][4]); - planes[i].dnc = calc_psl_dist(planes[i].brg, planes[i].dist, - planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1], - planes[i].wpts[wpn-1][4]); - calc_hd_course_dist(planes[i].brg, planes[i].dist, - planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1], - &course, &d); - planes[i].dnwp = d; - -} - -// ============================================================================ -// smallest difference between two angles in degree -// difference is negative if a1 > a2 and positive if a2 > a1 -// =========================================================================== -double FGApproach::angle_diff_deg( const double &a1, const double &a2) { - - double a3 = a2 - a1; - if (a3 < 180.0) a3 += 360.0; - if (a3 > 180.0) a3 -= 360.0; - - return a3; -} - -// ============================================================================ -// calculate waypoints -// ============================================================================ -void FGApproach::calc_wp( const int &i ) { - - int j; - double course, d, cd, a1; - - int wpn = planes[i].wpn; - // waypoint 0: Threshold of active runway - calc_gc_course_dist(Point3D(lon*SGD_DEGREES_TO_RADIANS, lat*SGD_DEGREES_TO_RADIANS, 0.0), - Point3D(active_rw_lon*SGD_DEGREES_TO_RADIANS,active_rw_lat*SGD_DEGREES_TO_RADIANS, 0.0 ), - &course, &d); - double d1 = active_rw_hdg+180.0; - if ( d1 > 360.0 ) d1 -=360.0; - calc_cd_head_dist(360.0-course*SGD_RADIANS_TO_DEGREES, d/SG_NM_TO_METER, - d1, active_rw_len/SG_NM_TO_METER/2.0, - &planes[i].wpts[wpn][0], &planes[i].wpts[wpn][1]); - planes[i].wpts[wpn][2] = elev; - planes[i].wpts[wpn][4] = 0.0; - planes[i].wpts[wpn][5] = 0.0; - wpn += 1; - - // ====================== - // horizontal navigation - // ====================== - // waypoint 1: point for turning onto final - calc_cd_head_dist(planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1], d1, lfl, - &planes[i].wpts[wpn][0], &planes[i].wpts[wpn][1]); - calc_hd_course_dist(planes[i].wpts[wpn][0], planes[i].wpts[wpn][1], - planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1], - &course, &d); - planes[i].wpts[wpn][4] = course; - planes[i].wpts[wpn][5] = d; - wpn += 1; - - // calculate course and distance from plane position to waypoint 1 - calc_hd_course_dist(planes[i].brg, planes[i].dist, planes[i].wpts[1][0], - planes[i].wpts[1][1], &course, &d); - // check if airport is not between plane and waypoint 1 and - // DCA to airport on course to waypoint 1 is larger than 10 miles - double zero = 0.0; - if ( fabs(angle_diff_deg( planes[i].wpts[1][0], planes[i].brg )) < 90.0 || - calc_psl_dist( zero, zero, planes[i].brg, planes[i].dist, course ) > 10.0 ) { - // check if turning angle at waypoint 1 would be > max_ta - if ( fabs(angle_diff_deg( planes[i].wpts[1][4], course )) > max_ta ) { - cd = calc_psl_dist(planes[i].brg, planes[i].dist, - planes[i].wpts[1][0], planes[i].wpts[1][1], - planes[i].wpts[1][4]); - a1 = atan2(cd,planes[i].wpts[1][1]); - planes[i].wpts[wpn][0] = planes[i].wpts[1][0] - a1/SGD_DEGREES_TO_RADIANS; - if ( planes[i].wpts[wpn][0] < 0.0) planes[i].wpts[wpn][0] += 360.0; - if ( planes[i].wpts[wpn][0] > 360.0) planes[i].wpts[wpn][0] -= 360.0; - planes[i].wpts[wpn][1] = fabs(cd) / sin(fabs(a1)); - calc_hd_course_dist(planes[i].wpts[wpn][0], planes[i].wpts[wpn][1], - planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1], - &course, &d); - planes[i].wpts[wpn][4] = course; - planes[i].wpts[wpn][5] = d; - wpn += 1; - - calc_hd_course_dist(planes[i].brg, planes[i].dist, planes[i].wpts[wpn-1][0], - planes[i].wpts[wpn-1][1], &course, &d); - } - } else { - double leg = 10.0; - a1 = atan2(planes[i].wpts[1][1], leg ); - - if ( angle_diff_deg(planes[i].brg,planes[i].wpts[1][0]) < 0 ) - planes[i].wpts[wpn][0] = planes[i].wpts[1][0] + a1/SGD_DEGREES_TO_RADIANS; - else planes[i].wpts[wpn][0] = planes[i].wpts[1][0] - a1/SGD_DEGREES_TO_RADIANS; - - planes[i].wpts[wpn][1] = sqrt( planes[i].wpts[1][1]*planes[i].wpts[1][1] + leg*leg ); - calc_hd_course_dist(planes[i].wpts[wpn][0], planes[i].wpts[wpn][1], - planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1], - &course, &d); - planes[i].wpts[wpn][4] = course; - planes[i].wpts[wpn][5] = d; - wpn += 1; - - calc_hd_course_dist(planes[i].brg, planes[i].dist, - planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1], - &course, &d); - } - - planes[i].wpts[wpn][0] = planes[i].brg; - planes[i].wpts[wpn][1] = planes[i].dist; - planes[i].wpts[wpn][2] = planes[i].alt; - planes[i].wpts[wpn][4] = course; - planes[i].wpts[wpn][5] = d; - wpn += 1; - - planes[i].wpn = wpn; - - // Now check if legs are too short or if legs can be shortend - // legs must be at least 2 flight minutes long - double mdist = planes[i].spd / 60.0 * 2.0; - for ( j=2; j 0 ) { - alt = planes[i].wpts[j-1][2] + - (planes[i].wpts[j][5] / planes[i].spd) * 60.0 * planes[i].desc_rate; - planes[i].wpts[j][2] = round_alt( false, alt ); - if ( planes[i].wpts[j][2] > planes[i].alt ) - planes[i].wpts[j][2] = round_alt( false, planes[i].alt ); - } - else { - planes[i].wpts[j][2] = planes[i].wpts[1][2]; - } - } - - cout << "Plane position: " << planes[i].brg << " " << planes[i].dist << endl; - for ( j=0; j (int)(alt)+0.5 ) alt = ((int)(alt)+1)*1000.0; - else alt = ((int)(alt)+0.5)*1000.0; - } - else { - if ( alt > (int)(alt)+0.5 ) alt = ((int)(alt)+0.5)*1000.0; - else alt = ((int)(alt))*1000.0; - } - - return alt; -} - - -// ============================================================================ -// get active runway -// ============================================================================ -void FGApproach::get_active_runway() { - //cout << "Entering FGApproach::get_active_runway()\n"; - - FGEnvironment stationweather = - ((FGEnvironmentMgr *)globals->get_subsystem("environment")) - ->getEnvironment(lat, lon, elev); - - double hdg = stationweather.get_wind_from_heading_deg(); - - FGRunway runway; - if ( globals->get_runways()->search( ident, int(hdg), &runway) ) { - active_runway = runway._rwy_no; - active_rw_hdg = runway._heading; - active_rw_lon = runway._lon; - active_rw_lat = runway._lat; - active_rw_len = runway._length; - //cout << "Active runway is: " << active_runway << " heading = " - // << active_rw_hdg - // << " lon = " << active_rw_lon - // << " lat = " << active_rw_lat <getDoubleValue(); - planes[i].lat = lat_node->getDoubleValue(); - planes[i].alt = elev_node->getDoubleValue(); - planes[i].hdg = hdg_node->getDoubleValue(); - planes[i].spd = speed_node->getDoubleValue(); - - /*Point3D aircraft = sgGeodToCart( Point3D(planes[i].lon*SGD_DEGREES_TO_RADIANS, - planes[i].lat*SGD_DEGREES_TO_RADIANS, - planes[i].alt*SG_FEET_TO_METER) );*/ - double course, distance; - calc_gc_course_dist(Point3D(lon*SGD_DEGREES_TO_RADIANS, lat*SGD_DEGREES_TO_RADIANS, 0.0), - Point3D(planes[i].lon*SGD_DEGREES_TO_RADIANS,planes[i].lat*SGD_DEGREES_TO_RADIANS, 0.0 ), - &course, &distance); - planes[i].dist = distance/SG_NM_TO_METER; - planes[i].brg = 360.0-course*SGD_RADIANS_TO_DEGREES; - - //cout << "Plane Id: " << planes[i].ident << " Distance to " << ident - // << " is " << planes[i].dist << " miles " << "Bearing " << planes[i].brg << endl; - - } -} - -// ======================================================================= -// Add plane to Approach list -// ======================================================================= -void FGApproach::AddPlane(const string& pid) { - - int i; - for ( i=0; i SGD_PI ) da -= SGD_2PI; - if ( fabs(da) > SGD_PI_2) { - //if ( x3*(x1-x2) < 0.0 && y3*(y1-y2) < 0.0) { - x3 *= -1.0; - y3 *= -1.0; - } - //cout << x3 << " " << y3 << endl; - double dis1 = x1-x2-x3; - double dis2 = y1-y2-y3; - dis = sqrt(dis); - da = atan2(dis2,dis1); - if ( da < 0.0 ) da += SGD_2PI; - if ( da < a3 ) dis *= -1.0; - //cout << dis1 << " " << dis2 << " " << da*SGD_RADIANS_TO_DEGREES << " " << h3 - // << " " << sqrt(dis1*dis1 + dis2*dis2) << " " << dis << endl; - //cout << atan2(dis2,dis1)*SGD_RADIANS_TO_DEGREES << " " << dis << endl; - - return dis; -} - - -// ======================================================================== -// Calculate new bear/dist given starting bear/dis, and offset radial, -// and distance. -// ======================================================================== -void FGApproach::calc_cd_head_dist(const double &h1, const double &d1, - const double &course, const double &dist, - double *h2, double *d2) -{ - double a1 = h1 * SGD_DEGREES_TO_RADIANS; - double a2 = course * SGD_DEGREES_TO_RADIANS; - double x1 = cos(a1) * d1; - double y1 = sin(a1) * d1; - double x2 = cos(a2) * dist; - double y2 = sin(a2) * dist; - - *d2 = sqrt((x1+x2)*(x1+x2) + (y1+y2)*(y1+y2)); - *h2 = atan2( (y1+y2), (x1+x2) ) * SGD_RADIANS_TO_DEGREES; - if ( *h2 < 0 ) *h2 = *h2+360; -} - - - -// ======================================================================== -// get heading and distance between two points; point1 ---> point2 -// ======================================================================== -void FGApproach::calc_hd_course_dist(const double &h1, const double &d1, - const double &h2, const double &d2, - double *course, double *dist) -{ - double a1 = h1 * SGD_DEGREES_TO_RADIANS; - double a2 = h2 * SGD_DEGREES_TO_RADIANS; - double x1 = cos(a1) * d1; - double y1 = sin(a1) * d1; - double x2 = cos(a2) * d2; - double y2 = sin(a2) * d2; - - *dist = sqrt( (y2-y1)*(y2-y1) + (x2-x1)*(x2-x1) ); - *course = atan2( (y2-y1), (x2-x1) ) * SGD_RADIANS_TO_DEGREES; - if ( *course < 0 ) *course = *course+360; - //cout << x1 << " " << y1 << " " << x2 << " " << y2 << " " << *dist << " " << *course << endl; -} - - - -int FGApproach::RemovePlane() { - - // first check if anything has to be done - bool rmplane = false; - int i; - - for (i=0; i range*SG_NM_TO_METER) { - rmplane = true; - break; - } - } - if (!rmplane) return num_planes; - - // now make a copy of the plane list - PlaneApp tmp[max_planes]; - for (i=0; i - -#include -#include -#include -#include -#include -#include - -#include
- -#ifdef SG_HAVE_STD_INCLUDES -# include -#include -#elif defined( SG_HAVE_NATIVE_SGI_COMPILERS ) -# include -#elif defined( __BORLANDC__ ) -# include -#else -# include -#include -#endif - -#if ! defined( SG_HAVE_NATIVE_SGI_COMPILERS ) -SG_USING_STD(istream); -#endif - -SG_USING_STD(string); - -#include "ATC.hxx" -#include "transmission.hxx" - -//DCL - a complete guess for now. -#define FG_APPROACH_DEFAULT_RANGE 100 - -// Contains all the information about a plane that the approach control needs -const int max_planes = 20; // max number of planes on the stack -const int max_wp = 10; // max number of waypoints for approach phase -const double max_ta = 130; // max turning angle for plane during approach -const double tbm = 2.0; // min time (in sec) between two messages -const double lfl = 10.0; // length of final leg - -struct PlaneApp { - - // variables for plane if it's on the radar - string ident; // indentification of plane - double lon; // longitude in degrees - double lat; // latitude in degrees - double alt; // Altitute above sea level in feet - double hdg; // heading of plane in degrees - double dist; // distance to airport in miles - double brg; // bearing relative to airport in degrees - double spd; // speed above ground - int contact; // contact with approach established? - // 0 = no contact yet - // 1 = in contact - // 2 = handed off to tower - double turn_rate; // standard turning rate of the plane in seconds per degree - double desc_rate; // standard descent rate of the plane in feets per minute - double clmb_rate; // standard climb rate of the plane in feets per minute - - // additional variables if contact has been established - int wpn; // number of waypoints - double wpts[max_wp][6]; // assigned waypoints for approach phase - // first wp in list is airport - // last waypoint point at which contact was established - // second index: 0 = bearing to airport - // second index: 1 = distance to airport - // second index: 2 = alt - // second index: 3 = ETA - // second index: 4 = heading to next waypoint - // second index: 5 = distance to next waypoint - - double dnwp; // distance to next waypoint - double dcc; // closest distance to current assigned course - double dnc; // closest distance to course from next to next to next wp - double aalt; // assigned altitude - double ahdg; // assigned heading - bool on_crs; // is the plane on course? - bool wp_change; // way point has changed - double tlm; // time when last message was sent - TransCode lmc; // code of last message -}; - - -class FGApproach : public FGATC { - - int bucket; - - string active_runway; - double active_rw_hdg; - double active_rw_lon; - double active_rw_lat; - double active_rw_len; - - int num_planes; // number of planes on the stack - PlaneApp planes[max_planes]; // Array of planes - string transmission; - bool first; - - SGPropertyNode_ptr comm1_node; - SGPropertyNode_ptr comm2_node; - - SGPropertyNode_ptr atcmenu_node; - SGPropertyNode_ptr atcopt0_node; - SGPropertyNode_ptr atcopt1_node; - SGPropertyNode_ptr atcopt2_node; - SGPropertyNode_ptr atcopt3_node; - SGPropertyNode_ptr atcopt4_node; - SGPropertyNode_ptr atcopt5_node; - SGPropertyNode_ptr atcopt6_node; - SGPropertyNode_ptr atcopt7_node; - SGPropertyNode_ptr atcopt8_node; - SGPropertyNode_ptr atcopt9_node; - - // for failure modeling - string trans_ident; // transmitted ident - bool approach_failed; // approach failed? - -public: - - FGApproach(void); - ~FGApproach(void); - - void Init(); - - void Update(double dt); - - // Add new plane to stack if not already registered - // Input: pid - id of plane (name) - // Output: "true" if added; "false" if already existend - void AddPlane(const string& pid); - - // Remove plane from stack if out of range - int RemovePlane(); - - inline double get_bucket() const { return bucket; } - inline int get_pnum() const { return num_planes; } - inline const string& get_trans_ident() { return trans_ident; } - -private: - - void calc_wp( const int &i); - - void update_plane_dat(); - - void get_active_runway(); - - void update_param(const int &i); - - double round_alt( bool hl, double alt ); - - double angle_diff_deg( const double &a1, const double &a2); - - void set_message(const string &s); - -// ======================================================================== -// get point2 given starting point1 and course and distance -// input: point1 = heading in degrees, distance -// input: course in degrees, distance -// output: point2 = heading in degrees, distance -// ======================================================================== - void calc_cd_head_dist(const double &h1, const double &d1, - const double &course, const double &dist, - double *h2, double *d2); - - -// ======================================================================== -// get heading and distance between two points; point2 ---> point1 -// input: point1 = heading in degrees, distance -// input: point2 = heading in degrees, distance -// output: course in degrees, distance -// ======================================================================== - void calc_hd_course_dist(const double &h1, const double &d1, - const double &h2, const double &d2, - double *course, double *dist); - - - -// ======================================================================== -// closest distance between a point and a straigt line in 2 dim. -// the input variables are given in (heading, distance) -// relative to a common point -// input: point = heading in degrees, distance -// input: straigt line = anker vector (heading in degrees, distance), -// heading of direction vector -// output: distance -// ======================================================================== - double calc_psl_dist(const double &h1, const double &d1, - const double &h2, const double &d2, - const double &h3); - - // Pointers to current users position - SGPropertyNode_ptr lon_node; - SGPropertyNode_ptr lat_node; - SGPropertyNode_ptr elev_node; - SGPropertyNode_ptr hdg_node; - SGPropertyNode_ptr speed_node; - SGPropertyNode_ptr etime_node; - - //Update the transmission string - void UpdateTransmission(void); - - friend istream& operator>> ( istream&, FGApproach& ); -}; - -#endif // _FG_APPROACH_HXX diff --git a/src/ATC/atis.cxx b/src/ATC/atis.cxx deleted file mode 100644 index 0957d18ac..000000000 --- a/src/ATC/atis.cxx +++ /dev/null @@ -1,225 +0,0 @@ -// atis.cxx - routines to generate the ATIS info string -// This is the implementation of the FGATIS class -// -// Written by David Luff, started October 2001. -// -// Copyright (C) 2001 David C Luff - david.luff@nottingham.ac.uk -// -// 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. - - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include - -#include // atoi() -#include // sprintf -#include -SG_USING_STD(string); - -#include STL_IOSTREAM -SG_USING_STD(cout); - -#include - -#include -#include - -#include
-#include
-#include - -#include "atis.hxx" -#include "commlist.hxx" -#include "ATCutils.hxx" -#include "ATCmgr.hxx" - -FGATIS::FGATIS() : - transmission(""), - trans_ident(""), - atis_failed(false), - refname("atis") - //type(ATIS) -{ - _vPtr = globals->get_ATC_mgr()->GetVoicePointer(ATIS); - _voiceOK = (_vPtr == NULL ? false : true); - _type = ATIS; -} - -FGATIS::~FGATIS() { -} - -// Main update function - checks whether we are displaying or not the correct message. -void FGATIS::Update(double dt) { - if(_display) { - if(_displaying) { - // Check if we need to update the message - // - basically every hour and if the weather changes significantly at the station - } else { - // We need to get and display the message - UpdateTransmission(); - //cout << "ATIS.CXX - calling ATCMgr to render transmission..." << endl; - Render(transmission, refname, true); - _displaying = true; - } - } else { - // We shouldn't be displaying - if(_displaying) { - //cout << "ATIS.CXX - calling NoRender()..." << endl; - NoRender(refname); - _displaying = false; - } - } -} - -// Sets the actual broadcast ATIS transmission. -void FGATIS::UpdateTransmission() { - double visibility; - char buf[10]; - int phonetic_id; - string phonetic_id_string; - string time_str = fgGetString("sim/time/gmt-string"); - int hours; - // int minutes; - - FGEnvironment stationweather = - ((FGEnvironmentMgr *)globals->get_subsystem("environment")) - ->getEnvironment(lat, lon, 0.0); - - transmission = ""; - - // UK CAA radiotelephony manual indicated ATIS transmissions start with "This is" - // Not sure about rest of the world though. - transmission += "This_is "; - // transmitted station name. - transmission += name; - // Add "Information" - transmission += " information"; - - //cout << "In atis.cxx, time_str = " << time_str << '\n'; - // Add the recording identifier - // For now we will assume we only transmit every hour - hours = atoi((time_str.substr(1,2)).c_str()); //Warning - this is fragile if the - //time string format changes - //cout << "In atis.cxx, hours = " << hours << endl; - phonetic_id = current_commlist->GetCallSign(ident, hours, 0); - phonetic_id_string = GetPhoneticIdent(phonetic_id); - transmission += " "; - transmission += phonetic_id_string; - - // Output the recording time. - we'll just output the last whole hour for now. - // FIXME - this only gets GMT time but that appears to be all the clock outputs for now - //cout << "in atis.cxx, time = " << time_str << endl; - transmission = transmission + " / Weather " + ConvertNumToSpokenDigits((time_str.substr(0,3) + "00")) + " hours zulu"; - - // Get the temperature - int temp; - temp = (int)stationweather.get_temperature_degc(); - - // HACK ALERT - at the moment the new environment subsystem returns bogus temperatures - // FIXME - take out this hack when this gets fixed upstream - if((temp < -50) || (temp > 60)) { - temp = 15; - } - - sprintf(buf, "%i", abs(temp)); - transmission += " / Temperature "; - if(temp < 0) { - transmission += "minus "; - } - string tempstr1 = buf; - string tempstr2; - transmission += ConvertNumToSpokenDigits(tempstr1); - transmission += " degrees_Celsius"; - - // Get the visibility - visibility = stationweather.get_visibility_m(); - sprintf(buf, "%i", int(visibility/1600)); - transmission += " / Visibility "; - tempstr1 = buf; - transmission += ConvertNumToSpokenDigits(tempstr1); - transmission += " miles"; - - // Get the cloudbase - // FIXME: kludge for now - if (strcmp(fgGetString("/environment/clouds/layer[0]/type"), "clear")) { - double cloudbase = - fgGetDouble("/environment/clouds/layer[0]/elevation-ft"); - // For some reason the altitude returned doesn't seem to correspond to the actual cloud altitude. - char buf3[10]; - char buf4[10]; - // cout << "cloudbase = " << cloudbase << endl; - sprintf(buf3, "%i", int(cloudbase)/1000); - sprintf(buf4, "%i", ((int)cloudbase % 1000)/100); - transmission += " / Cloudbase"; - if(int(cloudbase)/1000) { - tempstr1 = buf3; - transmission = transmission + " " + ConvertNumToSpokenDigits(tempstr1) + " thousand"; - } - if(((int)cloudbase % 1000)/100) { - tempstr1 = buf4; - transmission = transmission + " " + ConvertNumToSpokenDigits(tempstr1) + " hundred"; - } - transmission += " feet"; - } - - // Get the pressure / altimeter - double P = fgGetDouble("/environment/pressure-sea-level-inhg"); - if(ident.substr(0,2) == "EG" && fgGetBool("/sim/atc/use-millibars") == true) { - // Convert to millibars for the UK! - P *= 33.864; - sprintf(buf, "%.0f", P); - } else { - sprintf(buf, "%.2f", P); - } - transmission += " / Altimeter "; - tempstr1 = buf; - transmission += ConvertNumToSpokenDigits(tempstr1); - - // Based on the airport-id and wind get the active runway - //FGRunway *r; - double speed = stationweather.get_wind_speed_kt(); - double hdg = stationweather.get_wind_from_heading_deg(); - if (speed == 0) { - hdg = 270; // This forces West-facing rwys to be used in no-wind situations - // which is consistent with Flightgear's initial setup. - transmission += " / Winds_light_and_variable"; - } else { - // FIXME: get gust factor in somehow - char buf5[10]; - char buf6[10]; - sprintf(buf5, "%i", int(speed)); - sprintf(buf6, "%i", int(hdg)); - tempstr1 = buf5; - tempstr2 = buf6; - transmission = transmission + " / Winds " + ConvertNumToSpokenDigits(tempstr1) + " knots from " - + ConvertNumToSpokenDigits(tempstr2) + " degrees"; - } - - string rwy_no = globals->get_runways()->search(ident, int(hdg)); - if(rwy_no != "NN") { - transmission += " / Landing_and_departing_runway "; - transmission += ConvertRwyNumToSpokenString(atoi(rwy_no.c_str())); - //cout << "in atis.cxx, r.rwy_no = " << rwy_no << " r.id = " << r->id << " r.heading = " << r->heading << endl; - } - - // Anything else? - - transmission += " / Advise_controller_on_initial_contact_you_have "; - transmission += phonetic_id_string; - transmission += " /// "; -} diff --git a/src/ATC/atis.hxx b/src/ATC/atis.hxx deleted file mode 100644 index 05835d4a7..000000000 --- a/src/ATC/atis.hxx +++ /dev/null @@ -1,95 +0,0 @@ -// atis.hxx -- ATIS class -// -// Written by David Luff, started October 2001. -// Based on nav.hxx by Curtis Olson, started April 2000. -// -// Copyright (C) 2001 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - - -#ifndef _FG_ATIS_HXX -#define _FG_ATIS_HXX - -#include -#include - -#include -#include -#include -#include -#include - -#ifdef SG_HAVE_STD_INCLUDES -# include -# include -#elif defined( __BORLANDC__ ) || (__APPLE__) -# include -#else -# include -# include -#endif - -SG_USING_STD(istream); -SG_USING_STD(string); - -#include "ATC.hxx" - -//DCL - a complete guess for now. -#define FG_ATIS_DEFAULT_RANGE 30 - -class FGATIS : public FGATC { - - //atc_type type; - string transmission; // The actual ATIS transmission - // This is not stored in default.atis but is generated - // from the prevailing conditions when required. - - // for failure modeling - string trans_ident; // transmitted ident - bool atis_failed; // atis failed? - - // Aircraft position - // ATIS is actually a special case in that unlike other ATC eg.tower it doesn't actually know about - // or the whereabouts of the aircraft it is transmitting to. However, to ensure consistancy of - // operation with the other ATC classes the ATIS class must calculate range to the aircraft in order - // to decide whether to render the transmission - hence the users plane details must be stored. - //SGPropertyNode_ptr airplane_lon_node; - //SGPropertyNode_ptr airplane_lat_node; - //SGPropertyNode_ptr airplane_elev_node; - - public: - - FGATIS(void); - ~FGATIS(void); - - //run the ATIS instance - void Update(double dt); - - //inline void set_type(const atc_type tp) {type = tp;} - inline const string& get_trans_ident() { return trans_ident; } - inline void set_refname(const string& r) { refname = r; } - - private: - - string refname; // Holds the refname of a transmission in progress - - //Update the transmission string - void UpdateTransmission(void); - - friend istream& operator>> ( istream&, FGATIS& ); -}; - -#endif // _FG_ATIS_HXX diff --git a/src/ATC/commlist.cxx b/src/ATC/commlist.cxx deleted file mode 100644 index 4d27e1c79..000000000 --- a/src/ATC/commlist.cxx +++ /dev/null @@ -1,346 +0,0 @@ -// commlist.cxx -- comm frequency lookup class -// -// Written by David Luff and Alexander Kappes, started Jan 2003. -// Based on navlist.cxx by Curtis Olson, started April 2000. -// -// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt -// -// 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. - - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include -#include -#include -#include - -#include "commlist.hxx" -//#include "atislist.hxx" -#include "ATCutils.hxx" - - -FGCommList *current_commlist; - - -// Constructor -FGCommList::FGCommList( void ) { -} - - -// Destructor -FGCommList::~FGCommList( void ) { -} - - -// load the navaids and build the map -bool FGCommList::init( const SGPath& path ) { - - SGPath temp = path; - commlist_freq.erase(commlist_freq.begin(), commlist_freq.end()); - commlist_bck.erase(commlist_bck.begin(), commlist_bck.end()); - temp.append( "ATC/default.atis" ); - LoadComms(temp); - temp = path; - temp.append( "ATC/default.tower" ); - LoadComms(temp); - temp = path; - temp.append( "ATC/default.ground" ); - LoadComms(temp); - temp = path; - temp.append( "ATC/default.approach" ); - LoadComms(temp); - return true; -} - - -bool FGCommList::LoadComms(const SGPath& path) { - - sg_gzifstream fin( path.str() ); - if ( !fin.is_open() ) { - SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() ); - exit(-1); - } - - // read in each line of the file - fin >> skipcomment; - -#ifdef __MWERKS__ - char c = 0; - while ( fin.get(c) && c != '\0' ) { - fin.putback(c); -#else - while ( !fin.eof() ) { -#endif - ATCData a; - fin >> a; - if(a.type == INVALID) { - SG_LOG(SG_GENERAL, SG_DEBUG, "WARNING - INVALID type found in " << path.str() << '\n'); - } else { - // Push all stations onto frequency map - commlist_freq[a.freq].push_back(a); - - // Push non-atis stations onto bucket map as well - // In fact, push all stations onto bucket map for now so FGATCMgr::GetFrequency() works. - //if(a.type != ATIS) { - // get bucket number - SGBucket bucket(a.lon, a.lat); - int bucknum = bucket.gen_index(); - commlist_bck[bucknum].push_back(a); - //} - } - - fin >> skipcomment; - } - - fin.close(); - return true; -} - - -// query the database for the specified frequency, lon and lat are in -// degrees, elev is in meters -// If no atc_type is specified, it returns true if any non-invalid type is found -// If atc_type is specifed, returns true only if the specified type is found -bool FGCommList::FindByFreq( double lon, double lat, double elev, double freq, - ATCData* ad, atc_type tp ) -{ - lon *= SGD_DEGREES_TO_RADIANS; - lat *= SGD_DEGREES_TO_RADIANS; - - // HACK - if freq > 1000 assume it's in KHz, otherwise assume MHz. - // A bit ugly but it works for now!!!! - comm_list_type stations; - if(freq > 1000.0) { - stations = commlist_freq[(int)freq]; - } else { - stations = commlist_freq[(int)(freq*100.0 + 0.5)]; - } - comm_list_iterator current = stations.begin(); - comm_list_iterator last = stations.end(); - - // double az1, az2, s; - Point3D aircraft = sgGeodToCart( Point3D(lon, lat, elev) ); - Point3D station; - const double orig_max_d = 1e100; - double max_d = orig_max_d; - double d; - // TODO - at the moment this loop returns the first match found in range - // We want to return the closest match in the event of a frequency conflict - for ( ; current != last ; ++current ) { - //cout << "testing " << current->get_ident() << endl; - station = Point3D(current->x, current->y, current->z); - //cout << "aircraft = " << aircraft << endl; - //cout << "station = " << station << endl; - - d = aircraft.distance3Dsquared( station ); - - //cout << " dist = " << sqrt(d) - // << " range = " << current->range * SG_NM_TO_METER << endl; - - // TODO - match up to twice the published range so we can model - // reduced signal strength - // NOTE The below is squared since we match to distance3Dsquared (above) to avoid a sqrt. - if ( d < (current->range * SG_NM_TO_METER - * current->range * SG_NM_TO_METER ) ) { - //cout << "matched = " << current->ident << endl; - if((tp == INVALID) || (tp == (*current).type)) { - if(d < max_d) { - max_d = d; - *ad = *current; - } - } - } - } - - if(max_d < orig_max_d) { - return true; - } else { - return false; - } -} - -int FGCommList::FindByPos(double lon, double lat, double elev, double range, comm_list_type* stations, atc_type tp) -{ - // number of relevant stations found within range - int found = 0; - stations->erase(stations->begin(), stations->end()); - - // get bucket number for plane position - SGBucket buck(lon, lat); - - // get neigboring buckets - int bx = (int)( range*SG_NM_TO_METER / buck.get_width_m() / 2); - int by = (int)( range*SG_NM_TO_METER / buck.get_height_m() / 2 ); - - // loop over bucket range - for ( int i=-bx; i<=bx; i++) { - for ( int j=-by; j<=by; j++) { - buck = sgBucketOffset(lon, lat, i, j); - long int bucket = buck.gen_index(); - comm_list_type Fstations = commlist_bck[bucket]; - comm_list_iterator current = Fstations.begin(); - comm_list_iterator last = Fstations.end(); - - double rlon = lon * SGD_DEGREES_TO_RADIANS; - double rlat = lat * SGD_DEGREES_TO_RADIANS; - - // double az1, az2, s; - Point3D aircraft = sgGeodToCart( Point3D(rlon, rlat, elev) ); - Point3D station; - double d; - for(; current != last; ++current) { - if((current->type == tp) || (tp == INVALID)) { - station = Point3D(current->x, current->y, current->z); - d = aircraft.distance3Dsquared( station ); - // NOTE The below is squared since we match to distance3Dsquared (above) to avoid a sqrt. - if ( d < (current->range * SG_NM_TO_METER - * current->range * SG_NM_TO_METER ) ) { - stations->push_back(*current); - ++found; - } - } - } - } - } - return found; -} - - -// Returns the distance in meters to the closest station of a given type, -// with the details written into ATCData& ad. If no type is specifed simply -// returns the distance to the closest station of any type. -// Returns -9999 if no stations found within max_range in nautical miles (default 100 miles). -// Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if -// say 300 miles is specifed 10, then 100, then 1000 will be searched, breaking at first result -// and giving up after 1000. -double FGCommList::FindClosest( double lon, double lat, double elev, ATCData& ad, atc_type tp, double max_range) { - int num_stations = 0; - int range = 10; - comm_list_type stations; - comm_list_iterator itr; - double distance = -9999.0; - - while(num_stations == 0) { - num_stations = FindByPos(lon, lat, elev, range, &stations, tp); - if(num_stations) { - double closest = max_range * SG_NM_TO_METER; - double tmp; - for(itr = stations.begin(); itr != stations.end(); ++itr) { - ATCData ad2 = *itr; - //Point3D p1(*itr.lon, *itr.lat, *itr.elev); - Point3D p1(ad2.lon, ad2.lat, ad2.elev); - const FGAirport *a = fgFindAirportID(ad2.ident); - if (a) { - Point3D p2(lon, lat, elev); - tmp = dclGetHorizontalSeparation(p1, p2); - if(tmp <= closest) { - closest = tmp; - distance = tmp; - ad = *itr; - } - } - } - //cout << "Closest station is " << ad.ident << " at a range of " << distance << " meters\n"; - return(distance); - } - if(range > max_range) { - break; - } - range *= 10; - } - return(-9999.0); -} - - -// Find by Airport code. -// This is basically a wrapper for a call to the airport database to get the airport -// position followed by a call to FindByPos(...) -bool FGCommList::FindByCode( const string& ICAO, ATCData& ad, atc_type tp ) { - const FGAirport *a = fgFindAirportID( ICAO); - if ( a) { - comm_list_type stations; - int found = FindByPos(a->getLongitude(), a->getLatitude(), a->getElevation(), 10.0, &stations, tp); - if(found) { - comm_list_iterator itr = stations.begin(); - while(itr != stations.end()) { - if(((*itr).ident == ICAO) && ((*itr).type == tp)) { - ad = *itr; - return true; - } - ++itr; - } - } - } else { - return false; - } - return false; -} - - -// TODO - this function should move somewhere else eventually! -// Return an appropriate call-sign for an ATIS transmission. -int FGCommList::GetCallSign( const string& apt_id, int hours, int mins ) -{ - atis_transmission_type tran; - - if(atislog.find(apt_id) == atislog.end()) { - // This station has not transmitted yet - return a random identifier - // and add the transmission to the log - tran.hours = hours; - tran.mins = mins; - sg_srandom_time(); - tran.callsign = int(sg_random() * 25) + 1; // This *should* give a random int between 1 and 26 - //atislog[apt_id].push_back(tran); - atislog[apt_id] = tran; - } else { - // This station has transmitted - calculate the appropriate identifier - // and add the transmission to the log if it has changed - tran = atislog[apt_id]; - // This next bit assumes that no-one comes back to the same ATIS station - // after running FlightGear for more than 24 hours !! - if((tran.hours == hours) && (tran.mins == mins)) { - return(tran.callsign); - } else { - if(tran.hours == hours) { - // The minutes must have changed - tran.mins = mins; - tran.callsign++; - } else { - if(hours < tran.hours) { - hours += 24; - } - tran.callsign += (hours - tran.hours); - if(mins != 0) { - // Assume transmissions were made on every hour - tran.callsign++; - } - tran.hours = hours; - tran.mins = mins; - } - // Wrap if we've exceeded Zulu - if(tran.callsign > 26) { - tran.callsign -= 26; - } - // And write the new transmission to the log - atislog[apt_id] = tran; - } - } - return(tran.callsign); -} diff --git a/src/ATC/commlist.hxx b/src/ATC/commlist.hxx deleted file mode 100644 index 1cb2f4855..000000000 --- a/src/ATC/commlist.hxx +++ /dev/null @@ -1,140 +0,0 @@ -// commlist.hxx -- comm frequency lookup class -// -// Written by David Luff and Alexander Kappes, started Jan 2003. -// Based on navlist.hxx by Curtis Olson, started April 2000. -// -// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt -// -// 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. - -/***************************************************************** -* -* FGCommList is used to store communication frequency information -* for the ATC and AI subsystems. Two maps are maintained - one -* searchable by location and one searchable by frequency. The -* data structure returned from the search is the ATCData struct -* defined in ATC.hxx, containing location, frequency, name, range -* and type of the returned station. -* -******************************************************************/ - -#ifndef _FG_COMMLIST_HXX -#define _FG_COMMLIST_HXX - - -#include -#include - -#include -#include -#include - -#include "ATC.hxx" -#include "atis.hxx" - -SG_USING_STD(list); -SG_USING_STD(map); -SG_USING_STD(vector); -SG_USING_STD(string); - -// A list of ATC stations -typedef list < ATCData > comm_list_type; -typedef comm_list_type::iterator comm_list_iterator; -typedef comm_list_type::const_iterator comm_list_const_iterator; - -// A map of ATC station lists -typedef map < int, comm_list_type > comm_map_type; -typedef comm_map_type::iterator comm_map_iterator; -typedef comm_map_type::const_iterator comm_map_const_iterator; - - -class FGCommList { - -public: - - FGCommList(); - ~FGCommList(); - - // load all comm frequencies and build the map - bool init( const SGPath& path ); - - // query the database for the specified frequency, lon and lat are - // in degrees, elev is in meters. - // If no atc_type is specified, it returns true if any non-invalid type is found. - // If atc_type is specifed, returns true only if the specified type is found. - // Returns the station closest to the supplied position. - // The data found is written into the passed-in ATCData structure. - bool FindByFreq( double lon, double lat, double elev, double freq, ATCData* ad, atc_type tp = INVALID ); - - // query the database by location, lon and lat are in degrees, elev is in meters, range is in nautical miles. - // Returns the number of stations of the specified atc_type tp that are in range of the position defined by - // lon, lat and elev, and pushes them into stations. - // If no atc_type is specifed, returns the number of all stations in range, and pushes them into stations - // ** stations is erased before use ** - int FindByPos( double lon, double lat, double elev, double range, comm_list_type* stations, atc_type tp = INVALID ); - - // Returns the distance in meters to the closest station of a given type, - // with the details written into ATCData& ad. If no type is specifed simply - // returns the distance to the closest station of any type. - // Returns -9999 if no stations found within max_range in nautical miles (default 100 miles). - // Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if - // say 300 miles is specifed 10, then 100, then 1000 will be searched, breaking at first result - // and giving up after 1000. - // !!!Be warned that searching anything over 100 miles will pause the sim unacceptably!!! - // (The ability to search longer ranges should be used during init only). - double FindClosest( double lon, double lat, double elev, ATCData& ad, atc_type tp = INVALID, double max_range = 100.0 ); - - // Find by Airport code. - bool FindByCode( const string& ICAO, ATCData& ad, atc_type tp = INVALID ); - - // Return the callsign for an ATIS transmission given transmission time and airport id - // This maybe should get moved somewhere else!! - int GetCallSign( const string& apt_id, int hours, int mins ); - -private: - - // Comm stations mapped by frequency - comm_map_type commlist_freq; - - // Comm stations mapped by bucket - comm_map_type commlist_bck; - - // Load comms from a specified path (which must include the filename) - bool LoadComms(const SGPath& path); - -//----------- This stuff is left over from atislist.[ch]xx and maybe should move somewhere else - // Add structure and map for storing a log of atis transmissions - // made in this session of FlightGear. This allows the callsign - // to be allocated correctly wrt time. - typedef struct { - int hours; - int mins; - int callsign; - } atis_transmission_type; - - typedef map < string, atis_transmission_type > atis_log_type; - typedef atis_log_type::iterator atis_log_iterator; - typedef atis_log_type::const_iterator atis_log_const_iterator; - - atis_log_type atislog; -//----------------------------------------------------------------------------------------------- - -}; - - -extern FGCommList *current_commlist; - - -#endif // _FG_COMMLIST_HXX diff --git a/src/ATC/ground.cxx b/src/ATC/ground.cxx deleted file mode 100644 index 4adfa9958..000000000 --- a/src/ATC/ground.cxx +++ /dev/null @@ -1,732 +0,0 @@ -// FGGround - a class to provide ground control at larger airports. -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include -#include -#include -#include
- -#include -#include STL_FSTREAM - -#include "ground.hxx" -#include "ATCutils.hxx" -#include "AILocalTraffic.hxx" -#include "ATCmgr.hxx" - -SG_USING_STD(ifstream); -SG_USING_STD(cout); - -node::node() { -} - -node::~node() { - for(unsigned int i=0; i < arcs.size(); ++i) { - delete arcs[i]; - } -} - -// Make sure that a_path.cost += distance is safe from the moment it's created. -a_path::a_path() { - cost = 0; -} - -FGGround::FGGround() { - ATCmgr = globals->get_ATC_mgr(); - _type = GROUND; - networkLoadOK = false; - ground_traffic.erase(ground_traffic.begin(), ground_traffic.end()); - ground_traffic_itr = ground_traffic.begin(); - - // Init the property nodes - TODO - need to make sure we're getting surface winds. - wind_from_hdg = fgGetNode("/environment/wind-from-heading-deg", true); - wind_speed_knots = fgGetNode("/environment/wind-speed-kt", true); - - // TODO - get the actual airport elevation - aptElev = 0.0; -} - -FGGround::FGGround(const string& id) { - ATCmgr = globals->get_ATC_mgr(); - networkLoadOK = false; - ground_traffic.erase(ground_traffic.begin(), ground_traffic.end()); - ground_traffic_itr = ground_traffic.begin(); - ident = id; - - // Init the property nodes - TODO - need to make sure we're getting surface winds. - wind_from_hdg = fgGetNode("/environment/wind-from-heading-deg", true); - wind_speed_knots = fgGetNode("/environment/wind-speed-kt", true); - - // TODO - get the actual airport elevation - aptElev = 0.0; -} - -FGGround::~FGGround() { -} - -void FGGround::ParseRwyExits(node* np, char* es) { - char* token; - char estr[20]; - strcpy(estr, es); - const char delimiters[] = "-"; - token = strtok(estr, delimiters); - while(token != NULL) { - int i = atoi(token); - //cout << "token = " << token << endl; - //cout << "rwy number = " << i << endl; - //runways[(atoi(token))].exits.push_back(np); - runways[i].exits.push_back(np); - //cout << "token = " << token << '\n'; - token = strtok(NULL, delimiters); - } -} - - -// Load the ground logical network of the current instances airport -// Return true if successfull. -// TODO - currently the file is assumed to reside in the base/ATC directory. -// This might change to something more thought out in the future. -// NOTE - currently it is assumed that all nodes are loaded before any arcs. -// It won't work ATM if this doesn't hold true. -bool FGGround::LoadNetwork() { - node* np; - arc* ap; - Gate* gp; - - int gateCount = 0; // This is used to allocate gateID's from zero upwards - // This may well change in the future - probably to reading in the real-world - // gate numbers from file. - - ifstream fin; - SGPath path = globals->get_fg_root(); - //string taxiPath = "ATC/" + ident + ".taxi"; - string taxiPath = "ATC/KEMT.taxi"; // FIXME - HARDWIRED FOR TESTING - path.append(taxiPath); - - SG_LOG(SG_ATC, SG_INFO, "Trying to read taxiway data for " << ident << "..."); - //cout << "Trying to read taxiway data for " << ident << "..." << endl; - fin.open(path.c_str(), ios::in); - if(!fin) { - SG_LOG(SG_ATC, SG_ALERT, "Unable to open taxiway data input file " << path.c_str()); - //cout << "Unable to open taxiway data input file " << path.c_str() << endl; - return(false); - } - - char ch; - char buf[30]; - while(!fin.eof()) { - fin >> buf; - // Node, arc, or [End]? - //cout << "Read in ground network element type = " << buf << endl; - if(!strcmp(buf, "[End]")) { // TODO - maybe make this more robust to spelling errors by just looking for '[' - SG_LOG(SG_ATC, SG_INFO, "Done reading " << path.c_str() << endl); - break; - } else if(!strcmp(buf, "N")) { - // Node - np = new node; - np->struct_type = NODE; - fin >> buf; - np->nodeID = atoi(buf); - fin >> buf; - np->pos.setlon(atof(buf)); - fin >> buf; - np->pos.setlat(atof(buf)); - fin >> buf; - np->pos.setelev(atof(buf)); - fin >> buf; // node type - if(!strcmp(buf, "J")) { - np->type = JUNCTION; - } else if(!strcmp(buf, "T")) { - np->type = TJUNCTION; - } else if(!strcmp(buf, "H")) { - np->type = HOLD; - } else { - SG_LOG(SG_ATC, SG_ALERT, "**** ERROR ***** Unknown node type in taxi network...\n"); - delete np; - return(false); - } - fin >> buf; // rwy exit information - gets parsed later - FRAGILE - will break if buf is reused. - // Now the name - fin >> ch; // strip the leading " off - np->name = ""; - while(1) { - fin.unsetf(ios::skipws); - fin >> ch; - if((ch == '"') || (ch == 0x0A)) { - break; - } // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the " - np->name += ch; - } - fin.setf(ios::skipws); - network.push_back(np); - // FIXME - fragile - replies on buf not getting modified from exits read to here - // see if we also need to push it onto the runway exit list - //cout << "strlen(buf) = " << strlen(buf) << endl; - if(strlen(buf) > 2) { - //cout << "Calling ParseRwyExits for " << buf << endl; - ParseRwyExits(np, buf); - } - } else if(!strcmp(buf, "A")) { - ap = new arc; - ap->struct_type = ARC; - fin >> buf; - ap->n1 = atoi(buf); - fin >> buf; - ap->n2 = atoi(buf); - fin >> buf; - if(!strcmp(buf, "R")) { - ap->type = RUNWAY; - } else if(!strcmp(buf, "T")) { - ap->type = TAXIWAY; - } else { - SG_LOG(SG_ATC, SG_ALERT, "**** ERROR ***** Unknown arc type in taxi network...\n"); - delete ap; - return(false); - } - // directed? - fin >> buf; - if(!strcmp(buf, "Y")) { - ap->directed = true; - } else if(!strcmp(buf, "N")) { - ap->directed = false; - } else { - SG_LOG(SG_ATC, SG_ALERT, "**** ERROR ***** Unknown arc directed value in taxi network - should be Y/N !!!\n"); - delete ap; - return(false); - } - // Now the name - ap->name = ""; - while(1) { - fin.unsetf(ios::skipws); - fin >> ch; - ap->name += ch; - if((ch == '"') || (ch == 0x0A)) { - break; - } // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the " - } - fin.setf(ios::skipws); - ap->distance = (int)dclGetHorizontalSeparation(network[ap->n1]->pos, network[ap->n2]->pos); - //cout << "Distance = " << ap->distance << '\n'; - network[ap->n1]->arcs.push_back(ap); - network[ap->n2]->arcs.push_back(ap); - } else if(!strcmp(buf, "G")) { - gp = new Gate; - gp->struct_type = NODE; - gp->type = GATE; - fin >> buf; - gp->nodeID = atoi(buf); - fin >> buf; - gp->pos.setlon(atof(buf)); - fin >> buf; - gp->pos.setlat(atof(buf)); - fin >> buf; - gp->pos.setelev(atof(buf)); - fin >> buf; // gate type - ignore this for now - fin >> buf; // gate heading - gp->heading = atoi(buf); - // Now the name - gp->name = ""; - while(1) { - fin.unsetf(ios::skipws); - fin >> ch; - gp->name += ch; - if((ch == '"') || (ch == 0x0A)) { - break; - } // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the " - } - fin.setf(ios::skipws); - gp->id = gateCount; // Warning - this will likely change in the future. - gp->used = false; - network.push_back(gp); - gates[gateCount] = gp; - gateCount++; - } else { - // Something has gone seriously pear-shaped - SG_LOG(SG_ATC, SG_ALERT, "********* ERROR - unknown ground network element type... aborting read of " << path.c_str() << '\n'); - return(false); - } - - fin >> skipeol; - } - return(true); -} - -void FGGround::Init() { - untowered = false; - - // Figure out which is the active runway - TODO - it would be better to have ground call tower - // for runway operation details, but at the moment we can't guarantee that tower control at a given airport - // will be initialised before ground so we can't do that. - DoRwyDetails(); - //cout << "In FGGround::Init, active rwy is " << activeRwy << '\n'; - ortho.Init(rwy.threshold_pos, rwy.hdg); - - networkLoadOK = LoadNetwork(); -} - -void FGGround::Update(double dt) { - // Each time step, what do we need to do? - // We need to go through the list of outstanding requests and acknowedgements - // and process at least one of them. - // We need to go through the list of planes under our control and check if - // any need to be addressed. - // We need to check for planes not under our control coming within our - // control area and address if necessary. - - // Lets take the example of a plane which has just contacted ground - // following landing - presumably requesting where to go? - // First we need to establish the position of the plane within the logical network. - // Next we need to decide where its going. - - if(ground_traffic.size()) { - if(ground_traffic_itr == ground_traffic.end()) { - ground_traffic_itr = ground_traffic.begin(); - } - - //Process(*ground_traffic_itr); - GroundRec* g = *ground_traffic_itr; - if(g->taxiRequestOutstanding) { - double responseTime = 10.0; // seconds - this should get more sophisticated at some point - if(g->clearanceCounter > responseTime) { - // DO CLEARANCE - // TODO - move the mechanics of making up the transmission out of the main Update(...) routine. - string trns = ""; - trns += g->plane.callsign; - trns += " taxi holding point runway "; // TODO - add the holding point name - // eg " taxi holding point G2 runway " - trns += ConvertRwyNumToSpokenString(activeRwy); - if(_display) { - fgSetString("/sim/messages/ground", trns.c_str()); - } - g->planePtr->RegisterTransmission(1); // cleared to taxi - g->clearanceCounter = 0.0; - g->taxiRequestOutstanding = false; - } else { - g->clearanceCounter += (dt * ground_traffic.size()); - } - } else if(((FGAILocalTraffic*)(g->planePtr))->AtHoldShort()) { // That's a hack - eventually we should monitor actual position - // HACK ALERT - the automatic cast to AILocalTraffic has to go once we have other sorts working!!!!! FIXME TODO - // NOTE - we don't need to do the contact tower bit unless we have separate tower and ground - string trns = g->plane.callsign; - trns += " contact Tower "; - double f = globals->get_ATC_mgr()->GetFrequency(ident, TOWER) / 100.0; - char buf[10]; - sprintf(buf, "%.2f", f); - trns += buf; - if(_display) { - fgSetString("/sim/messages/ground", trns.c_str()); - } - g->planePtr->RegisterTransmission(2); // contact tower - delete *ground_traffic_itr; - ground_traffic.erase(ground_traffic_itr); - ground_traffic_itr = ground_traffic.begin(); - } - ++ground_traffic_itr; - } - - // Call the base class update for the response time handling. - FGATC::Update(dt); -} - -// Figure out which runways are active. -// For now we'll just be simple and do one active runway - eventually this will get much more complex -// Copied from FGTower - TODO - it would be better to implement this just once, and have ground call tower -// for runway operation details, but at the moment we can't guarantee that tower control at a given airport -// will be initialised before ground so we can't do that. -void FGGround::DoRwyDetails() { - //cout << "GetRwyDetails called" << endl; - - // Based on the airport-id and wind get the active runway - - //wind - double hdg = wind_from_hdg->getDoubleValue(); - double speed = wind_speed_knots->getDoubleValue(); - hdg = (speed == 0.0 ? 270.0 : hdg); - //cout << "Heading = " << hdg << '\n'; - - FGRunway runway; - bool rwyGood = globals->get_runways()->search(ident, int(hdg), &runway); - if(rwyGood) { - activeRwy = runway._rwy_no; - rwy.rwyID = runway._rwy_no; - SG_LOG(SG_ATC, SG_INFO, "In FGGround, active runway for airport " << ident << " is " << activeRwy); - - // Get the threshold position - double other_way = runway._heading - 180.0; - while(other_way <= 0.0) { - other_way += 360.0; - } - // move to the +l end/center of the runway - //cout << "Runway center is at " << runway._lon << ", " << runway._lat << '\n'; - Point3D origin = Point3D(runway._lon, runway._lat, aptElev); - Point3D ref = origin; - double tshlon, tshlat, tshr; - double tolon, tolat, tor; - rwy.length = runway._length * SG_FEET_TO_METER; - geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), other_way, - rwy.length / 2.0 - 25.0, &tshlat, &tshlon, &tshr ); - geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), runway._heading, - rwy.length / 2.0 - 25.0, &tolat, &tolon, &tor ); - // Note - 25 meters in from the runway end is a bit of a hack to put the plane ahead of the user. - // now copy what we need out of runway into rwy - rwy.threshold_pos = Point3D(tshlon, tshlat, aptElev); - Point3D takeoff_end = Point3D(tolon, tolat, aptElev); - //cout << "Threshold position = " << tshlon << ", " << tshlat << ", " << aptElev << '\n'; - //cout << "Takeoff position = " << tolon << ", " << tolat << ", " << aptElev << '\n'; - rwy.hdg = runway._heading; - // Set the projection for the local area based on this active runway - ortho.Init(rwy.threshold_pos, rwy.hdg); - rwy.end1ortho = ortho.ConvertToLocal(rwy.threshold_pos); // should come out as zero - rwy.end2ortho = ortho.ConvertToLocal(takeoff_end); - } else { - SG_LOG(SG_ATC, SG_ALERT, "Help - can't get good runway in FGTower!!"); - activeRwy = "NN"; - } -} - -// Return a random gate ID of an unused gate. -// Two error values may be returned and must be checked for by the calling function: -// -2 signifies that no gates exist at this airport. -// -1 signifies that all gates are currently full. -int FGGround::GetRandomGateID() { - // Check that this airport actually has some gates!! - if(!gates.size()) { - return(-2); - } - - gate_vec_type gateVec; - int num = 0; - int thenum; - int ID; - - gatesItr = gates.begin(); - while(gatesItr != gates.end()) { - if((gatesItr->second)->used == false) { - gateVec.push_back(gatesItr->second); - num++; - } - ++gatesItr; - } - - // Check that there are some unused gates! - if(!gateVec.size()) { - return(-1); - } - - // Randomly select one from the list - sg_srandom_time(); - thenum = (int)(sg_random() * gateVec.size()); - ID = gateVec[thenum]->id; - - return(ID); -} - -// Return a pointer to an unused gate node -Gate* FGGround::GetGateNode() { - int id = GetRandomGateID(); - if(id < 0) { - return(NULL); - } else { - return(gates[id]); - } -} - - -node* FGGround::GetHoldShortNode(const string& rwyID) { - return(NULL); // TODO - either implement me or remove me!!! -} - - -// WARNING - This is hardwired to my prototype logical network format -// and will almost certainly change when Bernie's stuff comes on-line. -// Returns NULL if it can't find a valid node. -node* FGGround::GetThresholdNode(const string& rwyID) { - // For now go through all the nodes and parse their names - // Maybe in the future we'll map threshold nodes by ID - //cout << "Size of network is " << network.size() << '\n'; - for(unsigned int i=0; ipath.push_back(A); - pathPtr->cost = 0; - pathMap[A->nodeID] = pathPtr; - bool solution_found = false; // Flag to indicate that at least one candidate path has been found - int solution_cost = -1; // Cost of current best cost solution. -1 indicates no solution found yet. - a_path solution_path; - - node* nPtr; // nPtr is used to point to the node we are currently working with - - while(nodesLeft.size()) { - //cout << "\n*****nodesLeft*****\n"; - //for(unsigned int i=0; inodeID << '\n'; - //} - //cout << "*******************\n\n"; - nPtr = *nodesLeft.begin(); // Thought - definate optimization possibilities here in the choice of which nodes we process first. - nodesLeft.erase(nodesLeft.begin()); - //cout << "Walking out from node " << nPtr->nodeID << '\n'; - for(unsigned int i=0; iarcs.size(); ++i) { - //cout << "ARC TO " << ((nPtr->arcs[i]->n1 == nPtr->nodeID) ? nPtr->arcs[i]->n2 : nPtr->arcs[i]->n1) << '\n'; - } - if((solution_found) && (solution_cost <= pathMap[nPtr->nodeID]->cost)) { - // Do nothing - we've already found a solution and this partial path is already more expensive - } else { - // This path could still be better than the current solution - check it out - for(unsigned int i=0; i<(nPtr->arcs.size()); i++) { - // Map the new path against the end node, ie. *not* the one we just started with. - unsigned int end_nodeID = ((nPtr->arcs[i]->n1 == nPtr->nodeID) ? nPtr->arcs[i]->n2 : nPtr->arcs[i]->n1); - //cout << "end_nodeID = " << end_nodeID << '\n'; - //cout << "pathMap size is " << pathMap.size() << '\n'; - if(end_nodeID == nPtr->nodeID) { - //cout << "Circular arc!\n"; - // Then its a circular arc - don't bother!! - //nPtr->arcs.erase(nPtr->arcs.begin() + i); - } else { - // see if the end node is already in the map or not - if(pathMap.find(end_nodeID) == pathMap.end()) { - //cout << "Not in the map" << endl;; - // Not in the map - easy! - pathPtr = new a_path; - pathsCreated++; - *pathPtr = *pathMap[nPtr->nodeID]; // *copy* the path - pathPtr->path.push_back(nPtr->arcs[i]); - pathPtr->path.push_back(network[end_nodeID]); - pathPtr->cost += nPtr->arcs[i]->distance; - pathMap[end_nodeID] = pathPtr; - nodesLeft.push_back(network[end_nodeID]); // By definition this can't be in the list already, or - // it would also have been in the map and hence OR'd with this one. - if(end_nodeID == B->nodeID) { - //cout << "Solution found!!!" << endl; - // Since this node wasn't in the map this is by definition the first solution - solution_cost = pathPtr->cost; - solution_path = *pathPtr; - solution_found = true; - } - } else { - //cout << "Already in the map" << endl; - // In the map - not so easy - need to get rid of an arc from the higher cost one. - //cout << "Current cost of node " << end_nodeID << " is " << pathMap[end_nodeID]->cost << endl; - int newCost = pathMap[nPtr->nodeID]->cost + nPtr->arcs[i]->distance; - //cout << "New cost is of node " << nPtr->nodeID << " is " << newCost << endl; - if(newCost >= pathMap[end_nodeID]->cost) { - // No need to do anything. - //cout << "Not doing anything!" << endl; - } else { - delete pathMap[end_nodeID]; - pathsCreated--; - - pathPtr = new a_path; - pathsCreated++; - *pathPtr = *pathMap[nPtr->nodeID]; // *copy* the path - pathPtr->path.push_back(nPtr->arcs[i]); - pathPtr->path.push_back(network[end_nodeID]); - pathPtr->cost += nPtr->arcs[i]->distance; - pathMap[end_nodeID] = pathPtr; - - // We need to add this node to the list-to-do again to force a recalculation - // onwards from this node with the new lower cost to node cost. - nodesLeft.push_back(network[end_nodeID]); - - if(end_nodeID == B->nodeID) { - //cout << "Solution found!!!" << endl; - // Need to check if there is a previous better solution - if((solution_cost < 0) || (pathPtr->cost < solution_cost)) { - solution_cost = pathPtr->cost; - solution_path = *pathPtr; - solution_found = true; - } - } - } - } - } - } - } - } - - // delete all the paths before returning - shortest_path_map_iterator spItr = pathMap.begin(); - while(spItr != pathMap.end()) { - if(spItr->second != NULL) { - delete spItr->second; - --pathsCreated; - } - ++spItr; - } - - //cout << "pathsCreated = " << pathsCreated << '\n'; - if(pathsCreated > 0) { - SG_LOG(SG_ATC, SG_ALERT, "WARNING - Possible memory leak in FGGround::GetShortestPath\n\ - Please report to flightgear-devel@flightgear.org\n"); - } - - //cout << (solution_found ? "Result: solution found\n" : "Result: no solution found\n"); - return(solution_path.path); // TODO - we really ought to have a fallback position incase a solution isn't found. -} - - - -// Randomly or otherwise populate some of the gates with parked planes -// (might eventually be done by the AIMgr if and when lots of AI traffic is generated) - -// Return a list of exits from a given runway -// It is up to the calling function to check for non-zero size of returned array before use -node_array_type FGGround::GetExits(const string& rwyID) { - // FIXME - get a 07L or similar in here and we're stuffed!!! - return(runways[atoi(rwyID.c_str())].exits); -} - -void FGGround::RequestDeparture(const PlaneRec& plane, FGAIEntity* requestee) { - // For now we'll just automatically clear all planes to the runway hold. - // This communication needs to be delayed 20 sec or so from receiving the request. - // Even if display=false we still need to start the timer in case display=true when communication starts. - // We also need to bear in mind we also might have other outstanding communications, although for now we'll punt that issue! - // FIXME - sort the above! - - // HACK - assume that anything requesting departure is new for now - FIXME LATER - GroundRec* g = new GroundRec; - g->plane = plane; - g->planePtr = requestee; - g->taxiRequestOutstanding = true; - g->clearanceCounter = 0; - g->cleared = false; - g->incoming = false; - // TODO - need to handle the next 3 as well - //Point3D current_pos; - //node* destination; - //node* last_clearance; - - ground_traffic.push_back(g); -} - -#if 0 -void FGGround::NewArrival(plane_rec plane) { - // What are we going to do here? - // We need to start a new ground_rec and add the plane_rec to it - // We need to decide what gate we are going to clear it to. - // Then we need to add clearing it to that gate to the pending transmissions queue? - or simply transmit? - // Probably simply transmit for now and think about a transmission queue later if we need one. - // We might need one though in order to add a little delay for response time. - ground_rec* g = new ground_rec; - g->plane_rec = plane; - g->current_pos = ConvertWGS84ToXY(plane.pos); - g->node = GetNode(g->current_pos); // TODO - might need to sort out node/arc here - AssignGate(g); - g->cleared = false; - ground_traffic.push_back(g); - NextClearance(g); -} - -void FGGround::NewContact(plane_rec plane) { - // This is a bit of a convienience function at the moment and is likely to change. - if(at a gate or apron) - NewDeparture(plane); - else - NewArrival(plane); -} - -void FGGround::NextClearance(ground_rec &g) { - // Need to work out where we can clear g to. - // Assume the pilot doesn't need progressive instructions - // We *should* already have a gate or holding point assigned by the time we get here - // but it wouldn't do any harm to check. - - // For now though we will hardwire it to clear to the final destination. -} - -void FGGround::AssignGate(ground_rec &g) { - // We'll cheat for now - since we only have the user's aircraft and a couple of airports implemented - // we'll hardwire the gate! - // In the long run the logic of which gate or area to send the plane to could be somewhat non-trivial. -} -#endif //0 - diff --git a/src/ATC/ground.hxx b/src/ATC/ground.hxx deleted file mode 100644 index 43f5c0a31..000000000 --- a/src/ATC/ground.hxx +++ /dev/null @@ -1,367 +0,0 @@ -// FGGround - a class to provide ground control at larger airports. -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifndef _FG_GROUND_HXX -#define _FG_GROUND_HXX - -#include -#include -#include - -#include -#include -#include -#include - -#include "ATC.hxx" -#include "ATCProjection.hxx" - -#include STL_IOSTREAM -#include STL_STRING - -SG_USING_STD(string); -SG_USING_STD(ios); - -SG_USING_STD(map); -SG_USING_STD(vector); -SG_USING_STD(list); - -class FGAIEntity; -class FGATCMgr; - -////////////////////////////////////////////////////// -// Types for the logical network data structure -enum arc_type { - RUNWAY, - TAXIWAY -}; - -enum node_type { - GATE, - APRON, - HOLD, - JUNCTION, - TJUNCTION -}; - -enum GateType { - TRANSPORT_PASSENGER, - TRANSPORT_PASSENGER_NARROWBODY, - TRANSPORT_PASSENGER_WIDEBODY, - TRANSPORT_CARGO, - GA_LOCAL, - GA_LOCAL_SINGLE, - GA_LOCAL_TWIN, - GA_TRANSIENT, - GA_TRANSIENT_SINGLE, - GA_TRANSIENT_TWIN, - OTHER // ie. anything goes!! -}; - -enum network_element_type { - NODE, - ARC -}; - -struct ground_network_element { - network_element_type struct_type; -}; - -struct arc : public ground_network_element { - int distance; - string name; - arc_type type; - bool directed; //false if 2-way, true if 1-way. - //This is a can of worms since arcs might be one way in different directions under different circumstances - unsigned int n1; // The nodeID of the first node - unsigned int n2; // The nodeID of the second node - // If the arc is directed then flow is normally from n1 to n2. See the above can of worms comment though. -}; - -typedef vector arc_array_type; // This was and may become again a list instead of vector -typedef arc_array_type::iterator arc_array_iterator; -typedef arc_array_type::const_iterator arc_array_const_iterator; - -struct node : public ground_network_element { - node(); - ~node(); - - unsigned int nodeID; //each node in an airport needs a unique ID number - this is ZERO-BASED to match array position - Point3D pos; - Point3D orthoPos; - string name; - node_type type; - arc_array_type arcs; - double max_turn_radius; -}; - -typedef vector node_array_type; -typedef node_array_type::iterator node_array_iterator; -typedef node_array_type::const_iterator node_array_const_iterator; - -struct Gate : public node { - GateType gateType; - int max_weight; //units?? - //airline_code airline; //For the future - we don't have any airline codes ATM - int id; // The gate number in the logical scheme of things - string name; // The real-world gate letter/number - //node* pNode; - bool used; - double heading; // The direction the parked-up plane should point in degrees -}; - -typedef vector < Gate* > gate_vec_type; -typedef gate_vec_type::iterator gate_vec_iterator; -typedef gate_vec_type::const_iterator gate_vec_const_iterator; - -// A map of gate vs. the logical (internal FGFS) gate ID -typedef map < int, Gate* > gate_map_type; -typedef gate_map_type::iterator gate_map_iterator; -typedef gate_map_type::const_iterator gate_map_const_iterator; - -// Runways - all the runway stuff is likely to change in the future -struct Rwy { - int id; //note this is a very simplified scheme for now - R & L are not differentiated - //It should work for simple one rwy airports - node_array_type exits; //Array of available exits from runway - // should probably add an FGRunway structure here as well eventually - // Eventually we will also want some encoding of real-life preferred runways - // This will get us up and running for single runway airports though. -}; - -typedef vector < Rwy > runway_array_type; -typedef runway_array_type::iterator runway_array_iterator; -typedef runway_array_type::const_iterator runway_array_const_iterator; - -// end logical network types -/////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////// -// Structures to use the network - -// A path through the network -typedef vector < ground_network_element* > ground_network_path_type; -typedef ground_network_path_type::iterator ground_network_path_iterator; -typedef ground_network_path_type::const_iterator ground_network_path_const_iterator; - -////////////////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////// -// -// Stuff for the shortest-path algorithms -struct a_path { - a_path(); - - ground_network_path_type path; - int cost; -}; - -// Paths mapped by nodeID reached so-far -typedef map < unsigned int, a_path* > shortest_path_map_type; -typedef shortest_path_map_type::iterator shortest_path_map_iterator; - -// Nodes mapped by their ID -//typedef map < unsigned int, node* > node_map_type; -//typedef node_map_type::iterator node_map_iterator; -//////////////////////////////////////////////// - -// Planes active within the ground network. - -// A more specialist plane rec to include ground information -struct GroundRec { - FGAIEntity* planePtr; // This might move to the planeRec eventually - - PlaneRec plane; - Point3D current_pos; - node* destination; - node* last_clearance; - bool taxiRequestOutstanding; // Plane has requested taxi and we haven't responded yet - double clearanceCounter; // Hack for communication timing - counter since clearance requested in seconds - - bool cleared; // set true when the plane has been cleared to somewhere - bool incoming; //true for arrivals, false for departures - // status? - // Almost certainly need to add more here -}; - -typedef list < GroundRec* > ground_rec_list; -typedef ground_rec_list::iterator ground_rec_list_itr; -typedef ground_rec_list::const_iterator ground_rec_list_const_itr; - -////////////////////////////////////////////////////////////////////////////////////////// - -// Hack -// perhaps we could use an FGRunway instead of this -struct GRunwayDetails { - Point3D threshold_pos; - Point3D end1ortho; // ortho projection end1 (the threshold ATM) - Point3D end2ortho; // ortho projection end2 (the take off end in the current hardwired scheme) - double hdg; // true runway heading - double length; // In *METERS* - string rwyID; -}; - -/////////////////////////////////////////////////////////////////////////////// -// -// FGGround -// -/////////////////////////////////////////////////////////////////////////////// -class FGGround : public FGATC { - -public: - FGGround(); - FGGround(const string& id); - ~FGGround(); - void Init(); - - void Update(double dt); - - inline const string& get_trans_ident() { return trans_ident; } - - // Contact ground control on arrival, assumed to request any gate - //void NewArrival(plane_rec plane); - - // Contact ground control on departure, assumed to request currently active runway. - void RequestDeparture(const PlaneRec& plane, FGAIEntity* requestee); - - // Contact ground control when the calling routine doesn't know if arrival - // or departure is appropriate. - //void NewContact(plane_rec plane); - - // Make a request of ground control. - //void Request(ground_request request); - - // Randomly fill some of the available gates and GA parking spots with planes - void PopulateGates(); - - // Return a suitable gate (maybe this should be a list of suitable gates so the plane or controller can choose the closest one) - void ReturnGate(Gate &gate, GateType type); - - // Return a pointer to an unused gate - Gate* GetGateNode(); - - // Return a pointer to a hold short node - node* GetHoldShortNode(const string& rwyID); - - // Runway stuff - this might change in the future. - // Get a list of exits from a given runway - // It is up to the calling function to check for non-zero size of returned array before use - node_array_type GetExits(const string& rwyID); - - // Get a path from one node to another - ground_network_path_type GetPath(node* A, node* B); - - // Get a path from a node to a runway threshold - ground_network_path_type GetPath(node* A, const string& rwyID); - - // Get a path from a node to a runway hold short point - // Bit of a hack this at the moment! - ground_network_path_type GetPathToHoldShort(node* A, const string& rwyID); - -private: - FGATCMgr* ATCmgr; - // This is purely for synactic convienience to avoid writing globals->get_ATC_mgr()-> all through the code! - - // Need a data structure to hold details of the various active planes - // Need a data structure to hold details of the logical network - // including which gates are filled - or possibly another data structure - // with the positions of the inactive planes. - // Need a data structure to hold outstanding communications from aircraft. - // Possibly need a data structure to hold outstanding communications to aircraft. - - // The logical network - // NODES WILL BE STORED IN THE NETWORK IN ORDER OF nodeID NUMBER - // ie. NODE 5 WILL BE AT network[5] - node_array_type network; - - // A map of all the gates indexed against internal (FGFS) ID - gate_map_type gates; - gate_map_iterator gatesItr; - - FGATCAlignedProjection ortho; - - // Planes currently active - //ground_rec_list ground_traffic; - - // Find the shortest route through the logical network between two points. - //FindShortestRoute(point a, point b); - - // Assign a gate or parking location to a new arrival - //AssignGate(ground_rec &g); - - // Generate the next clearance for an airplane - //NextClearance(ground_rec &g); - - // environment - need to make sure we're getting the surface winds and not winds aloft. - SGPropertyNode_ptr wind_from_hdg; //degrees - SGPropertyNode_ptr wind_speed_knots; //knots - - // for failure modeling - string trans_ident; // transmitted ident - bool ground_failed; // ground failed? - bool networkLoadOK; // Indicates whether LoadNetwork returned true or false at last attempt - - // Tower control - bool untowered; // True if this is an untowered airport (we still need the ground class for shortest path implementation etc - //FGATC* tower; // Pointer to the tower control - - // Logical runway details - this might change in the future. - //runway_array_type runways; // STL way - Rwy runways[37]; // quick hack! - - // Physical runway details - double aptElev; // Airport elevation - string activeRwy; // Active runway number - For now we'll disregard multiple / alternate runway operation. - RunwayDetails rwy; // Assumed to be the active one for now.// Figure out which runways are active. - - // For now we'll just be simple and do one active runway - eventually this will get much more complex - // Copied from FGTower - TODO - it would be better to implement this just once, and have ground call tower - // for runway operation details, but at the moment we can't guarantee that tower control at a given airport - // will be initialised before ground so we can't do that. - void DoRwyDetails(); - - // Load the logical ground network for this airport from file. - // Return true if successfull. - bool LoadNetwork(); - - // Parse a runway exit string and push the supplied node pointer onto the runway exit list - void ParseRwyExits(node* np, char* es); - - // Return a random gate ID of an unused gate. - // Two error values may be returned and must be checked for by the calling function: - // -2 signifies that no gates exist at this airport. - // -1 signifies that all gates are currently full. - // TODO - modify to return a suitable gate based on aircraft size/weight. - int GetRandomGateID(); - - // Return a pointer to the node at a runway threshold - // Returns NULL if unsuccessful. - node* GetThresholdNode(const string& rwyID); - - // A shortest path algorithm from memory (I can't find the bl&*dy book again!) - ground_network_path_type GetShortestPath(node* A, node* B); - - // Planes - ground_rec_list ground_traffic; - ground_rec_list_itr ground_traffic_itr; -}; - -#endif // _FG_GROUND_HXX - diff --git a/src/ATC/tower.cxx b/src/ATC/tower.cxx deleted file mode 100644 index fccaaaba1..000000000 --- a/src/ATC/tower.cxx +++ /dev/null @@ -1,2671 +0,0 @@ -// FGTower - a class to provide tower control at towered airports. -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// Copyright (C) 2008 Daniyar Atadjanov (ground clearance, gear check, weather, etc.) -// -// 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. - -#ifdef HAVE_CONFIG_H -# include -#endif - -#ifdef HAVE_STRINGS_H -# include // bcopy() -#else -# include // MSVC doesn't have strings.h -#endif - -#include -#include - -#include -#include -#include -#include - -#include
-#include - -#include "tower.hxx" -#include "ATCmgr.hxx" -#include "ATCutils.hxx" -#include "ATCDialog.hxx" -#include "commlist.hxx" -#include "AILocalTraffic.hxx" - - -SG_USING_STD(cout); - -// TowerPlaneRec - -TowerPlaneRec::TowerPlaneRec() : - planePtr(NULL), - clearedToLand(false), - clearedToLineUp(false), - clearedToTakeOff(false), - holdShortReported(false), - lineUpReported(false), - downwindReported(false), - longFinalReported(false), - longFinalAcknowledged(false), - finalReported(false), - finalAcknowledged(false), - rwyVacatedReported(false), - rwyVacatedAcknowledged(false), - goAroundReported(false), - instructedToGoAround(false), - onRwy(false), - nextOnRwy(false), - vfrArrivalReported(false), - vfrArrivalAcknowledged(false), - opType(TTT_UNKNOWN), - leg(LEG_UNKNOWN), - landingType(AIP_LT_UNKNOWN), - gearWasUp(false), - gearUpReported(false), - isUser(false) -{ - plane.callsign = "UNKNOWN"; -} - -TowerPlaneRec::TowerPlaneRec(const PlaneRec& p) : - planePtr(NULL), - clearedToLand(false), - clearedToLineUp(false), - clearedToTakeOff(false), - holdShortReported(false), - lineUpReported(false), - downwindReported(false), - longFinalReported(false), - longFinalAcknowledged(false), - finalReported(false), - finalAcknowledged(false), - rwyVacatedReported(false), - rwyVacatedAcknowledged(false), - goAroundReported(false), - instructedToGoAround(false), - onRwy(false), - nextOnRwy(false), - vfrArrivalReported(false), - vfrArrivalAcknowledged(false), - opType(TTT_UNKNOWN), - leg(LEG_UNKNOWN), - landingType(AIP_LT_UNKNOWN), - gearWasUp(false), - gearUpReported(false), - isUser(false) -{ - plane = p; -} - -TowerPlaneRec::TowerPlaneRec(const Point3D& pt) : - planePtr(NULL), - clearedToLand(false), - clearedToLineUp(false), - clearedToTakeOff(false), - holdShortReported(false), - lineUpReported(false), - downwindReported(false), - longFinalReported(false), - longFinalAcknowledged(false), - finalReported(false), - finalAcknowledged(false), - rwyVacatedReported(false), - rwyVacatedAcknowledged(false), - goAroundReported(false), - instructedToGoAround(false), - onRwy(false), - nextOnRwy(false), - vfrArrivalReported(false), - vfrArrivalAcknowledged(false), - opType(TTT_UNKNOWN), - leg(LEG_UNKNOWN), - landingType(AIP_LT_UNKNOWN), - gearWasUp(false), - gearUpReported(false), - isUser(false) -{ - plane.callsign = "UNKNOWN"; - pos = pt; -} - -TowerPlaneRec::TowerPlaneRec(const PlaneRec& p, const Point3D& pt) : - planePtr(NULL), - clearedToLand(false), - clearedToLineUp(false), - clearedToTakeOff(false), - holdShortReported(false), - lineUpReported(false), - downwindReported(false), - longFinalReported(false), - longFinalAcknowledged(false), - finalReported(false), - finalAcknowledged(false), - rwyVacatedReported(false), - rwyVacatedAcknowledged(false), - goAroundReported(false), - instructedToGoAround(false), - onRwy(false), - nextOnRwy(false), - vfrArrivalReported(false), - vfrArrivalAcknowledged(false), - opType(TTT_UNKNOWN), - leg(LEG_UNKNOWN), - landingType(AIP_LT_UNKNOWN), - gearWasUp(false), - gearUpReported(false), - isUser(false) -{ - plane = p; - pos = pt; -} - - -// FGTower - -/******************************************* - TODO List - -Currently user is assumed to have taken off again when leaving the runway - check speed/elev for taxiing-in. (MAJOR) - -Use track instead of heading to determine what leg of the circuit the user is flying. (MINOR) - -Use altitude as well as position to try to determine if the user has left the circuit. (MEDIUM - other issues as well). - -Currently HoldShortReported code assumes there will be only one plane holding for the runway at once and -will break when planes start queueing. (CRITICAL) - -Report-Runway-Vacated is left as only user ATC option following a go-around. (MAJOR) - -Report-Downwind is not added as ATC option when user takes off to fly a circuit. (MAJOR) - -eta of USER can be calculated very wrongly in circuit if flying straight out and turn4 etc are with +ve ortho y. -This can then screw up circuit ordering for other planes (MEDIUM) - -USER leaving circuit needs to be more robustly considered when intentions unknown -Currently only considered during climbout and breaks when user turns (MEDIUM). - -GetPos() of the AI planes is called erratically - either too much or not enough. (MINOR) - -GO-AROUND is instructed very late at < 12s to landing - possibly make more dependent on chance of rwy clearing before landing (FEATURE) - -Need to make clear when TowerPlaneRecs do or don't get deleted in RemoveFromCircuitList etc. (MINOR until I misuse it - then CRITICAL!) - -FGTower::RemoveAllUserDialogOptions() really ought to be replaced by an ATCDialog::clear_entries() function. (MINOR - efficiency). - -At the moment planes in the lists are not guaranteed to always have a sensible ETA - it should be set as part of AddList functions, and lists should only be accessed this way. (FAIRLY MAJOR). -*******************************************/ - -FGTower::FGTower() : - separateGround(true), - ground(0) -{ - ATCmgr = globals->get_ATC_mgr(); - - _type = TOWER; - - // Init the property nodes - TODO - need to make sure we're getting surface winds. - wind_from_hdg = fgGetNode("/environment/wind-from-heading-deg", true); - wind_speed_knots = fgGetNode("/environment/wind-speed-kt", true); - - update_count = 0; - update_count_max = 15; - - holdListItr = holdList.begin(); - appListItr = appList.begin(); - depListItr = depList.begin(); - rwyListItr = rwyList.begin(); - circuitListItr = circuitList.begin(); - trafficListItr = trafficList.begin(); - vacatedListItr = vacatedList.begin(); - - freqClear = true; - - timeSinceLastDeparture = 9999; - departed = false; - - nominal_downwind_leg_pos = 1000.0; - nominal_base_leg_pos = -1000.0; - // TODO - set nominal crosswind leg pos based on minimum distance from takeoff end of rwy. - - _departureControlled = false; -} - -FGTower::~FGTower() { - if(!separateGround) { - delete ground; - } -} - -void FGTower::Init() { - //cout << "Initialising tower " << ident << '\n'; - - // Pointers to user's position - user_lon_node = fgGetNode("/position/longitude-deg", true); - user_lat_node = fgGetNode("/position/latitude-deg", true); - user_elev_node = fgGetNode("/position/altitude-ft", true); - user_hdg_node = fgGetNode("/orientation/heading-deg", true); - - // Need some way to initialise rwyOccupied flag correctly if the user is on the runway and to know its the user. - // I'll punt the startup issue for now though!!! - rwyOccupied = false; - - // Setup the ground control at this airport - AirportATC a; - //cout << "Tower ident = " << ident << '\n'; - if(ATCmgr->GetAirportATCDetails(ident, &a)) { - if(a.ground_freq) { // Ground control - ground = (FGGround*)ATCmgr->GetATCPointer(ident, GROUND); - separateGround = true; - if(ground == NULL) { - // Something has gone wrong :-( - SG_LOG(SG_ATC, SG_WARN, "ERROR - ground has frequency but can't get ground pointer :-("); - ground = new FGGround(ident); - separateGround = false; - ground->Init(); - if(_display) { - ground->SetDisplay(); - } else { - ground->SetNoDisplay(); - } - } - } else { - // Initialise ground anyway to do the shortest path stuff! - // Note that we're now responsible for updating and deleting this - NOT the ATCMgr. - ground = new FGGround(ident); - separateGround = false; - ground->Init(); - if(_display) { - ground->SetDisplay(); - } else { - ground->SetNoDisplay(); - } - } - } else { - SG_LOG(SG_ATC, SG_ALERT, "Unable to find airport details for " << ident << " in FGTower::Init()"); - // Initialise ground anyway to avoid segfault later - ground = new FGGround(ident); - separateGround = false; - ground->Init(); - if(_display) { - ground->SetDisplay(); - } else { - ground->SetNoDisplay(); - } - } - - RemoveAllUserDialogOptions(); - - // TODO - attempt to get a departure control pointer to see if we need to hand off departing traffic to departure. - - // Get the airport elevation - aptElev = fgGetAirportElev(ident.c_str()); - - // TODO - this function only assumes one active rwy. - DoRwyDetails(); - - // TODO - this currently assumes only one active runway. - rwyOccupied = OnActiveRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0)); - - if(!OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), false)) { - //cout << ident << " ADD 0\n"; - current_atcdialog->add_entry(ident, "@AP Tower, @CS @MI miles @CD of the airport for full stop@AT", - "Contact tower for VFR arrival (full stop)", TOWER, - (int)USER_REQUEST_VFR_ARRIVAL_FULL_STOP); - } else { - //cout << "User found on active runway\n"; - // Assume the user is started at the threshold ready to take-off - TowerPlaneRec* t = new TowerPlaneRec; - t->plane.callsign = fgGetString("/sim/user/callsign"); - t->plane.type = GA_SINGLE; // FIXME - hardwired!! - t->opType = TTT_UNKNOWN; // We don't know if the user wants to do circuits or a departure... - t->landingType = AIP_LT_UNKNOWN; - t->leg = TAKEOFF_ROLL; - t->isUser = true; - t->planePtr = NULL; - t->clearedToTakeOff = false; - rwyList.push_back(t); - rwyListItr = rwyList.begin(); - departed = false; - current_atcdialog->add_entry(ident, "@CS @TO", "Request departure / take-off clearance", - TOWER, (int)USER_REQUEST_TAKE_OFF); - } -} - -void FGTower::Update(double dt) { - //cout << "T" << endl; - // Each time step, what do we need to do? - // We need to go through the list of outstanding requests and acknowedgements - // and process at least one of them. - // We need to go through the list of planes under our control and check if - // any need to be addressed. - // We need to check for planes not under our control coming within our - // control area and address if necessary. - - // TODO - a lot of the below probably doesn't need to be called every frame and should be staggered. - - // Sort the arriving planes - - /* - if(ident == "KEMT") { - cout << update_count << "\ttL: " << trafficList.size() << " cL: " << circuitList.size() << " hL: " << holdList.size() << " aL: " << appList.size() << '\n'; - } - */ - //if(ident == "EGNX") cout << display << '\n'; - - if(departed != false) { - timeSinceLastDeparture += dt; - //if(ident == "KEMT") - // cout << " dt = " << dt << " timeSinceLastDeparture = " << timeSinceLastDeparture << '\n'; - } - - //cout << ident << " respond = " << respond << " responseReqd = " << responseReqd << '\n'; - if(respond) { - if(!responseReqd) SG_LOG(SG_ATC, SG_ALERT, "ERROR - respond is true and responseReqd is false in FGTower::Update(...)"); - Respond(); - respond = false; - responseReqd = false; - } - - // Calculate the eta of each plane to the threshold. - // For ground traffic this is the fastest they can get there. - // For air traffic this is the middle approximation. - if(update_count == 1) { - doThresholdETACalc(); - } - - // Order the list of traffic as per expected threshold use and flag any conflicts - if(update_count == 2) { - //bool conflicts = doThresholdUseOrder(); - doThresholdUseOrder(); - } - - // sortConficts() !!! - - if(update_count == 4) { - CheckHoldList(dt); - } - - // Uggh - HACK - why have we got rwyOccupied - wouldn't simply testing rwyList.size() do? - if(rwyList.size()) { - rwyOccupied = true; - } else { - rwyOccupied = false; - } - - if(update_count == 5 && rwyOccupied) { - CheckRunwayList(dt); - } - - if(update_count == 6) { - CheckCircuitList(dt); - } - - if(update_count == 7) { - CheckApproachList(dt); - } - - if(update_count == 8) { - CheckDepartureList(dt); - } - - // TODO - do one plane from the departure list and set departed = false when out of consideration - - //doCommunication(); - - if(!separateGround) { - // The display stuff might have to get more clever than this when not separate - // since the tower and ground might try communicating simultaneously even though - // they're mean't to be the same contoller/frequency!! - // We could also get rid of this by overloading FGATC's Set(No)Display() functions. - if(_display) { - ground->SetDisplay(); - } else { - ground->SetNoDisplay(); - } - ground->Update(dt); - } - - ++update_count; - // How big should ii get - ie how long should the update cycle interval stretch? - if(update_count >= update_count_max) { - update_count = 0; - } - - // Call the base class update for the response time handling. - FGATC::Update(dt); - - /* - if(ident == "KEMT") { - // For AI debugging convienience - may be removed - Point3D user_pos; - user_pos.setlon(user_lon_node->getDoubleValue()); - user_pos.setlat(user_lat_node->getDoubleValue()); - user_pos.setelev(user_elev_node->getDoubleValue()); - Point3D user_ortho_pos = ortho.ConvertToLocal(user_pos); - fgSetDouble("/AI/user/ortho-x", user_ortho_pos.x()); - fgSetDouble("/AI/user/ortho-y", user_ortho_pos.y()); - fgSetDouble("/AI/user/elev", user_elev_node->getDoubleValue()); - } - */ - - //cout << "Done T" << endl; -} - -void FGTower::ReceiveUserCallback(int code) { - if(code == (int)USER_REQUEST_VFR_DEPARTURE) { - RequestDepartureClearance("USER"); - } else if(code == (int)USER_REQUEST_VFR_ARRIVAL) { - VFRArrivalContact("USER"); - } else if(code == (int)USER_REQUEST_VFR_ARRIVAL_FULL_STOP) { - VFRArrivalContact("USER", FULL_STOP); - } else if(code == (int)USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO) { - VFRArrivalContact("USER", TOUCH_AND_GO); - } else if(code == (int)USER_REPORT_DOWNWIND) { - ReportDownwind("USER"); - } else if(code == (int)USER_REPORT_3_MILE_FINAL) { - // For now we'll just call report final instead of report long final to avoid having to alter the response code - ReportFinal("USER"); - } else if(code == (int)USER_REPORT_RWY_VACATED) { - ReportRunwayVacated("USER"); - } else if(code == (int)USER_REPORT_GOING_AROUND) { - ReportGoingAround("USER"); - } else if(code == (int)USER_REQUEST_TAKE_OFF) { - RequestTakeOffClearance("USER"); - } -} - -// **************** RESPONSE FUNCTIONS **************** - -void FGTower::Respond() { - //cout << "\nEntering Respond, responseID = " << responseID << endl; - TowerPlaneRec* t = FindPlane(responseID); - if(t) { - // This will grow!!! - if(t->vfrArrivalReported && !t->vfrArrivalAcknowledged) { - //cout << "Tower " << ident << " is responding to VFR arrival reported...\n"; - // Testing - hardwire straight in for now - string trns = t->plane.callsign; - trns += " "; - trns += name; - trns += " Tower,"; - // Should we clear staight in or for downwind entry? - // For now we'll clear straight in if greater than 1km from a line drawn through the threshold perpendicular to the rwy. - // Later on we might check the actual heading and direct some of those to enter on downwind or base. - Point3D op = ortho.ConvertToLocal(t->pos); - float gp = fgGetFloat("/gear/gear/position-norm"); - if(gp < 1) - t->gearWasUp = true; // This will be needed on final to tell "Gear down, ready to land." - if(op.y() < -1000) { - trns += " Report three mile straight-in runway "; - t->opType = STRAIGHT_IN; - if(t->isUser) { - current_atcdialog->add_entry(ident, "@CS @MI mile final runway @RW@GR", "Report Final", TOWER, (int)USER_REPORT_3_MILE_FINAL); - } else { - t->planePtr->RegisterTransmission(14); - } - } else { - // For now we'll just request reporting downwind. - // TODO - In real life something like 'report 2 miles southwest right downwind rwy 19R' might be used - // but I'm not sure how to handle all permutations of which direction to tell to report from yet. - trns += " Report "; - //cout << "Responding, rwy.patterDirection is " << rwy.patternDirection << '\n'; - trns += ((rwy.patternDirection == 1) ? "right " : "left "); - trns += "downwind runway "; - t->opType = CIRCUIT; - // leave it in the app list until it gets into pattern though. - if(t->isUser) { - current_atcdialog->add_entry(ident, "@AP Tower, @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND); - } else { - t->planePtr->RegisterTransmission(15); - } - } - trns += ConvertRwyNumToSpokenString(activeRwy); - if(_display) { - pending_transmission = trns; - Transmit(); - } else { - //cout << "Not displaying, trns was " << trns << '\n'; - } - t->vfrArrivalAcknowledged = true; - } else if(t->downwindReported) { - //cout << "Tower " << ident << " is responding to downwind reported...\n"; - ProcessDownwindReport(t); - t->downwindReported = false; - } else if(t->lineUpReported) { - string trns = t->plane.callsign; - if(rwyOccupied) { - double f = globals->get_ATC_mgr()->GetFrequency(ident, ATIS) / 100.0; - string wtr; - if(!f) { - wtr = ", " + GetWeather(); - } - trns += " Cleared for take-off" + wtr; - t->clearedToTakeOff = true; - } else { - if(!OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), true)) { - // TODO: Check if any AI Planes on final and tell something like: "After the landing CALLSIGN line up runway two eight right" - trns += " Line up runway " + ConvertRwyNumToSpokenString(activeRwy); - t->clearedToTakeOff = false; - current_atcdialog->add_entry(ident, "@CS @TO", "Report ready for take-off", TOWER, (int)USER_REQUEST_TAKE_OFF); - - } else { - sg_srandom_time(); - if((int(sg_random() * 10) + 1) != 3) { - t->clearedToTakeOff = true; - trns += " Cleared immediate take-off "; - } else { - t->clearedToTakeOff = false; - trns += " Negative, departure runway " + ConvertRwyNumToSpokenString(activeRwy); - } - } - } - if(_display) { - pending_transmission = trns; - Transmit(); - } else { - //cout << "Not displaying, trns was " << trns << '\n'; - } - t->lineUpReported = false; - } else if(t->holdShortReported) { - //cout << "Tower " << ident << " is reponding to holdShortReported...\n"; - if(t->nextOnRwy) { - if(rwyOccupied) { // TODO - ought to add a sanity check that it isn't this plane only on the runway (even though it shouldn't be!!) - // Do nothing for now - consider acknowloging hold short eventually - } else { - ClearHoldingPlane(t); - t->leg = TAKEOFF_ROLL; - rwyList.push_back(t); - rwyListItr = rwyList.begin(); - rwyOccupied = true; - // WARNING - WE ARE ASSUMING ONLY ONE PLANE REPORTING HOLD AT A TIME BELOW - // FIXME TODO - FIX THIS!!! - if(!holdList.empty()) { - if(holdListItr == holdList.end()) { - holdListItr = holdList.begin(); - } - holdList.erase(holdListItr); - holdListItr = holdList.begin(); - } - } - } else { - // Tell him to hold and what position he is. - // Not currently sure under which circumstances we do or don't bother transmitting this. - string trns = t->plane.callsign; - trns += " hold position"; - if(_display) { - pending_transmission = trns; - Transmit(); - } - // TODO - add some idea of what traffic is blocking him. - } - t->holdShortReported = false; - } else if(t->finalReported && !(t->finalAcknowledged)) { - //cout << "Tower " << ident << " is responding to finalReported...\n"; - bool disp = true; - string trns = t->plane.callsign; - //cout << (t->nextOnRwy ? "Next on rwy " : "Not next!! "); - //cout << (rwyOccupied ? "RWY OCCUPIED!!\n" : "Rwy not occupied\n"); - if(t->nextOnRwy && !rwyOccupied && !(t->instructedToGoAround)) { - if(t->landingType == FULL_STOP) { - trns += " cleared to land "; - } else { - double f = globals->get_ATC_mgr()->GetFrequency(ident, ATIS) / 100.0; - string wtr; - if(!f) { - wtr = ", " + GetWeather(); - } else { - wtr = ", runway " + ConvertRwyNumToSpokenString(activeRwy); - } - trns += " cleared to land" + wtr; - } - // TODO - add winds - t->clearedToLand = true; - // Maybe remove report downwind from menu here as well incase user didn't bother to? - if(t->isUser) { - //cout << "ADD VACATED B\n"; - // Put going around at the top (and hence default) since that'll be more desperate, - // or put rwy vacated at the top since that'll be more common? - current_atcdialog->add_entry(ident, "@CS Going Around", "Report going around", TOWER, USER_REPORT_GOING_AROUND); - current_atcdialog->add_entry(ident, "@CS Clear of the runway", "Report runway vacated", TOWER, USER_REPORT_RWY_VACATED); - } else { - t->planePtr->RegisterTransmission(7); - } - } else if(t->eta < 20) { - // Do nothing - we'll be telling it to go around in less than 10 seconds if the - // runway doesn't clear so no point in calling "continue approach". - disp = false; - } else { - trns += " continue approach"; - trns += " and report "; - trns += ((rwy.patternDirection == 1) ? "right " : "left "); - trns += "downwind runway " + ConvertRwyNumToSpokenString(activeRwy); - t->opType = CIRCUIT; - if(t->isUser) { - current_atcdialog->add_entry(ident, "@AP Tower, @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND); - } else { - t->planePtr->RegisterTransmission(15); - } - t->clearedToLand = false; - } - if(_display && disp) { - pending_transmission = trns; - Transmit(); - } - t->finalAcknowledged = true; - } else if(t->rwyVacatedReported && !(t->rwyVacatedAcknowledged)) { - ProcessRunwayVacatedReport(t); - t->rwyVacatedAcknowledged = true; - } - } - //freqClear = true; // FIXME - set this to come true after enough time to render the message - _releaseCounter = 0.0; - _releaseTime = 5.5; - _runReleaseCounter = true; - //cout << "Done Respond\n" << endl; -} - -void FGTower::ProcessDownwindReport(TowerPlaneRec* t) { - int i = 1; - int a = 0; // Count of preceding planes on approach - bool cf = false; // conflicting traffic on final - bool cc = false; // preceding traffic in circuit - TowerPlaneRec* tc = NULL; - for(tower_plane_rec_list_iterator twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) { - if((*twrItr)->plane.callsign == responseID) break; - tc = *twrItr; - ++i; - } - if(i > 1) { cc = true; } - doThresholdETACalc(); - TowerPlaneRec* tf = NULL; - for(tower_plane_rec_list_iterator twrItr = appList.begin(); twrItr != appList.end(); twrItr++) { - if((*twrItr)->eta < (t->eta + 45) && strcmp((*twrItr)->plane.callsign.c_str(), t->plane.callsign.c_str()) != 0) { // don't let ATC ask you to follow yourself - a++; - tf = *twrItr; - cf = true; - // This should set the flagged plane to be the last conflicting one, and hence the one to follow. - // It ignores the fact that we might have problems slotting into the approach traffic behind it - - // eventually we'll need some fancy algorithms for that! - } - } - string trns = t->plane.callsign; - trns += " Number "; - trns += ConvertNumToSpokenDigits(i + a); - // This assumes that the number spoken is landing position, not circuit position, since some of the traffic might be on straight-in final. - trns += " "; - TowerPlaneRec* tt = NULL; - if((i == 1) && rwyList.empty() && (t->nextOnRwy) && (!cf)) { // Unfortunately nextOnRwy currently doesn't handle circuit/straight-in ordering properly at present, hence the cf check below. - trns += "Cleared to land"; // TODO - clear for the option if appropriate - t->clearedToLand = true; - if(!t->isUser) t->planePtr->RegisterTransmission(7); - } else if((i+a) > 1) { - //First set tt to point to the correct preceding plane - final or circuit - if(tc && tf) { - tt = (tf->eta < tc->eta ? tf : tc); - } else if(tc) { - tt = tc; - } else if(tf) { - tt = tf; - } else { - // We should never get here! - SG_LOG(SG_ATC, SG_ALERT, "ALERT - Logic error in FGTower::ProcessDownwindReport"); - return; - } - trns += "Follow the "; - string s = tt->plane.callsign; - int p = s.find('-'); - s = s.substr(0,p); - trns += s; - if((tt->opType) == CIRCUIT) { - PatternLeg leg; - if(tt->isUser) { - leg = tt->leg; - } else { - leg = tt->planePtr->GetLeg(); - } - if(leg == FINAL) { - trns += " on final"; - } else if(leg == TURN4) { - trns += " turning final"; - } else if(leg == BASE) { - trns += " on base"; - } else if(leg == TURN3) { - trns += " turning base"; - } - } else { - double miles_out = CalcDistOutMiles(tt); - if(miles_out < 2) { - trns += " on short final"; - } else { - trns += " on "; - trns += ConvertNumToSpokenDigits((int)miles_out); - trns += " mile final"; - } - } - } - if(_display) { - pending_transmission = trns; - Transmit(); - } - if(t->isUser) { - if(t->opType == TTT_UNKNOWN) t->opType = CIRCUIT; - //cout << "ADD VACATED A\n"; - // Put going around at the top (and hence default) since that'll be more desperate, - // or put rwy vacated at the top since that'll be more common? - //cout << "ident = " << ident << ", adding go-around option\n"; - current_atcdialog->add_entry(ident, "@CS Going Around", "Report going around", TOWER, USER_REPORT_GOING_AROUND); - current_atcdialog->add_entry(ident, "@CS Clear of the runway", "Report runway vacated", TOWER, USER_REPORT_RWY_VACATED); - } -} - -void FGTower::ProcessRunwayVacatedReport(TowerPlaneRec* t) { - //cout << "Processing rwy vacated...\n"; - if(t->isUser) current_atcdialog->remove_entry(ident, USER_REPORT_GOING_AROUND, TOWER); - string trns = t->plane.callsign; - if(separateGround) { - trns += " Contact ground on "; - double f = globals->get_ATC_mgr()->GetFrequency(ident, GROUND) / 100.0; - char buf[10]; - sprintf(buf, "%.2f", f); - trns += buf; - trns += " Good Day"; - if(!t->isUser) t->planePtr->RegisterTransmission(5); - } else { - // Cop-out!! - trns += " cleared for taxi to general aviation parking"; - if(!t->isUser) t->planePtr->RegisterTransmission(6); // TODO - this is a mega-hack!! - } - //cout << "trns = " << trns << '\n'; - if(_display) { - pending_transmission = trns; - Transmit(); - } - RemoveFromRwyList(t->plane.callsign); - AddToVacatedList(t); - // Maybe we should check that the plane really *has* vacated the runway! -} - -// *********** END RESPONSE FUNCTIONS ***************** - -// Currently this assumes we *are* next on the runway and doesn't check for planes about to land - -// this should be done prior to calling this function. -void FGTower::ClearHoldingPlane(TowerPlaneRec* t) { - //cout << "Entering ClearHoldingPlane..." << endl; - // Lets Roll !!!! - string trns = t->plane.callsign; - //if(departed plane < some threshold in time away) { - if(0) { // FIXME - //if(timeSinceLastDeparture <= 60.0 && departed == true) { - trns += " line up runway " + ConvertRwyNumToSpokenString(activeRwy); - t->clearedToLineUp = true; - t->planePtr->RegisterTransmission(3); // cleared to line-up - //} else if(arriving plane < some threshold away) { - } else if(GetTrafficETA(2) < 150.0 && (timeSinceLastDeparture > 60.0 || departed == false)) { // Hack - hardwired time - trns += " cleared immediate take-off"; - if(trafficList.size()) { - tower_plane_rec_list_iterator trfcItr = trafficList.begin(); - trfcItr++; // At the moment the holding plane should be first in trafficList. - // Note though that this will break if holding planes aren't put in trafficList in the future. - TowerPlaneRec* trfc = *trfcItr; - trns += "... traffic is"; - switch(trfc->plane.type) { - case UNKNOWN: - break; - case GA_SINGLE: - trns += " a Cessna"; // TODO - add ability to specify actual plane type somewhere - break; - case GA_HP_SINGLE: - trns += " a Piper"; - break; - case GA_TWIN: - trns += " a King-air"; - break; - case GA_JET: - trns += " a Learjet"; - break; - case MEDIUM: - trns += " a Regional"; - break; - case HEAVY: - trns += " a Heavy"; - break; - case MIL_JET: - trns += " Military"; - break; - } - //if(trfc->opType == STRAIGHT_IN || trfc->opType == TTT_UNKNOWN) { - if(trfc->opType == STRAIGHT_IN) { - double miles_out = CalcDistOutMiles(trfc); - if(miles_out < 2) { - trns += " on final"; - } else { - trns += " on "; - trns += ConvertNumToSpokenDigits((int)miles_out); - trns += " mile final"; - } - } else if(trfc->opType == CIRCUIT) { - //cout << "Getting leg of " << trfc->plane.callsign << '\n'; - switch(trfc->leg) { - case FINAL: - trns += " on final"; - break; - case TURN4: - trns += " turning final"; - break; - case BASE: - trns += " on base"; - break; - case TURN3: - trns += " turning base"; - break; - case DOWNWIND: - trns += " in circuit"; // At the moment the user plane is generally flagged as unknown opType when downwind incase its a downwind departure which means we won't get here. - break; - // And to eliminate compiler warnings... - case TAKEOFF_ROLL: break; - case CLIMBOUT: break; - case TURN1: break; - case CROSSWIND: break; - case TURN2: break; - case LANDING_ROLL: break; - case LEG_UNKNOWN: break; - } - } - } else { - // By definition there should be some arriving traffic if we're cleared for immediate takeoff - SG_LOG(SG_ATC, SG_WARN, "Warning: Departing traffic cleared for *immediate* take-off despite no arriving traffic in FGTower"); - } - t->clearedToTakeOff = true; - t->planePtr->RegisterTransmission(4); // cleared to take-off - TODO differentiate between immediate and normal take-off - departed = false; - timeSinceLastDeparture = 0.0; - } else { - //} else if(timeSinceLastDeparture > 60.0 || departed == false) { // Hack - test for timeSinceLastDeparture should be in lineup block eventually - trns += " cleared for take-off"; - // TODO - add traffic is... ? - t->clearedToTakeOff = true; - t->planePtr->RegisterTransmission(4); // cleared to take-off - departed = false; - timeSinceLastDeparture = 0.0; - } - if(_display) { - pending_transmission = trns; - Transmit(); - } - //cout << "Done ClearHoldingPlane " << endl; -} - - -// *************************************************************************************** -// ********** Functions to periodically check what the various traffic is doing ********** - -// Do one plane from the hold list -void FGTower::CheckHoldList(double dt) { - //cout << "Entering CheckHoldList..." << endl; - if(!holdList.empty()) { - //cout << "*holdListItr = " << *holdListItr << endl; - if(holdListItr == holdList.end()) { - holdListItr = holdList.begin(); - } - //cout << "*holdListItr = " << *holdListItr << endl; - //Process(*holdListItr); - TowerPlaneRec* t = *holdListItr; - //cout << "t = " << t << endl; - if(t->holdShortReported) { - // NO-OP - leave it to the response handler. - } else { // not responding to report, but still need to clear if clear - if(t->nextOnRwy) { - //cout << "departed = " << departed << '\n'; - //cout << "timeSinceLastDeparture = " << timeSinceLastDeparture << '\n'; - if(rwyOccupied) { - RemoveAllUserDialogOptions(); - current_atcdialog->add_entry(ident, "@CS Ready for take-off", "Request take-off clearance", TOWER, (int)USER_REQUEST_TAKE_OFF); - } else if(timeSinceLastDeparture <= 60.0 && departed == true) { - // Do nothing - this is a bit of a hack - should maybe do line up be ready here - } else { - ClearHoldingPlane(t); - t->leg = TAKEOFF_ROLL; - rwyList.push_back(t); - rwyListItr = rwyList.begin(); - rwyOccupied = true; - holdList.erase(holdListItr); - holdListItr = holdList.begin(); - if (holdList.empty()) - return; - } - } - // TODO - rationalise the considerable code duplication above! - } - ++holdListItr; - } - //cout << "Done CheckHoldList" << endl; -} - -// do the ciruit list -void FGTower::CheckCircuitList(double dt) { - //cout << "Entering CheckCircuitList..." << endl; - // Clear the constraints - we recalculate here. - base_leg_pos = 0.0; - downwind_leg_pos = 0.0; - crosswind_leg_pos = 0.0; - - if(!circuitList.empty()) { // Do one plane from the circuit - if(circuitListItr == circuitList.end()) { - circuitListItr = circuitList.begin(); - } - TowerPlaneRec* t = *circuitListItr; - //cout << ident << ' ' << circuitList.size() << ' ' << t->plane.callsign << " " << t->leg << " eta " << t->eta << '\n'; - if(t->isUser) { - t->pos.setlon(user_lon_node->getDoubleValue()); - t->pos.setlat(user_lat_node->getDoubleValue()); - t->pos.setelev(user_elev_node->getDoubleValue()); - //cout << ident << ' ' << circuitList.size() << ' ' << t->plane.callsign << " " << t->leg << " eta " << t->eta << '\n'; - } else { - t->pos = t->planePtr->GetPos(); // We should probably only set the pos's on one walk through the traffic list in the update function, to save a few CPU should we end up duplicating this. - t->landingType = t->planePtr->GetLandingOption(); - //cout << "AI plane landing option is " << t->landingType << '\n'; - } - Point3D tortho = ortho.ConvertToLocal(t->pos); - if(t->isUser) { - // Need to figure out which leg he's on - //cout << "rwy.hdg = " << rwy.hdg << " user hdg = " << user_hdg_node->getDoubleValue(); - double ho = GetAngleDiff_deg(user_hdg_node->getDoubleValue(), rwy.hdg); - //cout << " ho = " << ho << " abs(ho = " << abs(ho) << '\n'; - // TODO FIXME - get the wind and convert this to track, or otherwise use track somehow!!! - // If it's gusty might need to filter the value, although we are leaving 30 degrees each way leeway! - if(fabs(ho) < 30) { - // could be either takeoff, climbout or landing - check orthopos.y - //cout << "tortho.y = " << tortho.y() << '\n'; - if((tortho.y() < 0) || (t->leg == TURN4) || (t->leg == FINAL)) { - t->leg = FINAL; - //cout << "Final\n"; - } else { - t->leg = CLIMBOUT; // TODO - check elev wrt. apt elev to differentiate takeoff roll and climbout - //cout << "Climbout\n"; - // If it's the user we may be unsure of his/her intentions. - // (Hopefully the AI planes won't try confusing the sim!!!) - //cout << "tortho.y = " << tortho.y() << '\n'; - if(t->opType == TTT_UNKNOWN) { - if(tortho.y() > 5000) { - // 5 km out from threshold - assume it's a departure - t->opType = OUTBOUND; // TODO - could check if the user has climbed significantly above circuit altitude as well. - // Since we are unknown operation we should be in depList already. - //cout << ident << " Removing user from circuitList (TTT_UNKNOWN)\n"; - circuitListItr = circuitList.erase(circuitListItr); - RemoveFromTrafficList(t->plane.callsign); - if (circuitList.empty()) - return; - } - } else if(t->opType == CIRCUIT) { - if(tortho.y() > 10000) { - // 10 km out - assume the user has abandoned the circuit!! - t->opType = OUTBOUND; - depList.push_back(t); - depListItr = depList.begin(); - //cout << ident << " removing user from circuitList (CIRCUIT)\n"; - circuitListItr = circuitList.erase(circuitListItr); - if (circuitList.empty()) - return; - } - } - } - } else if(fabs(ho) < 60) { - // turn1 or turn 4 - // TODO - either fix or doublecheck this hack by looking at heading and pattern direction - if((t->leg == CLIMBOUT) || (t->leg == TURN1)) { - t->leg = TURN1; - //cout << "Turn1\n"; - } else { - t->leg = TURN4; - //cout << "Turn4\n"; - } - } else if(fabs(ho) < 120) { - // crosswind or base - // TODO - either fix or doublecheck this hack by looking at heading and pattern direction - if((t->leg == TURN1) || (t->leg == CROSSWIND)) { - t->leg = CROSSWIND; - //cout << "Crosswind\n"; - } else { - t->leg = BASE; - //cout << "Base\n"; - } - } else if(fabs(ho) < 150) { - // turn2 or turn 3 - // TODO - either fix or doublecheck this hack by looking at heading and pattern direction - if((t->leg == CROSSWIND) || (t->leg == TURN2)) { - t->leg = TURN2; - //cout << "Turn2\n"; - } else { - t->leg = TURN3; - // Probably safe now to assume the user is flying a circuit - t->opType = CIRCUIT; - //cout << "Turn3\n"; - } - } else { - // downwind - t->leg = DOWNWIND; - //cout << "Downwind\n"; - } - if(t->leg == FINAL) { - if(OnActiveRunway(t->pos)) { - t->leg = LANDING_ROLL; - } - } - } else { - t->leg = t->planePtr->GetLeg(); - } - - // Set the constraints IF this is the first plane in the circuit - // TODO - at the moment we're constraining plane 2 based on plane 1 - this won't (or might not) work for 3 planes in the circuit!! - if(circuitListItr == circuitList.begin()) { - switch(t->leg) { - case FINAL: - // Base leg must be at least as far out as the plane is - actually possibly not necessary for separation, but we'll use that for now. - base_leg_pos = tortho.y(); - //cout << "base_leg_pos = " << base_leg_pos << '\n'; - break; - case TURN4: - // Fall through to base - case BASE: - base_leg_pos = tortho.y(); - //cout << "base_leg_pos = " << base_leg_pos << '\n'; - break; - case TURN3: - // Fall through to downwind - case DOWNWIND: - // Only have the downwind leg pos as turn-to-base constraint if more negative than we already have. - base_leg_pos = (tortho.y() < base_leg_pos ? tortho.y() : base_leg_pos); - //cout << "base_leg_pos = " << base_leg_pos; - downwind_leg_pos = tortho.x(); // Assume that a following plane can simply be constrained by the immediately in front downwind plane - //cout << " downwind_leg_pos = " << downwind_leg_pos << '\n'; - break; - case TURN2: - // Fall through to crosswind - case CROSSWIND: - crosswind_leg_pos = tortho.y(); - //cout << "crosswind_leg_pos = " << crosswind_leg_pos << '\n'; - t->instructedToGoAround = false; - break; - case TURN1: - // Fall through to climbout - case CLIMBOUT: - // Only use current by constraint as largest - crosswind_leg_pos = (tortho.y() > crosswind_leg_pos ? tortho.y() : crosswind_leg_pos); - //cout << "crosswind_leg_pos = " << crosswind_leg_pos << '\n'; - break; - case TAKEOFF_ROLL: - break; - case LEG_UNKNOWN: - break; - case LANDING_ROLL: - break; - default: - break; - } - } - - if(t->leg == FINAL && !(t->instructedToGoAround)) { - doThresholdETACalc(); - doThresholdUseOrder(); - /* - if(t->isUser) { - cout << "Checking USER on final... "; - cout << "eta " << t->eta; - if(t->clearedToLand) cout << " cleared to land\n"; - } - */ - //cout << "YES FINAL, t->eta = " << t->eta << ", rwyList.size() = " << rwyList.size() << '\n'; - if(t->landingType == FULL_STOP) { - t->opType = INBOUND; - //cout << "\n******** SWITCHING TO INBOUND AT POINT AAA *********\n\n"; - } - if(t->eta < 12 && rwyList.size()) { - // TODO - need to make this more sophisticated - // eg. is the plane accelerating down the runway taking off [OK], - // or stationary near the start [V. BAD!!]. - // For now this should stop the AI plane landing on top of the user. - tower_plane_rec_list_iterator twrItr; - twrItr = rwyList.begin(); - TowerPlaneRec* tpr = *twrItr; - if(strcmp(tpr->plane.callsign.c_str(), t->plane.callsign.c_str()) == 0 - && rwyList.size() == 1) { - // Fixing bug when ATC says that we must go around because of traffic on rwy - // but that traffic is our plane! In future we can use this expression - // for other ATC-messages like "On ground at 46, vacate left." - - } else { - string trns = t->plane.callsign; - trns += " GO AROUND TRAFFIC ON RUNWAY I REPEAT GO AROUND"; - pending_transmission = trns; - ImmediateTransmit(); - t->instructedToGoAround = true; - t->clearedToLand = false; - // Assume it complies!!! - t->opType = CIRCUIT; - t->leg = CLIMBOUT; - if(t->planePtr) { - //cout << "Registering Go-around transmission with AI plane\n"; - t->planePtr->RegisterTransmission(13); - } - } - } else if(!t->clearedToLand) { - // The whip through the appList is a hack since currently t->nextOnRwy doesn't always work - // TODO - fix this! - bool cf = false; - for(tower_plane_rec_list_iterator twrItr = appList.begin(); twrItr != appList.end(); twrItr++) { - if((*twrItr)->eta < t->eta) { - cf = true; - } - } - if(t->nextOnRwy && !cf) { - if(!rwyList.size()) { - string trns = t->plane.callsign; - trns += " Cleared to land"; - pending_transmission = trns; - Transmit(); - //if(t->isUser) cout << "Transmitting cleared to Land!!!\n"; - t->clearedToLand = true; - if(!t->isUser) { - t->planePtr->RegisterTransmission(7); - } - } - } else { - //if(t->isUser) cout << "Not next\n"; - } - } - } else if(t->leg == LANDING_ROLL) { - //cout << t->plane.callsign << " has landed - adding to rwyList\n"; - rwyList.push_front(t); - // TODO - if(!clearedToLand) shout something!! - t->clearedToLand = false; - RemoveFromTrafficList(t->plane.callsign); - if(t->isUser) { - t->opType = TTT_UNKNOWN; - } // TODO - allow the user to specify opType via ATC menu - //cout << ident << " Removing " << t->plane.callsign << " from circuitList..." << endl; - circuitListItr = circuitList.erase(circuitListItr); - if(circuitListItr == circuitList.end() ) { - circuitListItr = circuitList.begin(); - // avoid increment of circuitListItr (would increment to second element, or crash if no element left) - return; - } - } - ++circuitListItr; - } - //cout << "Done CheckCircuitList" << endl; -} - -// Do the runway list - we'll do the whole runway list since it's important and there'll never be many planes on the rwy at once!! -// FIXME - at the moment it looks like we're only doing the first plane from the rwy list. -// (However, at the moment there should only be one airplane on the rwy at once, until we -// start allowing planes to line up whilst previous arrival clears the rwy.) -void FGTower::CheckRunwayList(double dt) { - //cout << "Entering CheckRunwayList..." << endl; - if(rwyOccupied) { - if(!rwyList.size()) { - rwyOccupied = false; - } else { - rwyListItr = rwyList.begin(); - TowerPlaneRec* t = *rwyListItr; - if(t->isUser) { - t->pos.setlon(user_lon_node->getDoubleValue()); - t->pos.setlat(user_lat_node->getDoubleValue()); - t->pos.setelev(user_elev_node->getDoubleValue()); - } else { - t->pos = t->planePtr->GetPos(); // We should probably only set the pos's on one walk through the traffic list in the update function, to save a few CPU should we end up duplicating this. - } - bool on_rwy = OnActiveRunway(t->pos); - if(!on_rwy) { - // TODO - for all of these we need to check what the user is *actually* doing! - if((t->opType == INBOUND) || (t->opType == STRAIGHT_IN)) { - //cout << "Tower " << ident << " is removing plane " << t->plane.callsign << " from rwy list (vacated)\n"; - //cout << "Size of rwylist was " << rwyList.size() << '\n'; - //cout << "Size of vacatedList was " << vacatedList.size() << '\n'; - RemoveFromRwyList(t->plane.callsign); - AddToVacatedList(t); - //cout << "Size of rwylist is " << rwyList.size() << '\n'; - //cout << "Size of vacatedList is " << vacatedList.size() << '\n'; - // At the moment we wait until Runway Vacated is reported by the plane before telling to contact ground etc. - // It's possible we could be a bit more proactive about this. - } else if(t->opType == OUTBOUND) { - depList.push_back(t); - depListItr = depList.begin(); - rwyList.pop_front(); - departed = true; - timeSinceLastDeparture = 0.0; - } else if(t->opType == CIRCUIT) { - //cout << ident << " adding " << t->plane.callsign << " to circuitList" << endl; - circuitList.push_back(t); - circuitListItr = circuitList.begin(); - AddToTrafficList(t); - rwyList.pop_front(); - departed = true; - timeSinceLastDeparture = 0.0; - } else if(t->opType == TTT_UNKNOWN) { - depList.push_back(t); - depListItr = depList.begin(); - //cout << ident << " adding " << t->plane.callsign << " to circuitList" << endl; - circuitList.push_back(t); - circuitListItr = circuitList.begin(); - AddToTrafficList(t); - rwyList.pop_front(); - departed = true; - timeSinceLastDeparture = 0.0; // TODO - we need to take into account that the user might taxi-in when flagged opType UNKNOWN - check speed/altitude etc to make decision as to what user is up to. - } else { - // HELP - we shouldn't ever get here!!! - } - } - } - } - //cout << "Done CheckRunwayList" << endl; -} - -// Do one plane from the approach list -void FGTower::CheckApproachList(double dt) { - //cout << "CheckApproachList called for " << ident << endl; - //cout << "AppList.size is " << appList.size() << endl; - if(!appList.empty()) { - if(appListItr == appList.end()) { - appListItr = appList.begin(); - } - TowerPlaneRec* t = *appListItr; - //cout << "t = " << t << endl; - //cout << "Checking " << t->plane.callsign << endl; - if(t->isUser) { - t->pos.setlon(user_lon_node->getDoubleValue()); - t->pos.setlat(user_lat_node->getDoubleValue()); - t->pos.setelev(user_elev_node->getDoubleValue()); - } else { - // TODO - set/update the position if it's an AI plane - } - doThresholdETACalc(); // We need this here because planes in the lists are not guaranteed to *always* have the correct ETA - //cout << "eta is " << t->eta << ", rwy is " << (rwyList.size() ? "occupied " : "clear ") << '\n'; - Point3D tortho = ortho.ConvertToLocal(t->pos); - if(t->eta < 12 && rwyList.size() && !(t->instructedToGoAround)) { - // TODO - need to make this more sophisticated - // eg. is the plane accelerating down the runway taking off [OK], - // or stationary near the start [V. BAD!!]. - // For now this should stop the AI plane landing on top of the user. - tower_plane_rec_list_iterator twrItr; - twrItr = rwyList.begin(); - TowerPlaneRec* tpr = *twrItr; - if(strcmp ( tpr->plane.callsign.c_str(), t->plane.callsign.c_str() ) == 0 && rwyList.size() == 1) { - // Fixing bug when ATC says that we must go around because of traffic on rwy - // but that traffic is we! In future we can use this expression - // for other ATC-messages like "On ground at 46, vacate left." - - } else { - string trns = t->plane.callsign; - trns += " GO AROUND TRAFFIC ON RUNWAY I REPEAT GO AROUND"; - pending_transmission = trns; - ImmediateTransmit(); - t->instructedToGoAround = true; - t->clearedToLand = false; - t->nextOnRwy = false; // But note this is recalculated so don't rely on it - // Assume it complies!!! - t->opType = CIRCUIT; - t->leg = CLIMBOUT; - if(!t->isUser) { - if(t->planePtr) { - //cout << "Registering Go-around transmission with AI plane\n"; - t->planePtr->RegisterTransmission(13); - } - } else { - // TODO - add Go-around ack to comm options, - // remove report rwy vacated. (possibly). - } - } - } else if(t->isUser && t->eta < 90 && tortho.y() > -2500 && t->clearedToLand && t->gearUpReported == false) { - // Check if gear up or down - double gp = fgGetFloat("/gear/gear/position-norm"); - if(gp < 1) { - string trnsm = t->plane.callsign; - sg_srandom_time(); - int rnd = int(sg_random() * 2) + 1; - if(rnd == 2) { // Random message for more realistic ATC ;) - trnsm += ", LANDING GEAR APPEARS UP!"; - } else { - trnsm += ", Check wheels down and locked."; - } - pending_transmission = trnsm; - ImmediateTransmit(); - t->gearUpReported = true; - } - } else if(t->eta < 90 && !t->clearedToLand) { - //doThresholdETACalc(); - doThresholdUseOrder(); - // The whip through the appList is a hack since currently t->nextOnRwy doesn't always work - // TODO - fix this! - bool cf = false; - for(tower_plane_rec_list_iterator twrItr = appList.begin(); twrItr != appList.end(); twrItr++) { - if((*twrItr)->eta < t->eta) { - cf = true; - } - } - if(t->nextOnRwy && !cf) { - if(!rwyList.size()) { - string trns = t->plane.callsign; - trns += " Cleared to land"; - pending_transmission = trns; - Transmit(); - //if(t->isUser) cout << "Transmitting cleared to Land!!!\n"; - t->clearedToLand = true; - if(!t->isUser) { - t->planePtr->RegisterTransmission(7); - } - } - } else { - //if(t->isUser) cout << "Not next\n"; - } - } - - // Check for landing... - bool landed = false; - if(!t->isUser) { - if(t->planePtr) { - if(t->planePtr->GetLeg() == LANDING_ROLL) { - landed = true; - } - } else { - SG_LOG(SG_ATC, SG_ALERT, "WARNING - not user and null planePtr in CheckApproachList!"); - } - } else { // user - if(OnActiveRunway(t->pos)) { - landed = true; - } - } - - if(landed) { - // Duplicated in CheckCircuitList - must be able to rationalise this somehow! - //cout << "A " << t->plane.callsign << " has landed, adding to rwyList...\n"; - rwyList.push_front(t); - // TODO - if(!clearedToLand) shout something!! - t->clearedToLand = false; - RemoveFromTrafficList(t->plane.callsign); - //if(t->isUser) { - // t->opType = TTT_UNKNOWN; - //} // TODO - allow the user to specify opType via ATC menu - appListItr = appList.erase(appListItr); - if(appListItr == appList.end() ) { - appListItr = appList.begin(); - } - if (appList.empty()) - return; - - } - - ++appListItr; - } - //cout << "Done" << endl; -} - -// Do one plane from the departure list -void FGTower::CheckDepartureList(double dt) { - if(!depList.empty()) { - if(depListItr == depList.end()) { - depListItr = depList.begin(); - } - TowerPlaneRec* t = *depListItr; - //cout << "Dep list, checking " << t->plane.callsign; - - double distout; // meters - if(t->isUser) distout = dclGetHorizontalSeparation(Point3D(lon, lat, elev), Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())); - else distout = dclGetHorizontalSeparation(Point3D(lon, lat, elev), t->planePtr->GetPos()); - //cout << " distout = " << distout << '\n'; - if(t->isUser && !(t->clearedToTakeOff)) { // HACK - we use clearedToTakeOff to check if ATC already contacted with plane (and cleared take-off) or not - if(!OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), false)) { - current_atcdialog->remove_entry(ident, USER_REQUEST_TAKE_OFF, TOWER); - t->clearedToTakeOff = true; // FIXME - } - } - if(distout > 10000) { - string trns = t->plane.callsign; - trns += " You are now clear of my airspace, good day"; - pending_transmission = trns; - Transmit(); - if(t->isUser) { - // Change the communication options - RemoveAllUserDialogOptions(); - //cout << "ADD A\n"; - current_atcdialog->add_entry(ident, "@AP Tower, @CS @MI miles @CD of the airport for full stop@AT", "Contact tower for VFR arrival (full stop)", TOWER, (int)USER_REQUEST_VFR_ARRIVAL_FULL_STOP); - } else { - // Send a clear-of-airspace signal - // TODO - implement this once we actually have departing AI traffic (currently all circuits or arrivals). - } - RemovePlane(t->plane.callsign); - } else { - ++depListItr; - } - } -} - -// ********** End periodic check functions *********************************************** -// *************************************************************************************** - - -// Remove all dialog options for this tower. -void FGTower::RemoveAllUserDialogOptions() { - current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_DEPARTURE, TOWER); - current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_ARRIVAL, TOWER); - current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_ARRIVAL_FULL_STOP, TOWER); - current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO, TOWER); - current_atcdialog->remove_entry(ident, USER_REPORT_3_MILE_FINAL, TOWER); - current_atcdialog->remove_entry(ident, USER_REPORT_DOWNWIND, TOWER); - current_atcdialog->remove_entry(ident, USER_REPORT_RWY_VACATED, TOWER); - current_atcdialog->remove_entry(ident, USER_REPORT_GOING_AROUND, TOWER); - current_atcdialog->remove_entry(ident, USER_REQUEST_TAKE_OFF, TOWER); -} - -// Returns true if positions of crosswind/downwind/base leg turns should be constrained by previous traffic -// plus the constraint position as a rwy orientated orthopos (meters) -bool FGTower::GetCrosswindConstraint(double& cpos) { - if(crosswind_leg_pos != 0.0) { - cpos = crosswind_leg_pos; - return(true); - } else { - cpos = 0.0; - return(false); - } -} -bool FGTower::GetDownwindConstraint(double& dpos) { - if(fabs(downwind_leg_pos) > nominal_downwind_leg_pos) { - dpos = downwind_leg_pos; - return(true); - } else { - dpos = 0.0; - return(false); - } -} -bool FGTower::GetBaseConstraint(double& bpos) { - if(base_leg_pos < nominal_base_leg_pos) { - bpos = base_leg_pos; - return(true); - } else { - bpos = nominal_base_leg_pos; - return(false); - } -} - - -// Figure out which runways are active. -// For now we'll just be simple and do one active runway - eventually this will get much more complex -// This is a private function - public interface to the results of this is through GetActiveRunway -void FGTower::DoRwyDetails() { - //cout << "GetRwyDetails called" << endl; - - // Based on the airport-id and wind get the active runway - - //wind - double hdg = wind_from_hdg->getDoubleValue(); - double speed = wind_speed_knots->getDoubleValue(); - hdg = (speed == 0.0 ? 270.0 : hdg); - //cout << "Heading = " << hdg << '\n'; - - FGRunway runway; - bool rwyGood = globals->get_runways()->search(ident, int(hdg), &runway); - if(rwyGood) { - //cout << "RUNWAY GOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOD\n"; - activeRwy = runway._rwy_no; - rwy.rwyID = runway._rwy_no; - SG_LOG(SG_ATC, SG_INFO, "Active runway for airport " << ident << " is " << activeRwy); - - // Get the threshold position - double other_way = runway._heading - 180.0; - while(other_way <= 0.0) { - other_way += 360.0; - } - // move to the +l end/center of the runway - //cout << "Runway center is at " << runway._lon << ", " << runway._lat << '\n'; - Point3D origin = Point3D(runway._lon, runway._lat, aptElev); - Point3D ref = origin; - double tshlon, tshlat, tshr; - double tolon, tolat, tor; - rwy.length = runway._length * SG_FEET_TO_METER; - rwy.width = runway._width * SG_FEET_TO_METER; - geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), other_way, - rwy.length / 2.0 - 25.0, &tshlat, &tshlon, &tshr ); - geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), runway._heading, - rwy.length / 2.0 - 25.0, &tolat, &tolon, &tor ); - // Note - 25 meters in from the runway end is a bit of a hack to put the plane ahead of the user. - // now copy what we need out of runway into rwy - rwy.threshold_pos = Point3D(tshlon, tshlat, aptElev); - Point3D takeoff_end = Point3D(tolon, tolat, aptElev); - //cout << "Threshold position = " << tshlon << ", " << tshlat << ", " << aptElev << '\n'; - //cout << "Takeoff position = " << tolon << ", " << tolat << ", " << aptElev << '\n'; - rwy.hdg = runway._heading; - // Set the projection for the local area based on this active runway - ortho.Init(rwy.threshold_pos, rwy.hdg); - rwy.end1ortho = ortho.ConvertToLocal(rwy.threshold_pos); // should come out as zero - rwy.end2ortho = ortho.ConvertToLocal(takeoff_end); - - // Set the pattern direction - // TODO - we'll check for a facilities file with this in eventually - for now assume left traffic except - // for certain circumstances (RH parallel rwy). - rwy.patternDirection = -1; // Left - if(rwy.rwyID.size() == 3) { - rwy.patternDirection = (rwy.rwyID.substr(2,1) == "R" ? 1 : -1); - } - //cout << "Doing details, rwy.patterDirection is " << rwy.patternDirection << '\n'; - } else { - SG_LOG(SG_ATC, SG_ALERT, "Help - can't get good runway in FGTower!!"); - activeRwy = "NN"; - } -} - - -// Figure out if a given position lies on the active runway -// Might have to change when we consider more than one active rwy. -bool FGTower::OnActiveRunway(const Point3D& pt) { - // TODO - check that the centre calculation below isn't confused by displaced thesholds etc. - Point3D xyc((rwy.end1ortho.x() + rwy.end2ortho.x())/2.0, (rwy.end1ortho.y() + rwy.end2ortho.y())/2.0, 0.0); - Point3D xyp = ortho.ConvertToLocal(pt); - - //cout << "Length offset = " << fabs(xyp.y() - xyc.y()) << '\n'; - //cout << "Width offset = " << fabs(xyp.x() - xyc.x()) << '\n'; - - double rlen = rwy.length/2.0 + 5.0; - double rwidth = rwy.width/2.0; - double ldiff = fabs(xyp.y() - xyc.y()); - double wdiff = fabs(xyp.x() - xyc.x()); - - return((ldiff < rlen) && (wdiff < rwidth)); -} - -// Figure out if a given position lies on any runway or not -// Only call this at startup - reading the runways database is expensive and needs to be fixed! -bool FGTower::OnAnyRunway(const Point3D& pt, bool onGround) { - ATCData ad; - double dist = current_commlist->FindClosest(lon, lat, elev, ad, TOWER, 7.0); - if(dist < 0.0) { - return(false); - } - - // Based on the airport-id, go through all the runways and check for a point in them - - // TODO - do we actually need to search for the airport - surely we already know our ident and - // can just search runways of our airport??? - //cout << "Airport ident is " << ad.ident << '\n'; - FGRunway runway; - bool rwyGood = globals->get_runways()->search(ad.ident, &runway); - if(!rwyGood) { - SG_LOG(SG_ATC, SG_WARN, "Unable to find any runways for airport ID " << ad.ident << " in FGTower"); - } - bool on = false; - while(runway._id == ad.ident) { - on = OnRunway(pt, runway); - //cout << "Runway " << runway._rwy_no << ": On = " << (on ? "true\n" : "false\n"); - if(on) { - if(onGround == false) - return(true); - if(runway._rwy_no != "xx") - return(true); - } - globals->get_runways()->next(&runway); - } - return(on); -} - - -// Returns true if successful -bool FGTower::RemoveFromTrafficList(const string& id) { - tower_plane_rec_list_iterator twrItr; - for(twrItr = trafficList.begin(); twrItr != trafficList.end(); twrItr++) { - TowerPlaneRec* tpr = *twrItr; - if(tpr->plane.callsign == id) { - trafficList.erase(twrItr); - trafficListItr = trafficList.begin(); - return(true); - } - } - SG_LOG(SG_ATC, SG_WARN, "Warning - unable to remove aircraft " << id << " from trafficList in FGTower"); - return(false); -} - - -// Returns true if successful -bool FGTower::RemoveFromAppList(const string& id) { - tower_plane_rec_list_iterator twrItr; - for(twrItr = appList.begin(); twrItr != appList.end(); twrItr++) { - TowerPlaneRec* tpr = *twrItr; - if(tpr->plane.callsign == id) { - appList.erase(twrItr); - appListItr = appList.begin(); - return(true); - } - } - //SG_LOG(SG_ATC, SG_WARN, "Warning - unable to remove aircraft " << id << " from appList in FGTower"); - return(false); -} - -// Returns true if successful -bool FGTower::RemoveFromRwyList(const string& id) { - tower_plane_rec_list_iterator twrItr; - for(twrItr = rwyList.begin(); twrItr != rwyList.end(); twrItr++) { - TowerPlaneRec* tpr = *twrItr; - if(tpr->plane.callsign == id) { - rwyList.erase(twrItr); - rwyListItr = rwyList.begin(); - return(true); - } - } - //SG_LOG(SG_ATC, SG_WARN, "Warning - unable to remove aircraft " << id << " from rwyList in FGTower"); - return(false); -} - - -// Add a tower plane rec with ETA to the traffic list in the correct position ETA-wise -// and set nextOnRwy if so. -// Returns true if this could cause a threshold ETA conflict with other traffic, false otherwise. -// For planes holding they are put in the first position with time to go, and the return value is -// true if in the first position (nextOnRwy) and false otherwise. -// See the comments in FGTower::doThresholdUseOrder for notes on the ordering -bool FGTower::AddToTrafficList(TowerPlaneRec* t, bool holding) { - //cout << "ADD: " << trafficList.size(); - //cout << "AddToTrafficList called, currently size = " << trafficList.size() << ", holding = " << holding << endl; - double separation_time = 90.0; // seconds - this is currently a guess for light plane separation, and includes a few seconds for a holding plane to taxi onto the rwy. - double departure_sep_time = 60.0; // Separation time behind departing airplanes. Comments above also apply. - bool conflict = false; - double lastETA = 0.0; - bool firstTime = true; - // FIXME - make this more robust for different plane types eg. light following heavy. - tower_plane_rec_list_iterator twrItr; - //twrItr = trafficList.begin(); - //while(1) { - for(twrItr = trafficList.begin(); twrItr != trafficList.end(); twrItr++) { - //if(twrItr == trafficList.end()) { - // cout << " END "; - // trafficList.push_back(t); - // return(holding ? firstTime : conflict); - //} else { - TowerPlaneRec* tpr = *twrItr; - if(holding) { - //cout << (tpr->isUser ? "USER!\n" : "NOT user\n"); - //cout << "tpr->eta - lastETA = " << tpr->eta - lastETA << '\n'; - double dep_allowance = (timeSinceLastDeparture < departure_sep_time ? departure_sep_time - timeSinceLastDeparture : 0.0); - double slot_time = (firstTime ? separation_time + dep_allowance : separation_time + departure_sep_time); - // separation_time + departure_sep_time in the above accounts for the fact that the arrival could be touch and go, - // and if not needs time to clear the rwy anyway. - if(tpr->eta - lastETA > slot_time) { - t->nextOnRwy = firstTime; - trafficList.insert(twrItr, t); - //cout << "\tH\t" << trafficList.size() << '\n'; - return(firstTime); - } - firstTime = false; - } else { - if(t->eta < tpr->eta) { - // Ugg - this one's tricky. - // It depends on what the two planes are doing and whether there's a conflict what we do. - if(tpr->eta - t->eta > separation_time) { // No probs, plane 2 can squeeze in before plane 1 with no apparent conflict - if(tpr->nextOnRwy) { - tpr->nextOnRwy = false; - t->nextOnRwy = true; - } - trafficList.insert(twrItr, t); - } else { // Ooops - this ones tricky - we have a potential conflict! - conflict = true; - // HACK - just add anyway for now and flag conflict - TODO - FIX THIS using CIRCUIT/STRAIGHT_IN and VFR/IFR precedence rules. - if(tpr->nextOnRwy) { - tpr->nextOnRwy = false; - t->nextOnRwy = true; - } - trafficList.insert(twrItr, t); - } - //cout << "\tC\t" << trafficList.size() << '\n'; - return(conflict); - } - } - //} - //++twrItr; - } - // If we get here we must be at the end of the list, or maybe the list is empty. - if(!trafficList.size()) { - t->nextOnRwy = true; - // conflict and firstTime should be false and true respectively in this case anyway. - } else { - t->nextOnRwy = false; - } - trafficList.push_back(t); - //cout << "\tE\t" << trafficList.size() << endl; - return(holding ? firstTime : conflict); -} - -// Add a tower plane rec with ETA to the circuit list in the correct position ETA-wise -// Returns true if this might cause a separation conflict (based on ETA) with other traffic, false otherwise. -// Safe to add a plane that is already in - planes with the same callsign are not added. -bool FGTower::AddToCircuitList(TowerPlaneRec* t) { - if(!t) { - //cout << "**********************************************\n"; - //cout << "AddToCircuitList called with NULL pointer!!!!!\n"; - //cout << "**********************************************\n"; - return false; - } - //cout << "ADD: " << circuitList.size(); - //cout << ident << " AddToCircuitList called for " << t->plane.callsign << ", currently size = " << circuitList.size() << endl; - double separation_time = 60.0; // seconds - this is currently a guess for light plane separation, and includes a few seconds for a holding plane to taxi onto the rwy. - bool conflict = false; - tower_plane_rec_list_iterator twrItr; - // First check if the plane is already in the list - //cout << "A" << endl; - //cout << "Checking whether " << t->plane.callsign << " is already in circuit list..." << endl; - //cout << "B" << endl; - for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) { - if((*twrItr)->plane.callsign == t->plane.callsign) { - //cout << "In list - returning...\n"; - return false; - } - } - //cout << "Not in list - adding..." << endl; - - for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) { - TowerPlaneRec* tpr = *twrItr; - //cout << tpr->plane.callsign << " eta is " << tpr->eta << '\n'; - //cout << "New eta is " << t->eta << '\n'; - if(t->eta < tpr->eta) { - // Ugg - this one's tricky. - // It depends on what the two planes are doing and whether there's a conflict what we do. - if(tpr->eta - t->eta > separation_time) { // No probs, plane 2 can squeeze in before plane 1 with no apparent conflict - circuitList.insert(twrItr, t); - circuitListItr = circuitList.begin(); - } else { // Ooops - this ones tricky - we have a potential conflict! - conflict = true; - // HACK - just add anyway for now and flag conflict. - circuitList.insert(twrItr, t); - circuitListItr = circuitList.begin(); - } - //cout << "\tC\t" << circuitList.size() << '\n'; - return(conflict); - } - } - // If we get here we must be at the end of the list, or maybe the list is empty. - //cout << ident << " adding " << t->plane.callsign << " to circuitList" << endl; - circuitList.push_back(t); // TODO - check the separation with the preceding plane for the conflict flag. - circuitListItr = circuitList.begin(); - //cout << "\tE\t" << circuitList.size() << endl; - return(conflict); -} - -// Add to vacated list only if not already present -void FGTower::AddToVacatedList(TowerPlaneRec* t) { - tower_plane_rec_list_iterator twrItr; - bool found = false; - for(twrItr = vacatedList.begin(); twrItr != vacatedList.end(); twrItr++) { - if((*twrItr)->plane.callsign == t->plane.callsign) { - found = true; - } - } - if(found) return; - vacatedList.push_back(t); -} - -void FGTower::AddToHoldingList(TowerPlaneRec* t) { - tower_plane_rec_list_iterator it, end = holdList.end(); - for (it = holdList.begin(); it != end; ++it) { - if ((*it)->plane.callsign == t->plane.callsign) - return; - - holdList.push_back(t); - } -} - -// Calculate the eta of a plane to the threshold. -// For ground traffic this is the fastest they can get there. -// For air traffic this is the middle approximation. -void FGTower::CalcETA(TowerPlaneRec* tpr, bool printout) { - // For now we'll be very crude and hardwire expected speeds to C172-like values - // The speeds below are specified in knots IAS and then converted to m/s - double app_ias = 100.0 * 0.514444; // Speed during straight-in approach - double circuit_ias = 80.0 * 0.514444; // Speed around circuit - double final_ias = 70.0 * 0.514444; // Speed during final approach - - //if(printout) { - //cout << "In CalcETA, airplane ident = " << tpr->plane.callsign << '\n'; - //cout << (tpr->isUser ? "USER\n" : "AI\n"); - //cout << flush; - //} - - // Sign convention - dist_out is -ve for approaching planes and +ve for departing planes - // dist_across is +ve in the pattern direction - ie a plane correctly on downwind will have a +ve dist_across - - Point3D op = ortho.ConvertToLocal(tpr->pos); - //if(printout) { - //if(!tpr->isUser) cout << "Orthopos is " << op.x() << ", " << op.y() << ' '; - //cout << "opType is " << tpr->opType << '\n'; - //} - double dist_out_m = op.y(); - double dist_across_m = fabs(op.x()); // The fabs is a hack to cope with the fact that we don't know the circuit direction yet - //cout << "Doing ETA calc for " << tpr->plane.callsign << '\n'; - - if(tpr->opType == STRAIGHT_IN || tpr->opType == INBOUND) { - //cout << "CASE 1\n"; - double dist_to_go_m = sqrt((dist_out_m * dist_out_m) + (dist_across_m * dist_across_m)); - if(dist_to_go_m < 1000) { - tpr->eta = dist_to_go_m / final_ias; - } else { - tpr->eta = (1000.0 / final_ias) + ((dist_to_go_m - 1000.0) / app_ias); - } - } else if(tpr->opType == CIRCUIT || tpr->opType == TTT_UNKNOWN) { // Hack alert - UNKNOWN has sort of been added here as a temporary hack. - //cout << "CASE 2\n"; - // It's complicated - depends on if base leg is delayed or not - //if(printout) { - //cout << "Leg = " << tpr->leg << '\n'; - //} - if(tpr->leg == LANDING_ROLL) { - tpr->eta = 0; - } else if((tpr->leg == FINAL) || (tpr->leg == TURN4)) { - //cout << "dist_out_m = " << dist_out_m << '\n'; - tpr->eta = fabs(dist_out_m) / final_ias; - } else if((tpr->leg == BASE) || (tpr->leg == TURN3)) { - tpr->eta = (fabs(dist_out_m) / final_ias) + (dist_across_m / circuit_ias); - } else { - // Need to calculate where base leg is likely to be - // FIXME - for now I'll hardwire it to 1000m which is what AILocalTraffic uses!!! - // TODO - as a matter of design - AILocalTraffic should get the nominal no-traffic base turn distance from Tower, since in real life the published pattern might differ from airport to airport - double nominal_base_dist_out_m = -1000; - double current_base_dist_out_m; - if(!GetBaseConstraint(current_base_dist_out_m)) { - current_base_dist_out_m = nominal_base_dist_out_m; - } - //cout << "current_base_dist_out_m = " << current_base_dist_out_m << '\n'; - double nominal_dist_across_m = 1000; // Hardwired value from AILocalTraffic - double current_dist_across_m; - if(!GetDownwindConstraint(current_dist_across_m)) { - current_dist_across_m = nominal_dist_across_m; - } - double nominal_cross_dist_out_m = 2000; // Bit of a guess - AI plane turns to crosswind at 700ft agl. - tpr->eta = fabs(current_base_dist_out_m) / final_ias; // final - //cout << "a = " << tpr->eta << '\n'; - if((tpr->leg == DOWNWIND) || (tpr->leg == TURN2)) { - tpr->eta += dist_across_m / circuit_ias; - //cout << "b = " << tpr->eta << '\n'; - tpr->eta += fabs(current_base_dist_out_m - dist_out_m) / circuit_ias; - //cout << "c = " << tpr->eta << '\n'; - } else if((tpr->leg == CROSSWIND) || (tpr->leg == TURN1)) { - //cout << "CROSSWIND calc: "; - //cout << tpr->eta << ' '; - if(dist_across_m > nominal_dist_across_m) { - tpr->eta += dist_across_m / circuit_ias; - //cout << "a "; - } else { - tpr->eta += nominal_dist_across_m / circuit_ias; - //cout << "b "; - } - //cout << tpr->eta << ' '; - // should we use the dist across of the previous plane if there is previous still on downwind? - //if(printout) cout << "bb = " << tpr->eta << '\n'; - if(dist_out_m > nominal_cross_dist_out_m) { - tpr->eta += fabs(current_base_dist_out_m - dist_out_m) / circuit_ias; - //cout << "c "; - } else { - tpr->eta += fabs(current_base_dist_out_m - nominal_cross_dist_out_m) / circuit_ias; - //cout << "d "; - } - //cout << tpr->eta << ' '; - //if(printout) cout << "cc = " << tpr->eta << '\n'; - if(nominal_dist_across_m > dist_across_m) { - tpr->eta += (nominal_dist_across_m - dist_across_m) / circuit_ias; - //cout << "e "; - } else { - // Nothing to add - //cout << "f "; - } - //cout << tpr->eta << '\n'; - //if(printout) cout << "dd = " << tpr->eta << '\n'; - } else { - // We've only just started - why not use a generic estimate? - tpr->eta = 240.0; - } - } - //if(printout) { - // cout << "ETA = " << tpr->eta << '\n'; - //} - //if(!tpr->isUser) cout << tpr->plane.callsign << '\t' << tpr->eta << '\n'; - } else { - tpr->eta = 99999; - } -} - - -// Calculate the distance of a plane to the threshold in meters -// TODO - Modify to calculate flying distance of a plane in the circuit -double FGTower::CalcDistOutM(TowerPlaneRec* tpr) { - return(dclGetHorizontalSeparation(rwy.threshold_pos, tpr->pos)); -} - - -// Calculate the distance of a plane to the threshold in miles -// TODO - Modify to calculate flying distance of a plane in the circuit -double FGTower::CalcDistOutMiles(TowerPlaneRec* tpr) { - return(CalcDistOutM(tpr) / 1600.0); // FIXME - use a proper constant if possible. -} - - -// Iterate through all the lists, update the position of, and call CalcETA for all the planes. -void FGTower::doThresholdETACalc() { - //cout << "Entering doThresholdETACalc..." << endl; - tower_plane_rec_list_iterator twrItr; - // Do the approach list first - for(twrItr = appList.begin(); twrItr != appList.end(); twrItr++) { - TowerPlaneRec* tpr = *twrItr; - if(!(tpr->isUser)) tpr->pos = tpr->planePtr->GetPos(); - //cout << "APP: "; - CalcETA(tpr); - } - // Then the circuit list - //cout << "Circuit list size is " << circuitList.size() << '\n'; - for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) { - TowerPlaneRec* tpr = *twrItr; - if(!(tpr->isUser)) tpr->pos = tpr->planePtr->GetPos(); - //cout << "CIRC: "; - CalcETA(tpr); - } - //cout << "Done doThresholdETCCalc" << endl; -} - - -// Check that the planes in traffic list are correctly ordered, -// that the nearest (timewise) is flagged next on rwy, and return -// true if any threshold use conflicts are detected, false otherwise. -bool FGTower::doThresholdUseOrder() { - //cout << "Entering doThresholdUseOrder..." << endl; - bool conflict = false; - - // Wipe out traffic list, go through circuit, app and hold list, and reorder them in traffic list. - // Here's the rather simplistic assumptions we're using: - // Currently all planes are assumed to be GA light singles with corresponding speeds and separation times. - // In order of priority for runway use: - // STRAIGHT_IN > CIRCUIT > HOLDING_FOR_DEPARTURE - // No modification of planes speeds occurs - conflicts are resolved by delaying turn for base, - // and holding planes until a space. - // When calculating if a holding plane can use the runway, time clearance from last departure - // as well as time clearance to next arrival must be considered. - - trafficList.clear(); - - tower_plane_rec_list_iterator twrItr; - // Do the approach list first - //if(ident == "KRHV") cout << "A" << flush; - for(twrItr = appList.begin(); twrItr != appList.end(); twrItr++) { - TowerPlaneRec* tpr = *twrItr; - //if(ident == "KRHV") cout << tpr->plane.callsign << '\n'; - conflict = AddToTrafficList(tpr); - } - // Then the circuit list - //if(ident == "KRHV") cout << "C" << flush; - for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) { - TowerPlaneRec* tpr = *twrItr; - //if(ident == "KRHV") cout << tpr->plane.callsign << '\n'; - conflict = AddToTrafficList(tpr); - } - // And finally the hold list - //cout << "H" << endl; - for(twrItr = holdList.begin(); twrItr != holdList.end(); twrItr++) { - TowerPlaneRec* tpr = *twrItr; - AddToTrafficList(tpr, true); - } - - - if(0) { - //if(ident == "KRHV") { - cout << "T\n"; - for(twrItr = trafficList.begin(); twrItr != trafficList.end(); twrItr++) { - TowerPlaneRec* tpr = *twrItr; - cout << tpr->plane.callsign << '\t' << tpr->eta << '\t'; - } - cout << endl; - } - - //cout << "Done doThresholdUseOrder" << endl; - return(conflict); -} - - -// Return the ETA of plane no. list_pos (1-based) in the traffic list. -// i.e. list_pos = 1 implies next to use runway. -double FGTower::GetTrafficETA(unsigned int list_pos, bool printout) { - if(trafficList.size() < list_pos) { - return(99999); - } - - tower_plane_rec_list_iterator twrItr; - twrItr = trafficList.begin(); - for(unsigned int i = 1; i < list_pos; i++, twrItr++); - TowerPlaneRec* tpr = *twrItr; - CalcETA(tpr, printout); - //cout << "ETA returned = " << tpr->eta << '\n'; - return(tpr->eta); -} - - -void FGTower::ContactAtHoldShort(const PlaneRec& plane, FGAIPlane* requestee, tower_traffic_type operation) { - // HACK - assume that anything contacting at hold short is new for now - FIXME LATER - TowerPlaneRec* t = new TowerPlaneRec; - t->plane = plane; - t->planePtr = requestee; - t->holdShortReported = true; - t->clearedToLineUp = false; - t->clearedToTakeOff = false; - t->opType = operation; - t->pos = requestee->GetPos(); - - //cout << "Hold Short reported by " << plane.callsign << '\n'; - SG_LOG(SG_ATC, SG_BULK, "Hold Short reported by " << plane.callsign); - -/* - bool next = AddToTrafficList(t, true); - if(next) { - double teta = GetTrafficETA(2); - if(teta < 150.0) { - t->clearanceCounter = 7.0; // This reduces the delay before response to 3 secs if an immediate takeoff is reqd - //cout << "Reducing response time to request due imminent traffic\n"; - } - } else { - } -*/ - // TODO - possibly add the reduced interval to clearance when immediate back in under the new scheme - - holdList.push_back(t); - - responseReqd = true; -} - -// Register the presence of an AI plane at a point where contact would already have been made in real life -// CAUTION - currently it is assumed that this plane's callsign is unique - it is up to AIMgr to generate unique callsigns. -void FGTower::RegisterAIPlane(const PlaneRec& plane, FGAIPlane* ai, const tower_traffic_type& op, const PatternLeg& lg) { - // At the moment this is only going to be tested with inserting an AI plane on downwind - TowerPlaneRec* t = new TowerPlaneRec; - t->plane = plane; - t->planePtr = ai; - t->opType = op; - t->leg = lg; - t->pos = ai->GetPos(); - - CalcETA(t); - - if(op == CIRCUIT && lg != LEG_UNKNOWN) { - AddToCircuitList(t); - } else { - // FLAG A WARNING - } - - doThresholdUseOrder(); -} - -void FGTower::DeregisterAIPlane(const string& id) { - RemovePlane(id); -} - -// Contact tower for VFR approach -// eg "Cessna Charlie Foxtrot Golf Foxtrot Sierra eight miles South of the airport for full stop with Bravo" -// This function probably only called via user interaction - AI planes will have an overloaded function taking a planerec. -// opt defaults to AIP_LT_UNKNOWN -void FGTower::VFRArrivalContact(const string& ID, const LandingType& opt) { - //cout << "USER Request Landing Clearance called for ID " << ID << '\n'; - - // For now we'll assume that the user is a light plane and can get him/her to join the circuit if necessary. - - TowerPlaneRec* t; - string usercall = fgGetString("/sim/user/callsign"); - if(ID == "USER" || ID == usercall) { - t = FindPlane(usercall); - if(!t) { - //cout << "NOT t\n"; - t = new TowerPlaneRec; - t->isUser = true; - t->pos.setlon(user_lon_node->getDoubleValue()); - t->pos.setlat(user_lat_node->getDoubleValue()); - t->pos.setelev(user_elev_node->getDoubleValue()); - } else { - //cout << "IS t\n"; - // Oops - the plane is already registered with this tower - maybe we took off and flew a giant circuit without - // quite getting out of tower airspace - just ignore for now and treat as new arrival. - // TODO - Maybe should remove from departure and circuit list if in there though!! - } - } else { - // Oops - something has gone wrong - put out a warning - cout << "WARNING - FGTower::VFRContact(string ID, LandingType lt) called with ID " << ID << " which does not appear to be the user.\n"; - return; - } - - - // TODO - // Calculate where the plane is in relation to the active runway and it's circuit - // and set the op-type as appropriate. - - // HACK - to get up and running I'm going to assume that the user contacts tower on a staight-in final for now. - t->opType = STRAIGHT_IN; - - t->plane.type = GA_SINGLE; // FIXME - Another assumption! - t->plane.callsign = usercall; - - t->vfrArrivalReported = true; - responseReqd = true; - - appList.push_back(t); // Not necessarily permanent - appListItr = appList.begin(); - AddToTrafficList(t); - - current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_ARRIVAL, TOWER); - current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_ARRIVAL_FULL_STOP, TOWER); - current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO, TOWER); -} - -// landingType defaults to AIP_LT_UNKNOWN -void FGTower::VFRArrivalContact(const PlaneRec& plane, FGAIPlane* requestee, const LandingType& lt) { - //cout << "VFRArrivalContact called for plane " << plane.callsign << " at " << ident << '\n'; - // Possible hack - assume this plane is new for now - TODO - should check really - TowerPlaneRec* t = new TowerPlaneRec; - t->plane = plane; - t->planePtr = requestee; - t->landingType = lt; - t->pos = requestee->GetPos(); - - //cout << "Hold Short reported by " << plane.callsign << '\n'; - SG_LOG(SG_ATC, SG_BULK, "VFR arrival contact made by " << plane.callsign); - //cout << "VFR arrival contact made by " << plane.callsign << '\n'; - - // HACK - to get up and running I'm going to assume a staight-in final for now. - t->opType = STRAIGHT_IN; - - t->vfrArrivalReported = true; - responseReqd = true; - - //cout << "Before adding, appList.size = " << appList.size() << " at " << ident << '\n'; - appList.push_back(t); // Not necessarily permanent - appListItr = appList.begin(); - //cout << "After adding, appList.size = " << appList.size() << " at " << ident << '\n'; - AddToTrafficList(t); -} - -void FGTower::RequestDepartureClearance(const string& ID) { - //cout << "Request Departure Clearance called...\n"; -} - -void FGTower::RequestTakeOffClearance(const string& ID) { - string uid=ID; - if(ID == "USER") { - uid = fgGetString("/sim/user/callsign"); - current_atcdialog->remove_entry(ident, USER_REQUEST_TAKE_OFF, TOWER); - } - TowerPlaneRec* t = FindPlane(uid); - if(t) { - if(!(t->clearedToTakeOff)) { - departed = false; - t->lineUpReported=true; - responseReqd = true; - } - } - else { - SG_LOG(SG_ATC, SG_WARN, "WARNING: Unable to find plane " << ID << " in FGTower::RequestTakeOffClearance(...)"); - } -} - -void FGTower::ReportFinal(const string& ID) { - //cout << "Report Final Called at tower " << ident << " by plane " << ID << '\n'; - string uid=ID; - if(ID == "USER") { - uid = fgGetString("/sim/user/callsign"); - current_atcdialog->remove_entry(ident, USER_REPORT_3_MILE_FINAL, TOWER); - } - TowerPlaneRec* t = FindPlane(uid); - if(t) { - t->finalReported = true; - t->finalAcknowledged = false; - if(!(t->clearedToLand)) { - responseReqd = true; - } else { - // possibly respond with wind even if already cleared to land? - t->finalReported = false; - t->finalAcknowledged = true; - // HACK!! - prevents next reporting being misinterpreted as this one. - } - } else { - SG_LOG(SG_ATC, SG_WARN, "WARNING: Unable to find plane " << ID << " in FGTower::ReportFinal(...)"); - } -} - -void FGTower::ReportLongFinal(const string& ID) { - string uid=ID; - if(ID == "USER") { - uid = fgGetString("/sim/user/callsign"); - current_atcdialog->remove_entry(ident, USER_REPORT_3_MILE_FINAL, TOWER); - } - TowerPlaneRec* t = FindPlane(uid); - if(t) { - t->longFinalReported = true; - t->longFinalAcknowledged = false; - if(!(t->clearedToLand)) { - responseReqd = true; - } // possibly respond with wind even if already cleared to land? - } else { - SG_LOG(SG_ATC, SG_WARN, "WARNING: Unable to find plane " << ID << " in FGTower::ReportLongFinal(...)"); - } -} - -//void FGTower::ReportOuterMarker(string ID); -//void FGTower::ReportMiddleMarker(string ID); -//void FGTower::ReportInnerMarker(string ID); - -void FGTower::ReportRunwayVacated(const string& ID) { - //cout << "Report Runway Vacated Called at tower " << ident << " by plane " << ID << '\n'; - string uid=ID; - if(ID == "USER") { - uid = fgGetString("/sim/user/callsign"); - current_atcdialog->remove_entry(ident, USER_REPORT_RWY_VACATED, TOWER); - } - TowerPlaneRec* t = FindPlane(uid); - if(t) { - //cout << "Found it...\n"; - t->rwyVacatedReported = true; - responseReqd = true; - } else { - SG_LOG(SG_ATC, SG_WARN, "WARNING: Unable to find plane " << ID << " in FGTower::ReportRunwayVacated(...)"); - SG_LOG(SG_ATC, SG_ALERT, "A WARNING: Unable to find plane " << ID << " in FGTower::ReportRunwayVacated(...)"); - //cout << "WARNING: Unable to find plane " << ID << " in FGTower::ReportRunwayVacated(...)\n"; - } -} - -TowerPlaneRec* FGTower::FindPlane(const string& ID) { - //cout << "FindPlane called for " << ID << "...\n"; - tower_plane_rec_list_iterator twrItr; - // Do the approach list first - for(twrItr = appList.begin(); twrItr != appList.end(); twrItr++) { - //cout << "appList callsign is " << (*twrItr)->plane.callsign << '\n'; - if((*twrItr)->plane.callsign == ID) return(*twrItr); - } - // Then the circuit list - for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) { - //cout << "circuitList callsign is " << (*twrItr)->plane.callsign << '\n'; - if((*twrItr)->plane.callsign == ID) return(*twrItr); - } - // Then the runway list - //cout << "rwyList.size() is " << rwyList.size() << '\n'; - for(twrItr = rwyList.begin(); twrItr != rwyList.end(); twrItr++) { - //cout << "rwyList callsign is " << (*twrItr)->plane.callsign << '\n'; - if((*twrItr)->plane.callsign == ID) return(*twrItr); - } - // The hold list - for(twrItr = holdList.begin(); twrItr != holdList.end(); twrItr++) { - if((*twrItr)->plane.callsign == ID) return(*twrItr); - } - // And finally the vacated list - for(twrItr = vacatedList.begin(); twrItr != vacatedList.end(); twrItr++) { - //cout << "vacatedList callsign is " << (*twrItr)->plane.callsign << '\n'; - if((*twrItr)->plane.callsign == ID) return(*twrItr); - } - SG_LOG(SG_ATC, SG_WARN, "Unable to find " << ID << " in FGTower::FindPlane(...)"); - //exit(-1); - return(NULL); -} - -void FGTower::RemovePlane(const string& ID) { - //cout << ident << " RemovePlane called for " << ID << '\n'; - // We have to be careful here - we want to erase the plane from all lists it is in, - // but we can only delete it once, AT THE END. - TowerPlaneRec* t = NULL; - tower_plane_rec_list_iterator twrItr; - for(twrItr = appList.begin(); twrItr != appList.end();) { - if((*twrItr)->plane.callsign == ID) { - t = *twrItr; - twrItr = appList.erase(twrItr); - appListItr = appList.begin(); - // HACK: aircraft are sometimes more than once in a list, so we need to - // remove them all before we can delete the TowerPlaneRec class - //break; - } else - ++twrItr; - } - for(twrItr = depList.begin(); twrItr != depList.end();) { - if((*twrItr)->plane.callsign == ID) { - t = *twrItr; - twrItr = depList.erase(twrItr); - depListItr = depList.begin(); - } else - ++twrItr; - } - for(twrItr = circuitList.begin(); twrItr != circuitList.end();) { - if((*twrItr)->plane.callsign == ID) { - t = *twrItr; - twrItr = circuitList.erase(twrItr); - circuitListItr = circuitList.begin(); - } else - ++twrItr; - } - for(twrItr = holdList.begin(); twrItr != holdList.end();) { - if((*twrItr)->plane.callsign == ID) { - t = *twrItr; - twrItr = holdList.erase(twrItr); - holdListItr = holdList.begin(); - } else - ++twrItr; - } - for(twrItr = rwyList.begin(); twrItr != rwyList.end();) { - if((*twrItr)->plane.callsign == ID) { - t = *twrItr; - twrItr = rwyList.erase(twrItr); - rwyListItr = rwyList.begin(); - } else - ++twrItr; - } - for(twrItr = vacatedList.begin(); twrItr != vacatedList.end();) { - if((*twrItr)->plane.callsign == ID) { - t = *twrItr; - twrItr = vacatedList.erase(twrItr); - vacatedListItr = vacatedList.begin(); - } else - ++twrItr; - } - for(twrItr = trafficList.begin(); twrItr != trafficList.end();) { - if((*twrItr)->plane.callsign == ID) { - t = *twrItr; - twrItr = trafficList.erase(twrItr); - trafficListItr = trafficList.begin(); - } else - ++twrItr; - } - // And finally, delete the record. - delete t; -} - -void FGTower::ReportDownwind(const string& ID) { - //cout << "ReportDownwind(...) called\n"; - string uid=ID; - if(ID == "USER") { - uid = fgGetString("/sim/user/callsign"); - current_atcdialog->remove_entry(ident, USER_REPORT_DOWNWIND, TOWER); - } - TowerPlaneRec* t = FindPlane(uid); - if(t) { - t->downwindReported = true; - responseReqd = true; - // If the plane is in the app list, remove it and put it in the circuit list instead. - // Ideally we might want to do this at the 2 mile report prior to 45 deg entry, but at - // the moment that would b&gg?r up the constraint position calculations. - RemoveFromAppList(ID); - t->leg = DOWNWIND; - if(t->isUser) { - t->pos.setlon(user_lon_node->getDoubleValue()); - t->pos.setlat(user_lat_node->getDoubleValue()); - t->pos.setelev(user_elev_node->getDoubleValue()); - } else { - // ASSERT(t->planePtr != NULL); - t->pos = t->planePtr->GetPos(); - } - CalcETA(t); - AddToCircuitList(t); - } else { - SG_LOG(SG_ATC, SG_WARN, "WARNING: Unable to find plane " << ID << " in FGTower::ReportDownwind(...)"); - } -} - -void FGTower::ReportGoingAround(const string& ID) { - string uid=ID; - if(ID == "USER") { - uid = fgGetString("/sim/user/callsign"); - RemoveAllUserDialogOptions(); // TODO - it would be much more efficient if ATCDialog simply had a clear() function!!! - current_atcdialog->add_entry(ident, "@AP Tower, @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND); - } - TowerPlaneRec* t = FindPlane(uid); - if(t) { - //t->goAroundReported = true; // No need to set this until we start responding to it. - responseReqd = false; // might change in the future but for now we'll not distract them during the go-around. - // If the plane is in the app list, remove it and put it in the circuit list instead. - RemoveFromAppList(ID); - t->leg = CLIMBOUT; - if(t->isUser) { - t->pos.setlon(user_lon_node->getDoubleValue()); - t->pos.setlat(user_lat_node->getDoubleValue()); - t->pos.setelev(user_elev_node->getDoubleValue()); - } else { - // ASSERT(t->planePtr != NULL); - t->pos = t->planePtr->GetPos(); - } - CalcETA(t); - AddToCircuitList(t); - } else { - SG_LOG(SG_ATC, SG_WARN, "WARNING: Unable to find plane " << ID << " in FGTower::ReportDownwind(...)"); - } -} - -string FGTower::GenText(const string& m, int c) { - const int cmax = 300; - //string message; - char tag[4]; - char crej = '@'; - char mes[cmax]; - char dum[cmax]; - //char buf[10]; - char *pos; - int len; - //FGTransmission t; - string usercall = fgGetString("/sim/user/callsign"); - TowerPlaneRec* t = FindPlane(responseID); - - //transmission_list_type tmissions = transmissionlist_station[station]; - //transmission_list_iterator current = tmissions.begin(); - //transmission_list_iterator last = tmissions.end(); - - //for ( ; current != last ; ++current ) { - // if ( current->get_code().c1 == code.c1 && - // current->get_code().c2 == code.c2 && - // current->get_code().c3 == code.c3 ) { - - //if ( ttext ) message = current->get_transtext(); - //else message = current->get_menutext(); - strcpy( &mes[0], m.c_str() ); - - // Replace all the '@' parameters with the actual text. - int check = 0; // If mes gets overflowed the while loop can go infinite - double gp = fgGetFloat("/gear/gear/position-norm"); - while ( strchr(&mes[0], crej) != NULL ) { // ie. loop until no more occurances of crej ('@') found - pos = strchr( &mes[0], crej ); - memmove(&tag[0], pos, 3); - tag[3] = '\0'; - int i; - len = 0; - for ( i=0; iget_ATC_mgr()->GetFrequency(ident, ATIS) / 100.0; - if(f) { - string atis_id; - atis_id = ", information " + GetATISID(); - strcat( &dum[0], atis_id.c_str() ); - } - } - else if ( strcmp ( tag, "@VD" ) == 0 ) { - /* - if ( tpars.VDir == 1 ) { - char buf[] = "Descend and maintain"; - strcat( &dum[0], &buf[0] ); - } - else if ( tpars.VDir == 2 ) { - char buf[] = "Maintain"; - strcat( &dum[0], &buf[0] ); - } - else if ( tpars.VDir == 3 ) { - char buf[] = "Climb and maintain"; - strcat( &dum[0], &buf[0] ); - } - */ - } - else if ( strcmp ( tag, "@AL" ) == 0 ) { - /* - char buf[10]; - sprintf( buf, "%i", (int)(tpars.alt) ); - strcat( &dum[0], &buf[0] ); - */ - } - else if ( strcmp ( tag, "@TO" ) == 0 ) { // Requesting take-off or departure clearance - string tmp; - if (rwyOccupied) { - tmp = "Ready for take-off"; - } else { - if (OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), - user_lat_node->getDoubleValue(), 0.0),true)) { - tmp = "Request take-off clearance"; - } else { - tmp = "Request departure clearance"; - } - } - strcat(&dum[0], tmp.c_str()); - } - else if ( strcmp ( tag, "@MI" ) == 0 ) { - char buf[10]; - //sprintf( buf, "%3.1f", tpars.miles ); - int dist_miles = (int)dclGetHorizontalSeparation(Point3D(lon, lat, elev), Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())) / 1600; - sprintf(buf, "%i", dist_miles); - strcat( &dum[0], &buf[0] ); - } - else if ( strcmp ( tag, "@FR" ) == 0 ) { - /* - char buf[10]; - sprintf( buf, "%6.2f", tpars.freq ); - strcat( &dum[0], &buf[0] ); - */ - } - else if ( strcmp ( tag, "@RW" ) == 0 ) { - strcat(&dum[0], ConvertRwyNumToSpokenString(activeRwy).c_str()); - } - else if ( strcmp ( tag, "@GR" ) == 0 ) { // Gear position (on final) - if(t->gearWasUp && gp > 0.99) { - strcat(&dum[0], ", gear down, ready to land."); - } - } - else if(strcmp(tag, "@CD") == 0) { // @CD = compass direction - double h = GetHeadingFromTo(Point3D(lon, lat, elev), Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())); - while(h < 0.0) h += 360.0; - while(h > 360.0) h -= 360.0; - if(h < 22.5 || h > 337.5) { - strcat(&dum[0], "North"); - } else if(h < 67.5) { - strcat(&dum[0], "North-East"); - } else if(h < 112.5) { - strcat(&dum[0], "East"); - } else if(h < 157.5) { - strcat(&dum[0], "South-East"); - } else if(h < 202.5) { - strcat(&dum[0], "South"); - } else if(h < 247.5) { - strcat(&dum[0], "South-West"); - } else if(h < 292.5) { - strcat(&dum[0], "West"); - } else { - strcat(&dum[0], "North-West"); - } - } else { - cout << "Tag " << tag << " not found" << endl; - break; - } - strcat( &dum[0], &mes[len+3] ); - strcpy( &mes[0], &dum[0] ); - - ++check; - if(check > 10) { - SG_LOG(SG_ATC, SG_WARN, "WARNING: Possibly endless loop terminated in FGTransmissionlist::gen_text(...)"); - break; - } - } - - //cout << mes << endl; - //break; - //} - //} - return mes[0] ? mes : "No transmission found"; -} - -string FGTower::GetWeather() { - std::ostringstream msg; - - // wind - double hdg = wind_from_hdg->getDoubleValue(); - double speed = wind_speed_knots->getDoubleValue(); - if (speed < 1) - msg << "wind calm"; - else - msg << "wind " << int(hdg) << " degrees at " << int(speed) << " knots"; - - // visibility - double visibility = fgGetDouble("/environment/visibility-m"); - if (visibility < 10000) - msg << ", visibility " << int(visibility / 1609) << " miles"; - - // pressure / altimeter - double pressure = fgGetDouble("/environment/pressure-sea-level-inhg"); - msg << ", QFE " << fixed << setprecision(2) << pressure << "."; - - return msg.str(); -} - -string FGTower::GetATISID() { - int hours = fgGetInt("/sim/time/utc/hour"); - int phonetic_id = current_commlist->GetCallSign(ident, hours, 0); - return GetPhoneticIdent(phonetic_id); -} - -ostream& operator << (ostream& os, tower_traffic_type ttt) { - switch(ttt) { - case(CIRCUIT): return(os << "CIRCUIT"); - case(INBOUND): return(os << "INBOUND"); - case(OUTBOUND): return(os << "OUTBOUND"); - case(TTT_UNKNOWN): return(os << "UNKNOWN"); - case(STRAIGHT_IN): return(os << "STRAIGHT_IN"); - } - return(os << "ERROR - Unknown switch in tower_traffic_type operator << "); -} - diff --git a/src/ATC/tower.hxx b/src/ATC/tower.hxx deleted file mode 100644 index 0fe4103a7..000000000 --- a/src/ATC/tower.hxx +++ /dev/null @@ -1,365 +0,0 @@ -// FGTower - a class to provide tower control at towered airports. -// -// Written by David Luff, started March 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifndef _FG_TOWER_HXX -#define _FG_TOWER_HXX - -#include -#include -#include -#include - -#include STL_IOSTREAM -#include STL_STRING - -SG_USING_STD(string); -SG_USING_STD(ios); - -#include "ATC.hxx" -#include "ATCProjection.hxx" -#include "AIPlane.hxx" - -class FGATCMgr; -class FGGround; - -//DCL - a complete guess for now. -#define FG_TOWER_DEFAULT_RANGE 30 - -enum tower_traffic_type { - CIRCUIT, - INBOUND, // CIRCUIT traffic gets changed to INBOUND when on final of the full-stop circuit. - OUTBOUND, - TTT_UNKNOWN, // departure, but we don't know if for circuits or leaving properly - STRAIGHT_IN -}; - -ostream& operator << (ostream& os, tower_traffic_type ttt); - -enum tower_callback_type { - USER_REQUEST_VFR_DEPARTURE = 1, - USER_REQUEST_VFR_ARRIVAL = 2, - USER_REQUEST_VFR_ARRIVAL_FULL_STOP = 3, - USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO = 4, - USER_REPORT_3_MILE_FINAL = 5, - USER_REPORT_DOWNWIND = 6, - USER_REPORT_RWY_VACATED = 7, - USER_REPORT_GOING_AROUND = 8, - USER_REQUEST_TAKE_OFF = 9 -}; - -// TODO - need some differentiation of IFR and VFR traffic in order to give the former priority. - -// Structure for holding details of a plane under tower control. -// Not fixed yet - may include more stuff later. -class TowerPlaneRec { - -public: - - TowerPlaneRec(); - TowerPlaneRec(const PlaneRec& p); - TowerPlaneRec(const Point3D& pt); - TowerPlaneRec(const PlaneRec& p, const Point3D& pt); - - FGAIPlane* planePtr; // This might move to the planeRec eventually - PlaneRec plane; - - Point3D pos; - double eta; // seconds - double dist_out; // meters from theshold - bool clearedToLand; - bool clearedToLineUp; - bool clearedToTakeOff; - // ought to add time cleared to depart so we can nag if necessary - bool holdShortReported; - bool lineUpReported; - bool downwindReported; - bool longFinalReported; - bool longFinalAcknowledged; - bool finalReported; - bool finalAcknowledged; - bool rwyVacatedReported; - bool rwyVacatedAcknowledged; - bool goAroundReported; // set true if plane informs tower that it's going around. - bool instructedToGoAround; // set true if plane told by tower to go around. - bool onRwy; // is physically on the runway - bool nextOnRwy; // currently projected by tower to be the next on the runway - bool gearWasUp; // Tell to ATC about gear - bool gearUpReported; // Tell to pilot about landing gear - - bool vfrArrivalReported; - bool vfrArrivalAcknowledged; - - // Type of operation the plane is doing - tower_traffic_type opType; - - // Whereabouts in circuit if doing circuits - PatternLeg leg; - - LandingType landingType; - bool isUser; // true if this plane is the user -}; - - -typedef list < TowerPlaneRec* > tower_plane_rec_list_type; -typedef tower_plane_rec_list_type::iterator tower_plane_rec_list_iterator; -typedef tower_plane_rec_list_type::const_iterator tower_plane_rec_list_const_iterator; - - -class FGTower : public FGATC { - -public: - - FGTower(); - ~FGTower(); - - void Init(); - - void Update(double dt); - - void ReceiveUserCallback(int code); - - // Contact tower for VFR approach - // eg "Cessna Charlie Foxtrot Golf Foxtrot Sierra eight miles South of the airport for full stop with Bravo" - // This function probably only called via user interaction - AI planes will have an overloaded function taking a planerec. - void VFRArrivalContact(const string& ID, const LandingType& opt = AIP_LT_UNKNOWN); - // For the AI planes... - void VFRArrivalContact(const PlaneRec& plane, FGAIPlane* requestee, const LandingType& lt = AIP_LT_UNKNOWN); - - void RequestDepartureClearance(const string& ID); - void RequestTakeOffClearance(const string& ID); - void ReportFinal(const string& ID); - void ReportLongFinal(const string& ID); - void ReportOuterMarker(const string& ID); - void ReportMiddleMarker(const string& ID); - void ReportInnerMarker(const string& ID); - void ReportRunwayVacated(const string& ID); - void ReportReadyForDeparture(const string& ID); - void ReportDownwind(const string& ID); - void ReportGoingAround(const string& ID); - - // Contact tower when at a hold short for departure - for now we'll assume plane - maybe vehicles might want to cross runway eventually? - void ContactAtHoldShort(const PlaneRec& plane, FGAIPlane* requestee, tower_traffic_type operation); - - // Register the presence of an AI plane at a point where contact would already have been made in real life - // CAUTION - currently it is assumed that this plane's callsign is unique - it is up to AIMgr to generate unique callsigns. - void RegisterAIPlane(const PlaneRec& plane, FGAIPlane* ai, const tower_traffic_type& op, const PatternLeg& lg = LEG_UNKNOWN); - - // Deregister and remove an AI plane. - void DeregisterAIPlane(const string& id); - - // Public interface to the active runway - this will get more complex - // in the future and consider multi-runway use, airplane weight etc. - inline const string& GetActiveRunway() const { return activeRwy; } - inline const RunwayDetails& GetActiveRunwayDetails() const { return rwy; } - // Get the pattern direction of the active rwy. - inline int GetPatternDirection() const { return rwy.patternDirection; } - - inline const string& get_trans_ident() const { return trans_ident; } - - inline FGGround* const GetGroundPtr() const { return ground; } - - // Returns true if positions of crosswind/downwind/base leg turns should be constrained by previous traffic - // plus the constraint position as a rwy orientated orthopos (meters) - bool GetCrosswindConstraint(double& cpos); - bool GetDownwindConstraint(double& dpos); - bool GetBaseConstraint(double& bpos); - - string GenText(const string& m, int c); - string GetWeather(); - string GetATISID(); - -private: - FGATCMgr* ATCmgr; - // This is purely for synactic convienience to avoid writing globals->get_ATC_mgr()-> all through the code! - - // Respond to a transmission - void Respond(); - - void ProcessRunwayVacatedReport(TowerPlaneRec* t); - void ProcessDownwindReport(TowerPlaneRec* t); - - // Remove all options from the user dialog choice - void RemoveAllUserDialogOptions(); - - // Periodic checks on the various traffic. - void CheckHoldList(double dt); - void CheckCircuitList(double dt); - void CheckRunwayList(double dt); - void CheckApproachList(double dt); - void CheckDepartureList(double dt); - - // Currently this assumes we *are* next on the runway and doesn't check for planes about to land - - // this should be done prior to calling this function. - void ClearHoldingPlane(TowerPlaneRec* t); - - // Find a pointer to plane of callsign ID within the internal data structures - TowerPlaneRec* FindPlane(const string& ID); - - // Remove and delete all instances of a plane with a given ID - void RemovePlane(const string& ID); - - // Figure out if a given position lies on the active runway - // Might have to change when we consider more than one active rwy. - bool OnActiveRunway(const Point3D& pt); - - // Figure out if a given position lies on a runway or not - bool OnAnyRunway(const Point3D& pt, bool onGround); - - // Calculate the eta of a plane to the threshold. - // For ground traffic this is the fastest they can get there. - // For air traffic this is the middle approximation. - void CalcETA(TowerPlaneRec* tpr, bool printout = false); - - // Iterate through all the lists and call CalcETA for all the planes. - void doThresholdETACalc(); - - // Order the list of traffic as per expected threshold use and flag any conflicts - bool doThresholdUseOrder(); - - // Calculate the crow-flys distance of a plane to the threshold in meters - double CalcDistOutM(TowerPlaneRec* tpr); - - // Calculate the crow-flys distance of a plane to the threshold in miles - double CalcDistOutMiles(TowerPlaneRec* tpr); - - /* - void doCommunication(); - */ - - void IssueLandingClearance(TowerPlaneRec* tpr); - void IssueGoAround(TowerPlaneRec* tpr); - void IssueDepartureClearance(TowerPlaneRec* tpr); - - unsigned int update_count; // Convienince counter for speading computational load over several updates - unsigned int update_count_max; // ditto. - - double timeSinceLastDeparture; // Time in seconds since last departure from active rwy. - bool departed; // set true when the above needs incrementing with time, false when it doesn't. - - // environment - need to make sure we're getting the surface winds and not winds aloft. - SGPropertyNode_ptr wind_from_hdg; //degrees - SGPropertyNode_ptr wind_speed_knots; //knots - - double aptElev; // Airport elevation - string activeRwy; // Active runway number - For now we'll disregard multiple / alternate runway operation. - RunwayDetails rwy; // Assumed to be the active one for now. - bool rwyOccupied; // Active runway occupied flag. For now we'll disregard land-and-hold-short operations. - FGATCAlignedProjection ortho; // Orthogonal mapping of the local area with the active runway threshold at the origin - FGATCAlignedProjection ortho_temp; // Ortho for any runway (needed to get plane position in airport) - - // Figure out which runways are active. - // For now we'll just be simple and do one active runway - eventually this will get much more complex - // This is a private function - public interface to the results of this is through GetActiveRunway - void DoRwyDetails(); - - // Need a data structure to hold details of the various active planes - // or possibly another data structure with the positions of the inactive planes. - // Need a data structure to hold outstanding communications from aircraft. - - // Linked-list of planes on approach to active rwy ordered with nearest first (timewise). - // Includes planes that have landed but not yet vacated runway. - // Somewhat analagous to the paper strips used (used to be used?) in real life. - // Doesn't include planes in circuit until they turn onto base/final? - // TODO - may need to alter this for operation to more than one active rwy. - tower_plane_rec_list_type appList; - tower_plane_rec_list_iterator appListItr; - - // What should we do with planes approaching the airport to join the circuit somewhere - // but not on straight-in though? - put them in here for now. - tower_plane_rec_list_type circuitAppList; - tower_plane_rec_list_iterator circuitAppListItr; - - // List of departed planes (planes doing circuits go into circuitList not depList after departure) - tower_plane_rec_list_type depList; - tower_plane_rec_list_iterator depListItr; - - // List of planes in the circuit (ordered by nearest to landing first) - tower_plane_rec_list_type circuitList; - tower_plane_rec_list_iterator circuitListItr; - - // List of planes waiting to depart - tower_plane_rec_list_type holdList; - tower_plane_rec_list_iterator holdListItr; - - // List of planes on rwy - tower_plane_rec_list_type rwyList; - tower_plane_rec_list_iterator rwyListItr; - - // List of all planes due to use a given rwy arranged in projected order of rwy use - tower_plane_rec_list_type trafficList; // TODO - needs to be expandable to more than one rwy - tower_plane_rec_list_iterator trafficListItr; - - // List of planes that have vacated the runway inbound but not yet handed off to ground - tower_plane_rec_list_type vacatedList; - tower_plane_rec_list_iterator vacatedListItr; - - // Returns true if successful - bool RemoveFromTrafficList(const string& id); - bool RemoveFromAppList(const string& id); - bool RemoveFromRwyList(const string& id); - - // Return the ETA of plane no. list_pos (1-based) in the traffic list. - // i.e. list_pos = 1 implies next to use runway. - double GetTrafficETA(unsigned int list_pos, bool printout = false); - - // Add a tower plane rec with ETA to the traffic list in the correct position ETA-wise. - // Returns true if this could cause a threshold ETA conflict with other traffic, false otherwise. - bool AddToTrafficList(TowerPlaneRec* t, bool holding = false); - - bool AddToCircuitList(TowerPlaneRec* t); - - // Add to vacated list only if not already present - void AddToVacatedList(TowerPlaneRec* t); - - void AddToHoldingList(TowerPlaneRec* t); - - // Ground can be separate or handled by tower in real life. - // In the program we will always use a separate FGGround class, but we need to know - // whether it is supposed to be separate or not to give the correct instructions. - bool separateGround; // true if ground control is separate - FGGround* ground; // The ground control associated with this airport. - - bool _departureControlled; // true if we need to hand off departing traffic to departure control - //FGDeparture* _departure; // The relevant departure control (once we've actually written it!) - - // for failure modeling - string trans_ident; // transmitted ident - bool tower_failed; // tower failed? - - // Pointers to current users position and orientation - SGPropertyNode_ptr user_lon_node; - SGPropertyNode_ptr user_lat_node; - SGPropertyNode_ptr user_elev_node; - SGPropertyNode_ptr user_hdg_node; - - // Details of the general traffic flow etc in the circuit - double crosswind_leg_pos; // Distance from threshold crosswind leg is being turned to in meters (actual operation - *not* ideal circuit) - double downwind_leg_pos; // Actual offset distance in meters from the runway that planes are flying the downwind leg - // Currently not sure whether the above should be always +ve or just take the natural orthopos sign (+ve for RH circuit, -ve for LH). - double base_leg_pos; // Actual offset distance from the threshold (-ve) that planes are turning to base leg. - - double nominal_crosswind_leg_pos; - double nominal_downwind_leg_pos; - double nominal_base_leg_pos; - - friend istream& operator>> ( istream&, FGTower& ); -}; - -#endif //_FG_TOWER_HXX diff --git a/src/ATC/transmission.cxx b/src/ATC/transmission.cxx deleted file mode 100644 index 0a6ea4b76..000000000 --- a/src/ATC/transmission.cxx +++ /dev/null @@ -1,100 +0,0 @@ -// FGTransmission - a class to provide transmission control at larger airports. -// -// Written by Alexander Kappes, started March 2002. -// Based on ground.cxx by David Luff, started March 2002. -// -// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk -// -// 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. - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include "transmission.hxx" - -#include - - -//Constructor -FGTransmission::FGTransmission(){ -} - -//Destructor -FGTransmission::~FGTransmission(){ -} - -void FGTransmission::Init() { -} - -// ============================================================================ -// extract parameters from transmission -// ============================================================================ -TransPar FGTransmission::Parse() { - TransPar tpar; - string tokens[20]; - int msglen,toklen; - //char dum; - int i,j,k; - const char *capl = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - msglen = strlen( TransText.c_str() ); - - int tkn = 0; - for ( i=0; i < msglen; ++i ) { - if ( TransText.c_str()[i] != ' ' ) { - if ( TransText.c_str()[i] != ',' ) tokens[tkn] += TransText.c_str()[i]; - } else if ( !tokens[tkn].empty() ) { - if ( tkn <= 20 ) { - tkn += 1; - } else { - cout << "Too many tokens" << endl; - } - } - } - - for ( i=0; i<20; ++i) { - - if ( tokens[i] == "request" ) { - tpar.request = true; - } else if ( tokens[i] == "approach" ) { - tpar.station = "approach"; - tpar.airport = tokens[i-1]; - } else if ( tokens[i] == "landing" ) { - tpar.intention = "landing"; - for ( j=i+1; j<=i+2; ++j ) { - if ( !tokens[j].empty() ) { - toklen = strlen( tokens[j].c_str() ); - bool aid = true; - for ( k=0; k - -#include -#include -#include -#include -#include -#include - -#include
- -#ifdef SG_HAVE_STD_INCLUDES -# include -#include -#elif defined( SG_HAVE_NATIVE_SGI_COMPILERS ) -# include -#elif defined( __BORLANDC__ ) -# include -#else -# include -#include -#endif - -#include "ATC.hxx" - -#if ! defined( SG_HAVE_NATIVE_SGI_COMPILERS ) -SG_USING_STD(istream); -#endif - -SG_USING_STD(string); - -struct TransCode { - int c1; - int c2; - int c3; -}; - -// TransPar - a representation of the logic of a parsed speech transmission -struct TransPar { - string station; - string callsign; - string airport; - string intention; // landing, crossing - string intid; // (airport) ID for intention - bool request; // is the transmission a request or an answer? - int tdir; // turning direction: 1=left, 2=right - double heading; - int VDir; // vertical direction: 1=descent, 2=maintain, 3=climb - double alt; - double miles; - string runway; - double freq; - double time; -}; - -// FGTransmission - a class to encapsulate a speech transmission -class FGTransmission { - - //int StationType; // Type of ATC station: 1 Approach - atc_type StationType; - TransCode Code; // DCL - no idea what this is. - string TransText; // The text of the spoken transmission - string MenuText; // An abbreviated version of the text for the menu entry - -public: - - FGTransmission(void); - ~FGTransmission(void); - - void Init(); - - inline atc_type get_station() const { return StationType; } - inline const TransCode& get_code() { return Code; } - inline const string& get_transtext() { return TransText; } - inline const string& get_menutext() { return MenuText; } - - // Return the parsed logic of the transmission - TransPar Parse(); - -private: - - friend istream& operator>> ( istream&, FGTransmission& ); - -}; - - -inline istream& -operator >> ( istream& in, FGTransmission& a ) { - char ch; - int tmp; - - static bool first_time = true; - static double julian_date = 0; - static const double MJD0 = 2415020.0; - if ( first_time ) { - julian_date = sgTimeCurrentMJD(0, 0) + MJD0; - first_time = false; - } - // Ugly hack alert - eventually we'll use xml format for the transmissions file - in >> tmp; - if(tmp == 1) { - a.StationType = APPROACH; - } else { - a.StationType = INVALID; - } - in >> a.Code.c1; - in >> a.Code.c2; - in >> a.Code.c3; - a.TransText = ""; - in >> ch; - if ( ch != '"' ) a.TransText += ch; - while(1) { - //in >> noskipws - in.unsetf(ios::skipws); - in >> ch; - if ( ch != '"' ) a.TransText += ch; - if((ch == '"') || (ch == 0x0A)) { - break; - } // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the " - } - in.setf(ios::skipws); - - a.MenuText = ""; - in >> ch; - if ( ch != '"' ) a.MenuText += ch; - while(1) { - //in >> noskipws - in.unsetf(ios::skipws); - in >> ch; - if ( ch != '"' ) a.MenuText += ch; - if((ch == '"') || (ch == 0x0A)) { - break; - } // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the " - } - in.setf(ios::skipws); - - //cout << "Code = " << a.Code << " Transmission text = " << a.TransText - // << " Menu text = " << a.MenuText << endl; - - return in >> skipeol; -} - - -#endif // _FG_TRANSMISSION_HXX diff --git a/src/ATC/transmissionlist.cxx b/src/ATC/transmissionlist.cxx deleted file mode 100644 index cb24e50ca..000000000 --- a/src/ATC/transmissionlist.cxx +++ /dev/null @@ -1,263 +0,0 @@ -// transmissionlist.cxx -- transmission management class -// -// Written by Alexander Kappes, started March 2002. -// Based on navlist.cxx by Curtis Olson, started April 2000. -// -// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// $Id$ - - -#ifdef HAVE_CONFIG_H -# include -#endif - -#ifdef HAVE_STRINGS_H -# include // bcopy() -#else -# include // MSVC doesn't have strings.h -#endif - - -#include -#include -#include - -#include "transmissionlist.hxx" - -#include - - -FGTransmissionList *current_transmissionlist; - - -FGTransmissionList::FGTransmissionList( void ) { -} - - -FGTransmissionList::~FGTransmissionList( void ) { -} - - -// load default.transmissions -bool FGTransmissionList::init( const SGPath& path ) { - FGTransmission a; - - transmissionlist_station.erase( transmissionlist_station.begin(), transmissionlist_station.end() ); - - sg_gzifstream in( path.str() ); - if ( !in.is_open() ) { - SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() ); - exit(-1); - } - - // read in each line of the file - - // in >> skipeol; - // in >> skipcomment; - -#ifdef __MWERKS__ - - char c = 0; - while ( in.get(c) && c != '\0' ) { - in.putback(c); - in >> a; - if ( a.get_type() != '[' ) { - transmissionlist_code[a.get_station()].push_back(a); - } - in >> skipcomment; - } - -#else - - double min = 100000; - double max = 0; - - while ( ! in.eof() ) { - in >> a; - transmissionlist_station[a.get_station()].push_back(a); - - in >> skipcomment; - - if ( a.get_station() < min ) { - min = a.get_station(); - } - if ( a.get_station() > max ) { - max = a.get_station(); - } - - /* - cout << a.get_station() << " " << a.get_code().c1 << " " << a.get_code().c2 << " " - << a.get_code().c3 << " " << a.get_transtext() - << " " << a.get_menutext() << endl; - */ - } - -#endif - - // init ATC menu - fgSetBool("/sim/atc/menu",false); - - return true; -} - -// query the database for the specified station type; -// for station see FlightGear/ATC/default.transmissions -bool FGTransmissionList::query_station( const atc_type &station, FGTransmission *t, - int max_trans, int &num_trans ) -{ - transmission_list_type tmissions = transmissionlist_station[station]; - transmission_list_iterator current = tmissions.begin(); - transmission_list_iterator last = tmissions.end(); - - for ( ; current != last ; ++current ) { - if (num_trans < max_trans) { - t[num_trans] = *current; - num_trans += 1; - } - else { - cout << "Transmissionlist error: Too many transmissions" << endl; - } - } - - if ( num_trans != 0 ) return true; - else { - cout << "No transmission with station " << station << "found." << endl; - string empty; - return false; - } -} - -string FGTransmissionList::gen_text(const atc_type &station, const TransCode code, - const TransPar &tpars, const bool ttext ) -{ - const int cmax = 300; - string message; - char tag[4]; - char crej = '@'; - char mes[cmax]; - char dum[cmax]; - //char buf[10]; - char *pos; - int len; - FGTransmission t; - - // if (current_transmissionlist->query_station( station, &t ) ) { - transmission_list_type tmissions = transmissionlist_station[station]; - transmission_list_iterator current = tmissions.begin(); - transmission_list_iterator last = tmissions.end(); - - for ( ; current != last ; ++current ) { - if ( current->get_code().c1 == code.c1 && - current->get_code().c2 == code.c2 && - current->get_code().c3 == code.c3 ) { - - if ( ttext ) message = current->get_transtext(); - else message = current->get_menutext(); - strcpy( &mes[0], message.c_str() ); - - // Replace all the '@' parameters with the actual text. - int check = 0; // If mes gets overflowed the while loop can go infinite - while ( strchr(&mes[0], crej) != NULL ) { // ie. loop until no more occurances of crej ('@') found - pos = strchr( &mes[0], crej ); - memmove(&tag[0], pos, 3); - tag[3] = '\0'; - int i; - len = 0; - for ( i=0; i 10) { - SG_LOG(SG_ATC, SG_WARN, "WARNING: Possibly endless loop terminated in FGTransmissionlist::gen_text(...)"); - break; - } - } - - //cout << mes << endl; - break; - } - } - return mes[0] ? mes : "No transmission found"; -} - - diff --git a/src/ATC/transmissionlist.hxx b/src/ATC/transmissionlist.hxx deleted file mode 100644 index ae6be8a8b..000000000 --- a/src/ATC/transmissionlist.hxx +++ /dev/null @@ -1,82 +0,0 @@ -// transmissionlist.hxx -- transmission management class -// -// Written by Alexander Kappes, started March 2002. -// Based on navlist.hxx by Curtis Olson, started April 2000. -// -// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt -// -// 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. -// - - -#ifndef _FG_TRANSMISSIONLIST_HXX -#define _FG_TRANSMISSIONLIST_HXX - - -#include -#include - -#include -#include - -#include "ATC.hxx" -#include "transmission.hxx" - -SG_USING_STD(map); -SG_USING_STD(vector); - -class FGTransmissionList { - - // convenience types - typedef vector < FGTransmission > transmission_list_type; - typedef transmission_list_type::iterator transmission_list_iterator; - typedef transmission_list_type::const_iterator transmission_list_const_iterator; - - // Map of transmission lists by station type - // typedef map < int, transmission_list_type, less > transmission_map_type; - typedef map < atc_type, transmission_list_type > transmission_map_type; - typedef transmission_map_type::iterator transmission_map_iterator; - typedef transmission_map_type::const_iterator transmission_map_const_iterator; - - transmission_map_type transmissionlist_station; - -public: - - FGTransmissionList(); - ~FGTransmissionList(); - - // load the transmission data and build the map - bool init( const SGPath& path ); - - // query the database for the specified code, - bool query_station( const atc_type &station, FGTransmission *a, int max_trans, int &num_trans ); - - // generate the transmission text given the code of the message - // and the parameters - // Set ttext = true to generate the spoken transmission text, - // or false to generate the abridged menu entry text. - string gen_text(const atc_type &station, const TransCode code, - const TransPar &tpars, const bool ttext); - -}; - - -void mkATCMenuInit (void); -void mkATCMenu (void); - -extern FGTransmissionList *current_transmissionlist; - - -#endif // _FG_TRANSMISSIONLIST_HXX diff --git a/src/Cockpit/hud_rwy.cxx b/src/Cockpit/hud_rwy.cxx index 6b40e178f..6a3a90ac4 100644 --- a/src/Cockpit/hud_rwy.cxx +++ b/src/Cockpit/hud_rwy.cxx @@ -33,7 +33,7 @@ #include #include #include SG_GLU_H -#include +#include // int x, int y, int width, int height, float scale_data, bool working) diff --git a/src/Instrumentation/HUD/HUD_runway.cxx b/src/Instrumentation/HUD/HUD_runway.cxx index 10d2fd807..c3dc5e19c 100644 --- a/src/Instrumentation/HUD/HUD_runway.cxx +++ b/src/Instrumentation/HUD/HUD_runway.cxx @@ -35,7 +35,7 @@ #include #include
#include
-#include +#include #include "HUD.hxx" diff --git a/src/Instrumentation/KLN89/kln89.cxx b/src/Instrumentation/KLN89/kln89.cxx index 2dbc71be4..ffc59c5f9 100644 --- a/src/Instrumentation/KLN89/kln89.cxx +++ b/src/Instrumentation/KLN89/kln89.cxx @@ -43,7 +43,7 @@ #include "kln89_symbols.hxx" #include -#include +#include #include
#include diff --git a/src/Instrumentation/KLN89/kln89_page_apt.cxx b/src/Instrumentation/KLN89/kln89_page_apt.cxx index 24110d945..5830bdc34 100644 --- a/src/Instrumentation/KLN89/kln89_page_apt.cxx +++ b/src/Instrumentation/KLN89/kln89_page_apt.cxx @@ -26,7 +26,7 @@ #endif #include "kln89_page_apt.hxx" -#include +#include #include
// This function is copied from Airports/runways.cxx diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 56502395b..7cb2c6fc3 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -72,8 +72,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx index 3a28be27c..e028c9417 100644 --- a/src/Main/globals.cxx +++ b/src/Main/globals.cxx @@ -34,8 +34,8 @@ #include #include -#include -#include +#include +#include #include #include #include diff --git a/src/Main/main.cxx b/src/Main/main.cxx index d0acc2776..55c5dbad5 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -68,8 +68,8 @@ #include #include #include -#include -#include +#include +#include #include