+++ /dev/null
-// 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 <config.h>
-#endif
-
-#include <Main/globals.hxx>
-#include <Scenery/scenery.hxx>
-#include <simgear/constants.h>
-#include <simgear/math/sg_geodesy.hxx>
-#include <simgear/misc/sg_path.hxx>
-#include <string>
-
-#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();
-}
+++ /dev/null
-// 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 <simgear/math/SGMath.hxx>
-#include <simgear/scene/model/placement.hxx>
-
-/*****************************************************************
-*
-* 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
-
+++ /dev/null
-// 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 <config.h>
-#endif
-
-#include <simgear/math/SGMath.hxx>
-#include <Airports/runways.hxx>
-#include <Main/globals.hxx>
-#include <string>
-#include <math.h>
-
-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::
+++ /dev/null
-// 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 <simgear/math/SGMath.hxx>
-#include <Main/fg_props.hxx>
-
-#include "AILocalTraffic.hxx"
-
-#include <string>
-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
+++ /dev/null
-// 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 <config.h>
-#endif
-
-#include <Airports/runways.hxx>
-#include <Main/globals.hxx>
-#include <Main/viewer.hxx>
-#include <Scenery/scenery.hxx>
-#include <Scenery/tilemgr.hxx>
-#include <simgear/math/SGMath.hxx>
-#include <simgear/misc/sg_path.hxx>
-#include <string>
-#include <math.h>
-
-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; i<path.size(); ++i) {
- switch(path[i]->struct_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; i<path.size(); ++i) {
- switch(path[i]->struct_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; i<exitNodes.size(); ++i) {
- cout << exitNodes[i]->nodeID << ' ';
- }
- 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; i<path.size(); ++i) {
- switch(path[i]->struct_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;
-}
-
+++ /dev/null
-// 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 <simgear/math/SGMath.hxx>
-#include <Main/fg_props.hxx>
-
-#include "AIPlane.hxx"
-#include "ATCProjection.hxx"
-#include "ground.hxx"
-
-class FGGround;
-class FGTower;
-struct Gate;
-
-#include <string>
-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
+++ /dev/null
-// 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 <config.h>
-#endif
-
-#include <iostream>
-
-#include <Main/fg_props.hxx>
-#include <Main/globals.hxx>
-#include <simgear/misc/sg_path.hxx>
-#include <simgear/math/sg_random.h>
-#include <simgear/scene/model/modellib.hxx>
-#include <list>
-
-#ifdef _MSC_VER
-# include <io.h>
-#else
-# include <sys/types.h> // for directory reading
-# include <dirent.h> // for directory reading
-#endif
-
-#include <Environment/environment_mgr.hxx>
-#include <Environment/environment.hxx>
-
-#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; i<j; ++i) {
- int n = int(sg_random() * 10);
- if(n == 10) --n;
- s += (char)('0' + n);
- }
- for(int i=j; i<5; ++i) {
- int n = int(sg_random() * 26);
- if(n == 26) --n;
- //cout << "Alpha, n = " << n << '\n';
- s += (char)('A' + n);
- }
- //cout << "s = " << s << '\n';
- return(s);
-}
-
-string FGAIMgr::GenerateUniqueCallsign() {
- while(1) {
- string s = GenerateCallsign();
- if(!ai_callsigns_used[s]) {
- ai_callsigns_used[s] = 1;
- return(s);
- }
- }
-}
-
-// This will be moved somewhere else eventually!!!!
-string FGAIMgr::GenerateShortForm(const string& callsign, const string& plane_str, bool local) {
- //cout << callsign << '\n';
- string s;
- if(local) s = "Trainer-";
- else s = plane_str;
- for(int i=3; i>0; --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);
-}
+++ /dev/null
-// 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 <simgear/structure/subsystem_mgr.hxx>
-
-#include <Main/fg_props.hxx>
-
-#include <list>
-
-#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 <FGAIEntity*> 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<osg::Node> _defaultModel; // Cessna 172!
- osg::ref_ptr<osg::Node> _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
+++ /dev/null
-// 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 <Main/globals.hxx>
-#include <Main/fg_props.hxx>
-#include <simgear/debug/logstream.hxx>
-#include <simgear/sound/soundmgr_openal.hxx>
-#include <math.h>
-#include <string>
-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 << ");
-}
-
+++ /dev/null
-// 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<SGSampleGroup> _sgr;
-};
-
-#endif // _FG_AI_PLANE_HXX
-
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) {
// 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);
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
#include "ground.hxx"
#include "ATCutils.hxx"
-#include "AILocalTraffic.hxx"
#include "ATCmgr.hxx"
using std::ifstream;
}
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);
// 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
-
#include <string>
-class FGAIEntity;
class FGATCMgr;
//////////////////////////////////////////////////////
// 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;
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();
#include <Airports/runways.hxx>
#include "tower.hxx"
+#include "ground.hxx"
#include "ATCmgr.hxx"
#include "ATCutils.hxx"
#include "ATCDialog.hxx"
#include "commlist.hxx"
-#include "AILocalTraffic.hxx"
using std::cout;
// TowerPlaneRec
TowerPlaneRec::TowerPlaneRec() :
- planePtr(NULL),
eta(0),
dist_out(0),
clearedToLand(false),
}
TowerPlaneRec::TowerPlaneRec(const PlaneRec& p) :
- planePtr(NULL),
eta(0),
dist_out(0),
clearedToLand(false),
}
TowerPlaneRec::TowerPlaneRec(const SGGeod& pt) :
- planePtr(NULL),
eta(0),
dist_out(0),
clearedToLand(false),
}
TowerPlaneRec::TowerPlaneRec(const PlaneRec& p, const SGGeod& pt) :
- planePtr(NULL),
eta(0),
dist_out(0),
clearedToLand(false),
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();
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.
// 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);
// 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
t->opType = CIRCUIT;
if(t->isUser) {
current_atcdialog->add_entry(ident, "@AP Tower, @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND);
- } else {
- t->planePtr->RegisterTransmission(15);
}
t->clearedToLand = false;
}
if((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) {
PatternLeg leg;
if(tt->isUser) {
leg = tt->leg;
- } else {
- leg = tt->planePtr->GetLeg();
}
if(leg == FINAL) {
trns += " on final";
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) {
//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";
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 {
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;
}
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) {
t->leg = LANDING_ROLL;
}
}
- } else {
- t->leg = t->planePtr->GetLeg();
}
// Set the constraints IF this is the first plane in the circuit
// 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
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";
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) {
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';
// 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).
}
Transmit();
//if(t->isUser) cout << "Transmitting cleared to Land!!!\n";
t->clearedToLand = true;
- if(!t->isUser) {
- t->planePtr->RegisterTransmission(7);
- }
}
} else {
//if(t->isUser) cout << "Not next\n";
// Check for landing...
bool landed = false;
- if(!t->isUser) {
- if(t->planePtr) {
- if(t->planePtr->GetLeg() == LANDING_ROLL) {
- landed = true;
- }
- } else {
- SG_LOG(SG_ATC, SG_ALERT, "WARNING - not user and null planePtr in CheckApproachList!");
- }
- } else { // user
+ if(t->isUser) {
if(OnActiveRunway(t->pos)) {
landed = true;
}
//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)) {
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 {
// 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;
//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"
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";
}
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);
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);
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 << ");
+}
+
#include "ATC.hxx"
#include "ATCProjection.hxx"
-#include "AIPlane.hxx"
class FGATCMgr;
class FGGround;
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,
TowerPlaneRec(const SGGeod& pt);
TowerPlaneRec(const PlaneRec& p, const SGGeod& pt);
- FGAIPlane* planePtr; // This might move to the planeRec eventually
PlaneRec plane;
SGGeod pos;
// 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);
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; }
#if ENABLE_ATCDCL
# include <ATCDCL/ATCmgr.hxx>
-# include <ATCDCL/AIMgr.hxx>
# include "ATCDCL/commlist.hxx"
#else
# include "ATC/atis.hxx"
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
#include <Aircraft/controls.hxx>
#include <Airports/runways.hxx>
-#include <ATCDCL/AIMgr.hxx>
#include <ATCDCL/ATCmgr.hxx>
#include <Autopilot/route_mgr.hxx>
#include <Cockpit/panel.hxx>
route_mgr( NULL ),
current_panel( NULL ),
ATC_mgr( NULL ),
- AI_mgr( NULL ),
controls( NULL ),
viewmgr( NULL ),
commands( SGCommandMgr::instance() ),
delete current_panel;
delete ATC_mgr;
- delete AI_mgr;
delete controls;
delete viewmgr;
class SGSubsystem;
class SGSoundMgr;
-class FGAIMgr;
class FGATCMgr;
class FGAircraftModel;
class FGControls;
// ATC manager
FGATCMgr *ATC_mgr;
- // AI manager
- FGAIMgr *AI_mgr;
-
// control input state
FGControls *controls;
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; }
#include <Sound/morse.hxx>
#include <Sound/fg_fx.hxx>
#include <ATCDCL/ATCmgr.hxx>
-#include <ATCDCL/AIMgr.hxx>
#include <Time/tmp.hxx>
#include <Environment/environment_mgr.hxx>
#include <Environment/ephemeris.hxx>
// Run ATC subsystem
if (fgGetBool("/sim/atc/enabled"))
globals->get_ATC_mgr()->update(delta_time_sec);
-
-
- // Run the AI subsystem
- // NOTE: the AI_mgr has nothing to do with the AIModels subsystem.
- // There was previously a comment here stating that the
- // AI_mgr code should be run then multiplayer is enabled.
- // Multiplayer relies on AIModels, and not on the old and
- // depricated AI_mgr system. So, we can safely skip the following
- // two lines at compile time when compiling with --disable-atcdcl
- if (fgGetBool("/sim/ai-traffic/enabled"))
- globals->get_AI_mgr()->update(delta_time_sec);
#endif
globals->get_subsystem_mgr()->update(delta_time_sec);