From: David Luff Date: Mon, 2 Aug 2010 23:21:56 +0000 (+0100) Subject: Remove old AI system X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=53358a55bf8663c94a88abef2e72d330e28e914a;p=flightgear.git Remove old AI system --- diff --git a/src/ATCDCL/AIEntity.cxx b/src/ATCDCL/AIEntity.cxx deleted file mode 100644 index 0d3d764fd..000000000 --- a/src/ATCDCL/AIEntity.cxx +++ /dev/null @@ -1,74 +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 "AIEntity.hxx" - -FGAIEntity::FGAIEntity() : - _ground_elevation_m(0) -{ -} - -FGAIEntity::~FGAIEntity() { - globals->get_scenery()->get_scene_graph()->removeChild(_aip.getSceneGraph()); -} - -void FGAIEntity::SetModel(osg::Node* model) { - _aip.init(model); - _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); - _aip.setOrientation(_roll, _pitch, _hdg); - _aip.update(); -} diff --git a/src/ATCDCL/AIEntity.hxx b/src/ATCDCL/AIEntity.hxx deleted file mode 100644 index 5254c8099..000000000 --- a/src/ATCDCL/AIEntity.hxx +++ /dev/null @@ -1,71 +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; - - const SGGeod& getPos() const - { return _pos; } - - virtual const string& GetCallsign()=0; - -protected: - - SGGeod _pos; // Geodetic position - double _hdg; //True heading in degrees - double _roll; //degrees - double _pitch; //degrees - - SGModelPlacement _aip; - double _ground_elevation_m; - - void Transform(); -}; - -#endif // _FG_AIEntity_HXX - diff --git a/src/ATCDCL/AIGAVFRTraffic.cxx b/src/ATCDCL/AIGAVFRTraffic.cxx deleted file mode 100644 index 93a1f80cb..000000000 --- a/src/ATCDCL/AIGAVFRTraffic.cxx +++ /dev/null @@ -1,465 +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 - -using std::string; - -#include "ATC.hxx" -#include "ATCmgr.hxx" -#include "AILocalTraffic.hxx" -#include "AIGAVFRTraffic.hxx" -#include "ATCutils.hxx" -#include "tower.hxx" - -// extern from Airports/simple.cxx -extern SGGeod fgGetAirportPos( const std::string& id ); - -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 SGGeod& 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.getElevationM() + 2500.0) * SG_FEET_TO_METER; // TODO look at terrain elevation as well - _pos.setElevationM(_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, SGGeod::fromDegM(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.getElevationM() >= _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.getElevationM() - _pos.getElevationM()) / dclGetHorizontalSeparation(_wp, _pos)) * DCL_RADIANS_TO_DEGREES; - double thesh_offset = 0.0; - SGVec3d opos = ortho.ConvertToLocal(_pos); - double angToApt = atan((_pos.getElevationM() - 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.getElevationM() - _pos.getElevationM()) / 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.getElevationM() < (fgGetAirportElev(airportID) + (1000.0 * SG_FEET_TO_METER))) slope = 0.0; - } - } - if(_incoming) { - //cout << "i" << '\n'; - SGVec3d orthopos = ortho.ConvertToLocal(_pos); - // TODO - Check whether to start descent - // become _local after the 3 mile report. - if(_pos.getElevationM() < (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.getElevationM() - fgGetAirportElev(airportID) << '\n'; - if(_established && (orthopos.y() > -5400.0)) { - slope = atan((_pos.getElevationM() - 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(GetPos()); - //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 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. -SGGeod FGAIGAVFRTraffic::GetPatternApproachPos() { - //cout << "\n\n"; - //cout << "Calculating pattern approach pos for " << plane.callsign << '\n'; - SGVec3d orthopos = ortho.ConvertToLocal(_pos); - SGVec3d 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.x() = 2000 * patternDirection; - tmp.y() = (rwy.end2ortho.y() / 2.0) + 2000; - tmp.z() = (fgGetAirportElev(airportID) + (1000 * SG_FEET_TO_METER)); - _e45 = true; - //cout << "45 deg entry... "; - } else { - tmp.x() = (1000 * patternDirection * -1); - tmp.y() = (rwy.end2ortho.y()); - tmp.z() = (fgGetAirportElev(airportID) + (1000 * SG_FEET_TO_METER)); - _e45 = false; - //cout << "90 deg entry... "; - } - } else { - tmp.x() = 0; - tmp.y() = -5400; - tmp.z() = ((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/ATCDCL/AIGAVFRTraffic.hxx b/src/ATCDCL/AIGAVFRTraffic.hxx deleted file mode 100644 index fda97cf39..000000000 --- a/src/ATCDCL/AIGAVFRTraffic.hxx +++ /dev/null @@ -1,124 +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 -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 SGGeod& 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; - SGGeod _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; - SGGeod _destPos; - bool _local; - bool _incoming; - bool _established; - bool _e45; - bool _entering; - bool _turning; - - int GetQuadrangleAltitude(int dir, int des_alt); - - SGGeod 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/ATCDCL/AILocalTraffic.cxx b/src/ATCDCL/AILocalTraffic.cxx deleted file mode 100644 index 4c3e67c57..000000000 --- a/src/ATCDCL/AILocalTraffic.cxx +++ /dev/null @@ -1,1549 +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 - -using std::string; - -#include "ATCmgr.hxx" -#include "AILocalTraffic.hxx" -#include "ATCutils.hxx" -#include "AIMgr.hxx" - -FGAILocalTraffic::FGAILocalTraffic() { - 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; - - ground = NULL; - tower = NULL; - ourGate = NULL; - nextTaxiNode = NULL; - holdShortNode = NULL; -} - -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; - - const FGAirport* apt = fgFindAirportID(id); - assert(apt); - FGRunway* runway(apt->getActiveRunwayForUsage()); - - double hdg = runway->headingDeg(); - 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'; - double tshlon = 0.0, tshlat = 0.0, tshr; - double tolon = 0.0, tolat = 0.0, tor; - rwy.length = runway->lengthM(); - rwy.width = runway->widthM(); - geo_direct_wgs_84 ( aptElev, runway->latitude(), runway->longitude(), other_way, - rwy.length / 2.0 - 25.0, &tshlat, &tshlon, &tshr ); - geo_direct_wgs_84 ( aptElev, runway->latitude(), runway->longitude(), 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 = SGGeod::fromDegM(tshlon, tshlat, aptElev); - SGGeod takeoff_end = SGGeod::fromDegM(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); -} - - -/* -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; - SGVec3d 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.setElevationM(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 = SGVec3d((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.setElevationM(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(SGVec3d(1000*patternDirection, 800, 0.0)); - _pos.setElevationM(rwy.threshold_pos.getElevationM() + 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 = rwy.threshold_pos; - _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, SGGeod::fromDegM(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 AWOS: 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(_ground_elevation_m > -9990.0) { - _pos.setElevationM(_ground_elevation_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(_ground_elevation_m > -9990.0) { - _pos.setElevationM(_ground_elevation_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(SGVec3d(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(_ground_elevation_m > -9990.0) { - _pos.setElevationM(_ground_elevation_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; - SGVec3d orthopos = ortho.ConvertToLocal(_pos); // ortho position of the plane - //cout << "runway elev = " << rwy.threshold_pos.getElevationM() << ' ' << rwy.threshold_pos.getElevationM() * 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(_ground_elevation_m > -9990.0) { - _pos.setElevationM(_ground_elevation_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.getElevationM() - rwy.threshold_pos.getElevationM()) * SG_METER_TO_FEET > 700) { - double cc = 0.0; - if(_controlled && 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.getElevationM() - rwy.threshold_pos.getElevationM()) * 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.getElevationM() - rwy.threshold_pos.getElevationM()) * 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(_controlled && 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.getElevationM() - rwy.threshold_pos.getElevationM()) * 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.getElevationM() - rwy.threshold_pos.getElevationM()) * SG_METER_TO_FEET > 995) && ((_pos.getElevationM() - rwy.threshold_pos.getElevationM()) * SG_METER_TO_FEET < 1015)) { - slope = 0.0; - _pitch = 0.0; - IAS = 90.0; // FIXME - use smooth transistion to new speed - } - if((_pos.getElevationM() - rwy.threshold_pos.getElevationM()) * 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(((_controlled && tower->GetBaseConstraint(d1)) ? d1 : -1000.0), ((_controlled && 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(_controlled && 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(((_controlled && 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.getElevationM() - 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.getElevationM() < (rwy.threshold_pos.getElevationM()+10.0+wheelOffset)) { - if(_ground_elevation_m > -9990.0) { - if(_pos.getElevationM() < (_ground_elevation_m + wheelOffset + 1.0)) { - slope = -2.0; - _pitch = 1.0; - IAS = 55.0; - } else if(_pos.getElevationM() < (_ground_elevation_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.getElevationM() < (rwy.threshold_pos.getElevationM()+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.getElevationM() < (rwy.threshold_pos.getElevationM()+10.0+wheelOffset)) { - //slope = -1.0; - //_pitch = 1.0; - if(_ground_elevation_m > -9990.0) { - if((_ground_elevation_m + wheelOffset) > _pos.getElevationM()) { - 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(_ground_elevation_m > -9990.0) { - _pos.setElevationM(_ground_elevation_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) { - // calculate ground speed and crab from the wind triangle - double wind_angle = GetAngleDiff_deg(wind_from + 180, track); - double wind_side = (wind_angle < 0) ? -1.0 : 1.0; - - double sine_of_crab = wind_speed / IAS * sin(fabs(wind_angle) * DCL_DEGREES_TO_RADIANS); - if (sine_of_crab >= 1.0) { - // The crosswind component is greater than the IAS, - // we can't keep the aircraft on track. - // Assume increased IAS such that it cancels lateral speed. - // This is unrealistic, but not sure how the rest of the sim - // would react to the aircraft going off course. - // Should be a rare case anyway. - crab = wind_side * 90.0; - } else { - crab = asin(sine_of_crab) * DCL_RADIANS_TO_DEGREES * wind_side; - } - vel = cos(wind_angle * DCL_DEGREES_TO_RADIANS) * wind_speed - + cos(crab * DCL_DEGREES_TO_RADIANS) * IAS; - } 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; - dclBoundHeading(_hdg); - 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; - const string& apt_name = _controlled ? tower->get_name() : airportID; - - trns += apt_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 += apt_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) { - if (_controlled) tower->ContactAtHoldShort(plane, this, CIRCUIT); - } else if(code == 3) { - if (_controlled) tower->ReportRunwayVacated(plane.callsign); - } else if(code == 11) { - if (_controlled) tower->ReportDownwind(plane.callsign); - } else if(code == 13) { - if (_controlled) 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 SGVec3d& 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!! - - 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(_ground_elevation_m > -9990) { - _pos.setElevationM(_ground_elevation_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(_ground_elevation_m > -9990) { - _pos.setElevationM(_ground_elevation_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() { - // 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, SGGeod::fromGeodM(vw->getPosition(), 0.0)) > visibility_meters) { - _ground_elevation_m = aptElev; - return; - } - - // FIXME: make shure the pos.lat/pos.lon values are in degrees ... - double range = 500.0; - if (!globals->get_tile_mgr()->scenery_available(_aip.getPosition(), range)) { - // Try to shedule tiles for that position. - globals->get_tile_mgr()->update( _aip.getPosition(), range ); - } - - // FIXME: make shure the pos.lat/pos.lon values are in degrees ... - double alt; - if (globals->get_scenery()->get_elevation_m(SGGeod::fromGeodM(_aip.getPosition(), 20000), alt, 0, _aip.getSceneGraph())) - _ground_elevation_m = alt; -} - diff --git a/src/ATCDCL/AILocalTraffic.hxx b/src/ATCDCL/AILocalTraffic.hxx deleted file mode 100644 index c62a9456e..000000000 --- a/src/ATCDCL/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 -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 SGVec3d& 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/ATCDCL/AIMgr.cxx b/src/ATCDCL/AIMgr.cxx deleted file mode 100644 index 8b6c448bc..000000000 --- a/src/ATCDCL/AIMgr.cxx +++ /dev/null @@ -1,604 +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 -#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" - -using std::list; -using std::cout; - -using namespace simgear; - -// extern from Airports/simple.cxx -extern SGGeod fgGetAirportPos( const std::string& id ); - -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); - - // 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; -} - -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'; - - SGGeod userPos = SGGeod::fromDegM(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; - } - */ - - SGGeod aptpos = fgGetAirportPos(ident); // TODO - check for elev of -9999 - //cout << "ident = " << ident << ", elev = " << aptpos.getElevationM() << '\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.getElevationM() > 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.getLongitudeDeg() / (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.getLatitudeDeg(), aptpos.getLongitudeDeg(), aptpos.getElevationM()); // 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. - SGGeod userpos = SGGeod::fromDegM(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"; - SGGeod tpos = dclUpdatePosition(aptpos, dir, 6.0, ad); - if(tpos.getElevationM() > (aptpos.getElevationM() + 3000.0)) tpos.setElevationM(aptpos.getElevationM() + 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"; - SGGeod tpos = dclUpdatePosition(aptpos, dir, 6.0, ad); - if(tpos.getElevationM() > (aptpos.getElevationM() + 3000.0)) tpos.setElevationM(aptpos.getElevationM() + 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 - _userAircraftPos = SGGeod::fromDegFt(lon_node->getDoubleValue(), - lat_node->getDoubleValue(), elev_node->getDoubleValue()); - SGBucket buck(_userAircraftPos); - - // 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(_userAircraftPos.getLongitudeDeg(), _userAircraftPos.getLatitudeDeg(), 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; - 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(_userAircraftPos, 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(_userAircraftPos, 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 += GetPhoneticLetter(c); - else s += ConvertNumToSpokenDigits(tmp); - if(i > 1) s += '-'; - } - return(s); -} diff --git a/src/ATCDCL/AIMgr.hxx b/src/ATCDCL/AIMgr.hxx deleted file mode 100644 index 8d87c5c98..000000000 --- a/src/ATCDCL/AIMgr.hxx +++ /dev/null @@ -1,136 +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" - -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; - - SGGeod _userAircraftPos; - - // 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/ATCDCL/AIPlane.cxx b/src/ATCDCL/AIPlane.cxx deleted file mode 100644 index 8fd67d13a..000000000 --- a/src/ATCDCL/AIPlane.cxx +++ /dev/null @@ -1,275 +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 -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; - - if ( !_sgr ) { - SGSoundMgr *smgr = globals->get_soundmgr(); - _sgr = smgr->find("atc", true); - _sgr->tie_to_listener(); - } -} - -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 - // TODO - at the moment the volume is always set off comm1 - float volume = fgGetFloat("/instrumentation/comm[0]/volume"); - Render(plane.callsign, volume, 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) { - // TODO - at the moment the volume is always set off comm1 - float volume = fgGetFloat("/instrumentation/comm[0]/volume"); - - Render(plane.callsign, volume, 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, const float volume, bool repeating) { - fgSetString("/sim/messages/ai-plane", pending_transmission.c_str()); -#ifdef ENABLE_AUDIO_SUPPORT - voice = (voiceOK && fgGetBool("/sim/sound/voice")); - if(voice) { - size_t len; - void* buf = vPtr->WriteMessage(pending_transmission, &len); - if(voice && (volume > 0.05)) { - SGSoundSample* simple = new SGSoundSample(&buf, len, 8000 ); - simple->set_volume(volume); - _sgr->add(simple, refname); - _sgr->play(refname, repeating); - } - } -#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 - _sgr->stop(refname); - _sgr->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/ATCDCL/AIPlane.hxx b/src/ATCDCL/AIPlane.hxx deleted file mode 100644 index 53158f929..000000000 --- a/src/ATCDCL/AIPlane.hxx +++ /dev/null @@ -1,167 +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 "AIEntity.hxx" -#include "ATC.hxx" - -class SGSampleGroup; - -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, const float volume, 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 - - SGSharedPtr _sgr; -}; - -#endif // _FG_AI_PLANE_HXX - diff --git a/src/ATCDCL/ATCmgr.cxx b/src/ATCDCL/ATCmgr.cxx index 2cb352a4f..fe0375015 100644 --- a/src/ATCDCL/ATCmgr.cxx +++ b/src/ATCDCL/ATCmgr.cxx @@ -178,45 +178,6 @@ unsigned short int FGATCMgr::GetFrequency(const string& ident, const atc_type& t 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->geod = ap->geod(); - a->atis_freq = GetFrequency(ident, ATIS) - || GetFrequency(ident, AWOS); - //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) { diff --git a/src/ATCDCL/ATCmgr.hxx b/src/ATCDCL/ATCmgr.hxx index 13349490d..c21d0c5f3 100644 --- a/src/ATCDCL/ATCmgr.hxx +++ b/src/ATCDCL/ATCmgr.hxx @@ -156,9 +156,6 @@ public: // 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); diff --git a/src/ATCDCL/Makefile.am b/src/ATCDCL/Makefile.am index a7c199283..eecca04f2 100644 --- a/src/ATCDCL/Makefile.am +++ b/src/ATCDCL/Makefile.am @@ -12,11 +12,6 @@ libATCDCL_a_SOURCES = \ 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 diff --git a/src/ATCDCL/ground.cxx b/src/ATCDCL/ground.cxx index e5d9c17d5..f345852b0 100644 --- a/src/ATCDCL/ground.cxx +++ b/src/ATCDCL/ground.cxx @@ -36,7 +36,6 @@ #include "ground.hxx" #include "ATCutils.hxx" -#include "AILocalTraffic.hxx" #include "ATCmgr.hxx" using std::ifstream; @@ -290,64 +289,6 @@ void FGGround::Init() { } 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); @@ -657,68 +598,3 @@ 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 - //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/ATCDCL/ground.hxx b/src/ATCDCL/ground.hxx index 257ffdad8..345787475 100644 --- a/src/ATCDCL/ground.hxx +++ b/src/ATCDCL/ground.hxx @@ -34,7 +34,6 @@ #include -class FGAIEntity; class FGATCMgr; ////////////////////////////////////////////////////// @@ -176,9 +175,7 @@ typedef shortest_path_map_type::iterator shortest_path_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 - +struct GroundRec { PlaneRec plane; node* destination; node* last_clearance; @@ -211,19 +208,6 @@ public: void Update(double dt); inline const std::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(); diff --git a/src/ATCDCL/tower.cxx b/src/ATCDCL/tower.cxx index d44e33018..f765fa4ee 100644 --- a/src/ATCDCL/tower.cxx +++ b/src/ATCDCL/tower.cxx @@ -42,11 +42,11 @@ #include #include "tower.hxx" +#include "ground.hxx" #include "ATCmgr.hxx" #include "ATCutils.hxx" #include "ATCDialog.hxx" #include "commlist.hxx" -#include "AILocalTraffic.hxx" using std::cout; @@ -54,7 +54,6 @@ using std::cout; // TowerPlaneRec TowerPlaneRec::TowerPlaneRec() : - planePtr(NULL), eta(0), dist_out(0), clearedToLand(false), @@ -86,7 +85,6 @@ TowerPlaneRec::TowerPlaneRec() : } TowerPlaneRec::TowerPlaneRec(const PlaneRec& p) : - planePtr(NULL), eta(0), dist_out(0), clearedToLand(false), @@ -118,7 +116,6 @@ TowerPlaneRec::TowerPlaneRec(const PlaneRec& p) : } TowerPlaneRec::TowerPlaneRec(const SGGeod& pt) : - planePtr(NULL), eta(0), dist_out(0), clearedToLand(false), @@ -151,7 +148,6 @@ TowerPlaneRec::TowerPlaneRec(const SGGeod& pt) : } TowerPlaneRec::TowerPlaneRec(const PlaneRec& p, const SGGeod& pt) : - planePtr(NULL), eta(0), dist_out(0), clearedToLand(false), @@ -345,7 +341,6 @@ void FGTower::Init() { 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(); @@ -508,8 +503,6 @@ void FGTower::Respond() { 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. @@ -523,8 +516,6 @@ void FGTower::Respond() { // 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); @@ -635,8 +626,6 @@ void FGTower::Respond() { // 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 @@ -650,8 +639,6 @@ void FGTower::Respond() { 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; } @@ -705,7 +692,6 @@ void FGTower::ProcessDownwindReport(TowerPlaneRec* t) { 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) { @@ -728,8 +714,6 @@ void FGTower::ProcessDownwindReport(TowerPlaneRec* t) { PatternLeg leg; if(tt->isUser) { leg = tt->leg; - } else { - leg = tt->planePtr->GetLeg(); } if(leg == FINAL) { trns += " on final"; @@ -777,11 +761,9 @@ void FGTower::ProcessRunwayVacatedReport(TowerPlaneRec* t) { 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) { @@ -806,7 +788,6 @@ void FGTower::ClearHoldingPlane(TowerPlaneRec* t) { //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"; @@ -884,7 +865,6 @@ void FGTower::ClearHoldingPlane(TowerPlaneRec* t) { 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 { @@ -892,7 +872,6 @@ void FGTower::ClearHoldingPlane(TowerPlaneRec* t) { 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; } @@ -968,10 +947,6 @@ void FGTower::CheckCircuitList(double dt) { t->pos.setLatitudeDeg(user_lat_node->getDoubleValue()); t->pos.setElevationM(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'; } SGVec3d tortho = ortho.ConvertToLocal(t->pos); if(t->isUser) { @@ -1059,8 +1034,6 @@ void FGTower::CheckCircuitList(double dt) { t->leg = LANDING_ROLL; } } - } else { - t->leg = t->planePtr->GetLeg(); } // Set the constraints IF this is the first plane in the circuit @@ -1151,10 +1124,6 @@ void FGTower::CheckCircuitList(double dt) { // 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 @@ -1173,9 +1142,6 @@ void FGTower::CheckCircuitList(double dt) { 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"; @@ -1219,8 +1185,6 @@ void FGTower::CheckRunwayList(double dt) { t->pos.setLongitudeDeg(user_lon_node->getDoubleValue()); t->pos.setLatitudeDeg(user_lat_node->getDoubleValue()); t->pos.setElevationM(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) { @@ -1283,8 +1247,6 @@ void FGTower::CheckApproachList(double dt) { t->pos.setLongitudeDeg(user_lon_node->getDoubleValue()); t->pos.setLatitudeDeg(user_lat_node->getDoubleValue()); t->pos.setElevationM(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'; @@ -1313,12 +1275,7 @@ void FGTower::CheckApproachList(double dt) { // 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 { + if(t->isUser) { // TODO - add Go-around ack to comm options, // remove report rwy vacated. (possibly). } @@ -1358,9 +1315,6 @@ void FGTower::CheckApproachList(double dt) { 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"; @@ -1369,15 +1323,7 @@ void FGTower::CheckApproachList(double dt) { // 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(t->isUser) { if(OnActiveRunway(t->pos)) { landed = true; } @@ -1417,9 +1363,10 @@ void FGTower::CheckDepartureList(double dt) { //cout << "Dep list, checking " << t->plane.callsign; double distout; // meters - if(t->isUser) distout = dclGetHorizontalSeparation(_geod, - SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())); - else distout = dclGetHorizontalSeparation(_geod, t->planePtr->getPos()); + if(t->isUser) { + distout = dclGetHorizontalSeparation(_geod, + SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())); + } //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(SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), false)) { @@ -1437,9 +1384,6 @@ void FGTower::CheckDepartureList(double dt) { 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 { @@ -1947,16 +1891,12 @@ void FGTower::doThresholdETACalc() { // 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; @@ -2035,65 +1975,6 @@ double FGTower::GetTrafficETA(unsigned int list_pos, bool 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" @@ -2151,33 +2032,6 @@ void FGTower::VFRArrivalContact(const string& ID, const LandingType& opt) { 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"; } @@ -2388,9 +2242,6 @@ void FGTower::ReportDownwind(const string& ID) { t->pos.setLongitudeDeg(user_lon_node->getDoubleValue()); t->pos.setLatitudeDeg(user_lat_node->getDoubleValue()); t->pos.setElevationM(user_elev_node->getDoubleValue()); - } else { - // ASSERT(t->planePtr != NULL); - t->pos = t->planePtr->getPos(); } CalcETA(t); AddToCircuitList(t); @@ -2417,9 +2268,6 @@ void FGTower::ReportGoingAround(const string& ID) { t->pos.setLongitudeDeg(user_lon_node->getDoubleValue()); t->pos.setLatitudeDeg(user_lat_node->getDoubleValue()); t->pos.setElevationM(user_elev_node->getDoubleValue()); - } else { - // ASSERT(t->planePtr != NULL); - t->pos = t->planePtr->getPos(); } CalcETA(t); AddToCircuitList(t); @@ -2659,3 +2507,32 @@ ostream& operator << (ostream& os, tower_traffic_type ttt) { return(os << "ERROR - Unknown switch in tower_traffic_type operator << "); } +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/ATCDCL/tower.hxx b/src/ATCDCL/tower.hxx index a09536993..fa7115c0d 100644 --- a/src/ATCDCL/tower.hxx +++ b/src/ATCDCL/tower.hxx @@ -31,7 +31,6 @@ #include "ATC.hxx" #include "ATCProjection.hxx" -#include "AIPlane.hxx" class FGATCMgr; class FGGround; @@ -49,6 +48,32 @@ enum tower_traffic_type { ostream& operator << (ostream& os, tower_traffic_type ttt); +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); + enum tower_callback_type { USER_REQUEST_VFR_DEPARTURE = 1, USER_REQUEST_VFR_ARRIVAL = 2, @@ -74,7 +99,6 @@ public: TowerPlaneRec(const SGGeod& pt); TowerPlaneRec(const PlaneRec& p, const SGGeod& pt); - FGAIPlane* planePtr; // This might move to the planeRec eventually PlaneRec plane; SGGeod pos; @@ -136,8 +160,6 @@ public: // 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 std::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 std::string& ID); void RequestTakeOffClearance(const std::string& ID); @@ -151,16 +173,6 @@ public: void ReportDownwind(const std::string& ID); void ReportGoingAround(const std::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 std::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 std::string& GetActiveRunway() const { return activeRwy; } diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 7805ac413..4dfd58328 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -76,7 +76,6 @@ #if ENABLE_ATCDCL # include -# include # include "ATCDCL/commlist.hxx" #else # include "ATC/atis.hxx" @@ -1565,14 +1564,6 @@ bool fgInitSubsystems() { SG_LOG(SG_GENERAL, SG_INFO, " ATC Manager"); globals->set_ATC_mgr(new FGATCMgr); globals->get_ATC_mgr()->init(); - - //////////////////////////////////////////////////////////////////// - // Initialise the AI Manager - //////////////////////////////////////////////////////////////////// - - SG_LOG(SG_GENERAL, SG_INFO, " AI Manager"); - globals->set_AI_mgr(new FGAIMgr); - globals->get_AI_mgr()->init(); #else //////////////////////////////////////////////////////////////////// // Initialise the ATIS Manager diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx index c01057925..35bc8f02d 100644 --- a/src/Main/globals.cxx +++ b/src/Main/globals.cxx @@ -37,7 +37,6 @@ #include #include -#include #include #include #include @@ -86,7 +85,6 @@ FGGlobals::FGGlobals() : route_mgr( NULL ), current_panel( NULL ), ATC_mgr( NULL ), - AI_mgr( NULL ), controls( NULL ), viewmgr( NULL ), commands( SGCommandMgr::instance() ), @@ -138,7 +136,6 @@ FGGlobals::~FGGlobals() delete current_panel; delete ATC_mgr; - delete AI_mgr; delete controls; delete viewmgr; diff --git a/src/Main/globals.hxx b/src/Main/globals.hxx index 3828a9326..fccee8c51 100644 --- a/src/Main/globals.hxx +++ b/src/Main/globals.hxx @@ -55,7 +55,6 @@ class SGSubsystemMgr; class SGSubsystem; class SGSoundMgr; -class FGAIMgr; class FGATCMgr; class FGAircraftModel; class FGControls; @@ -137,9 +136,6 @@ private: // ATC manager FGATCMgr *ATC_mgr; - // AI manager - FGAIMgr *AI_mgr; - // control input state FGControls *controls; @@ -261,9 +257,6 @@ public: inline FGATCMgr *get_ATC_mgr() const { return ATC_mgr; } inline void set_ATC_mgr( FGATCMgr *a ) {ATC_mgr = a; } - inline FGAIMgr *get_AI_mgr() const { return AI_mgr; } - inline void set_AI_mgr( FGAIMgr *a ) {AI_mgr = a; } - inline FGPanel *get_current_panel() const { return current_panel; } inline void set_current_panel( FGPanel *cp ) { current_panel = cp; } diff --git a/src/Main/main.cxx b/src/Main/main.cxx index 789d80ce8..0cae08de9 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -66,7 +66,6 @@ #include #include #include -#include #include