]> git.mxchange.org Git - flightgear.git/commitdiff
Attached are a fairly extensive series of patches to the ATC
authorcurt <curt>
Wed, 3 Apr 2002 23:54:44 +0000 (23:54 +0000)
committercurt <curt>
Wed, 3 Apr 2002 23:54:44 +0000 (23:54 +0000)
system.  A chap from Germany called Alexander Kappes (cc'd) got
in touch with me a few weeks ago and has written the start of
Approach control.  At the moment tuning in to a valid approach
frequency (Dortmund or East Midlands) should result in vectors to
a spot about 3 miles from the active runway, and a telling off if you
stray too far from the correct course, in the console window.  He
seems to know what he's doing so expect this to improve rapidly!!

I've added a rudimentry AI manager and a hardwired Cessna at
KEMT on the runway - I'll remove it before the next release if I don't
have it flying by then.  There seems to be an issue with framerate
which drops alarmingly when looking at it - I've a feeling that I've
possibly created several Cessnas on top of each other, but am not
sure.

29 files changed:
src/ATC/AIEntity.cxx [new file with mode: 0644]
src/ATC/AIEntity.hxx [new file with mode: 0644]
src/ATC/AILocalTraffic.cxx [new file with mode: 0644]
src/ATC/AILocalTraffic.hxx [new file with mode: 0644]
src/ATC/AIMgr.cxx [new file with mode: 0644]
src/ATC/AIMgr.hxx [new file with mode: 0644]
src/ATC/ATC.cxx
src/ATC/ATC.hxx
src/ATC/ATCdisplay.cxx
src/ATC/ATCdisplay.hxx
src/ATC/ATCmgr.cxx
src/ATC/ATCmgr.hxx
src/ATC/ATCutils.cxx [new file with mode: 0644]
src/ATC/Makefile.am
src/ATC/approach.cxx [new file with mode: 0644]
src/ATC/approach.hxx [new file with mode: 0644]
src/ATC/approachlist.cxx [new file with mode: 0644]
src/ATC/approachlist.hxx [new file with mode: 0644]
src/ATC/atislist.cxx
src/ATC/ground.cxx [new file with mode: 0644]
src/ATC/ground.hxx [new file with mode: 0644]
src/ATC/runways.cxx [new file with mode: 0644]
src/ATC/tower.cxx [new file with mode: 0644]
src/ATC/tower.hxx [new file with mode: 0644]
src/ATC/towerlist.cxx [new file with mode: 0644]
src/ATC/towerlist.hxx [new file with mode: 0644]
src/Main/fg_init.cxx
src/Main/globals.hxx
src/Main/main.cxx

diff --git a/src/ATC/AIEntity.cxx b/src/ATC/AIEntity.cxx
new file mode 100644 (file)
index 0000000..67ba889
--- /dev/null
@@ -0,0 +1,137 @@
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+/*****************************************************************
+*
+* WARNING - Curt has some ideas about AI traffic so anything in here
+* may get rewritten or scrapped.  Contact Curt curt@flightgear.org 
+* before spending any time or effort on this code!!!
+*
+******************************************************************/
+
+#include <Main/globals.hxx>
+#include <Scenery/scenery.hxx>
+//#include <simgear/constants.h>
+#include <simgear/math/point3d.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <string>
+
+#include "AIEntity.hxx"
+
+extern ssgRoot* scene; // The global Flightgear scene graph
+
+FGAIEntity::~FGAIEntity() {
+}
+
+// Run the internal calculations
+void FGAIEntity::Update() {
+}
+
+void FGAIEntity::Transform() {
+
+    // Translate moving object w.r.t eye
+    Point3D sc = scenery.get_center();
+    //cout << "sc0 = " << sc.x() << " sc1 = " << sc.y() << " sc2 = " << sc.z() << '\n';
+    //cout << "op0 = " << obj_pos.x() << " op1 = " << obj_pos.y() << " op2 = " << obj_pos.z() << '\n';
+
+    sgCoord shippos;
+    FastWorldCoordinate(&shippos, sc);
+    position->setTransform( &shippos );
+    scene->addKid(position);
+    //cout << "Transform called\n";
+}
+
+#if 0
+// Taken from tileentry.cxx
+void FGAIEntity::WorldCoordinate(sgCoord *obj_pos, Point3D center) {
+    // setup transforms
+    Point3D geod( lon * SGD_DEGREES_TO_RADIANS,
+                  lat * SGD_DEGREES_TO_RADIANS,
+                  elev );
+       
+    Point3D world_pos = sgGeodToCart( geod );
+    Point3D offset = world_pos - center;
+       
+    sgMat4 POS;
+    sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
+
+    sgVec3 obj_rt, obj_up;
+    sgSetVec3( obj_rt, 0.0, 1.0, 0.0); // Y axis
+    sgSetVec3( obj_up, 0.0, 0.0, 1.0); // Z axis
+
+    sgMat4 ROT_lon, ROT_lat, ROT_hdg;
+    sgMakeRotMat4( ROT_lon, lon, obj_up );
+    sgMakeRotMat4( ROT_lat, 90 - lat, obj_rt );
+    sgMakeRotMat4( ROT_hdg, hdg, obj_up );
+
+    sgMat4 TUX;
+    sgCopyMat4( TUX, ROT_hdg );
+    sgPostMultMat4( TUX, ROT_lat );
+    sgPostMultMat4( TUX, ROT_lon );
+    sgPostMultMat4( TUX, POS );
+
+    sgSetCoord( obj_pos, TUX );
+}
+#endif
+
+// Norman's 'fast hack' for above
+void FGAIEntity::FastWorldCoordinate(sgCoord *obj_pos, Point3D center) {
+    double lon_rad = lon * SGD_DEGREES_TO_RADIANS;
+    double lat_rad = lat * SGD_DEGREES_TO_RADIANS;
+    double hdg_rad = hdg * SGD_DEGREES_TO_RADIANS;
+
+    // setup transforms
+    Point3D geod( lon_rad, lat_rad, elev );
+       
+    Point3D world_pos = sgGeodToCart( geod );
+    Point3D offset = world_pos - center;
+
+    sgMat4 mat;
+
+    SGfloat sin_lat = (SGfloat)sin( lat_rad );
+    SGfloat cos_lat = (SGfloat)cos( lat_rad );
+    SGfloat cos_lon = (SGfloat)cos( lon_rad );
+    SGfloat sin_lon = (SGfloat)sin( lon_rad );
+    SGfloat sin_hdg = (SGfloat)sin( hdg_rad ) ;
+    SGfloat cos_hdg = (SGfloat)cos( hdg_rad ) ;
+
+    mat[0][0] =  cos_hdg * (SGfloat)sin_lat * (SGfloat)cos_lon - sin_hdg * (SGfloat)sin_lon;
+    mat[0][1] =  cos_hdg * (SGfloat)sin_lat * (SGfloat)sin_lon + sin_hdg * (SGfloat)cos_lon;
+    mat[0][2] =        -cos_hdg * (SGfloat)cos_lat;
+    mat[0][3] =         SG_ZERO;
+
+    mat[1][0] = -sin_hdg * (SGfloat)sin_lat * (SGfloat)cos_lon - cos_hdg * (SGfloat)sin_lon;
+    mat[1][1] = -sin_hdg * (SGfloat)sin_lat * (SGfloat)sin_lon + cos_hdg * (SGfloat)cos_lon;
+    mat[1][2] =         sin_hdg * (SGfloat)cos_lat;
+    mat[1][3] =         SG_ZERO;
+
+    mat[2][0] = (SGfloat)cos_lat * (SGfloat)cos_lon;
+    mat[2][1] = (SGfloat)cos_lat * (SGfloat)sin_lon;
+    mat[2][2] =        (SGfloat)sin_lat;
+    mat[2][3] =  SG_ZERO;
+
+    mat[3][0] = offset.x();
+    mat[3][1] = offset.y();
+    mat[3][2] = offset.z();
+    mat[3][3] = SG_ONE ;
+
+    sgSetCoord( obj_pos, mat );
+}
diff --git a/src/ATC/AIEntity.hxx b/src/ATC/AIEntity.hxx
new file mode 100644 (file)
index 0000000..f3ac19b
--- /dev/null
@@ -0,0 +1,71 @@
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+/*****************************************************************
+*
+* WARNING - Curt has some ideas about AI traffic so anything in here
+* may get rewritten or scrapped.  Contact Curt curt@flightgear.org 
+* before spending any time or effort on this code!!!
+*
+******************************************************************/
+
+#ifndef _FG_AIEntity_HXX
+#define _FG_AIEntity_HXX
+
+#include <plib/sg.h>
+#include <plib/ssg.h>
+#include <simgear/math/point3d.hxx>
+
+class FGAIEntity {
+
+public:
+
+    // 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
+
+    virtual ~FGAIEntity();
+
+    // Run the internal calculations
+    virtual void Update();
+
+protected:
+
+    double lat;                //WGS84
+    double lon;                //WGS84
+    double elev;       //Meters
+    double hdg;                //True heading in degrees
+    double roll;       //degrees
+    double pitch;      //degrees
+
+    char* model_path;  //Path to the 3D model
+
+    ssgEntity* model;
+    ssgTransform* position;
+
+    void Transform();
+
+    //void WorldCoordinate(sgCoord *obj_pos, Point3D center);
+
+    void FastWorldCoordinate(sgCoord *obj_pos, Point3D center);
+
+};
+
+#endif  // _FG_AIEntity_HXX
\ No newline at end of file
diff --git a/src/ATC/AILocalTraffic.cxx b/src/ATC/AILocalTraffic.cxx
new file mode 100644 (file)
index 0000000..8ecb807
--- /dev/null
@@ -0,0 +1,100 @@
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+/*****************************************************************
+*
+* WARNING - Curt has some ideas about AI traffic so anything in here
+* may get rewritten or scrapped.  Contact Curt curt@flightgear.org 
+* before spending any time or effort on this code!!!
+*
+******************************************************************/
+
+#include <Main/globals.hxx>
+//#include <simgear/constants.h>
+#include <simgear/math/point3d.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <string>
+
+SG_USING_STD(string);
+
+#include "ATCmgr.hxx"
+#include "AILocalTraffic.hxx"
+
+FGAILocalTraffic::FGAILocalTraffic() {
+}
+
+FGAILocalTraffic::~FGAILocalTraffic() {
+}
+
+void FGAILocalTraffic::Init() {
+    // Hack alert - Hardwired path!!
+    string planepath = "Aircraft/c172/Models/c172-dpm.ac";
+    SGPath path = globals->get_fg_root();
+    path.append(planepath);
+    ssgTexturePath((char*)path.dir().c_str());
+    model = ssgLoad((char*)planepath.c_str());
+    if (model == 0) {
+       model = ssgLoad((char*)"Models/Geometry/glider.ac");
+       if (model == 0)
+           cout << "Failed to load an aircraft model in AILocalTraffic\n";
+    } else {
+       cout << "AILocal Traffic Model loaded successfully\n";
+    }
+    position = new ssgTransform;
+    position->addKid(model);
+
+    // Hardwire to KEMT
+    lat = 34.081358;
+    lon = -118.037483;
+    elev = (287.0 + 0.5) * SG_FEET_TO_METER;  // Ground is 296 so this should be above it
+    mag_hdg = -10.0;
+    pitch = 0.0;
+    roll = 0.0;
+    mag_var = -14.0;
+
+    // Activate the tower - this is dependent on the ATC system being initialised before the AI system
+    AirportATC a;
+    if(globals->get_ATC_mgr()->GetAirportATCDetails((string)"KEMT", &a)) {
+       if(a.tower_freq) {      // Has a tower
+           tower = (FGTower*)globals->get_ATC_mgr()->GetATCPointer((string)"KEMT", TOWER);     // Maybe need some error checking here
+       } else {
+           // Check CTAF, unicom etc
+       }
+    } else {
+       cout << "Unable to find airport details in FGAILocalTraffic::Init()\n";
+    }
+
+    Transform();
+}
+
+// Run the internal calculations
+void FGAILocalTraffic::Update() {
+    hdg = mag_hdg + mag_var;
+    
+    // This should become if(the plane has moved) then Transform()
+    static int i = 0;
+    if(i == 60) {
+       Transform();
+       i = 0;
+    }
+    i++; 
+}
diff --git a/src/ATC/AILocalTraffic.hxx b/src/ATC/AILocalTraffic.hxx
new file mode 100644 (file)
index 0000000..fdbd2df
--- /dev/null
@@ -0,0 +1,100 @@
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+/*****************************************************************
+*
+* WARNING - Curt has some ideas about AI traffic so anything in here
+* may get rewritten or scrapped.  Contact Curt curt@flightgear.org 
+* before spending any time or effort on this code!!!
+*
+******************************************************************/
+
+#ifndef _FG_AILocalTraffic_HXX
+#define _FG_AILocalTraffic_HXX
+
+#include <plib/sg.h>
+#include <plib/ssg.h>
+#include <simgear/math/point3d.hxx>
+
+#include "tower.hxx"
+#include "AIEntity.hxx"
+
+typedef enum pattern_leg {
+    TAKEOFF_ROLL,
+    OUTWARD,
+    TURN1,
+    CROSSWIND,
+    TURN2,
+    DOWNWIND,
+    TURN3,
+    BASE,
+    TURN4,
+    FINAL,
+    LANDING_ROLL
+};
+
+class FGAILocalTraffic : public FGAIEntity {
+
+public:
+
+    FGAILocalTraffic();
+    ~FGAILocalTraffic();
+
+    // Initialise
+    void Init();
+
+    // Run the internal calculations
+    void Update();
+
+private:
+
+    char* airport;     // The ICAO code of the airport that we're operating around
+    double freq;       // The frequency that we're operating on - might not need this eventually
+    FGTower* tower;    // A pointer to the tower control.
+
+    double mag_hdg;    // degrees - the heading that the physical aircraft is pointing
+    double mag_var;    // degrees
+
+    double vel;                // velocity along track in m/s
+    double track;      // track - degrees relative to *magnetic* north
+    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
+
+    // Performance characteristics of the plane in knots and ft/min
+    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 stall_speed_landing_config;
+
+    // OK, what do we need to know whilst flying the pattern
+    pattern_leg leg;
+
+};
+
+#endif  // _FG_AILocalTraffic_HXX
\ No newline at end of file
diff --git a/src/ATC/AIMgr.cxx b/src/ATC/AIMgr.cxx
new file mode 100644 (file)
index 0000000..b4c341e
--- /dev/null
@@ -0,0 +1,70 @@
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+/*****************************************************************
+*
+* WARNING - Curt has some ideas about AI traffic so anything in here
+* may get rewritten or scrapped.  Contact Curt curt@flightgear.org 
+* before spending any time or effort on this code!!!
+*
+******************************************************************/
+
+
+
+#include <Main/fgfs.hxx>
+#include <Main/fg_props.hxx>
+
+#include <list>
+
+#include "AIMgr.hxx"
+#include "AILocalTraffic.hxx"
+
+SG_USING_STD(list);
+
+
+FGAIMgr::FGAIMgr() {
+}
+
+FGAIMgr::~FGAIMgr() {
+}
+
+void FGAIMgr::init() {
+    // Hard wire some local traffic for now.
+    // This is regardless of location and hence *very* ugly but it is a start.
+    FGAILocalTraffic* local_traffic = new FGAILocalTraffic;
+    local_traffic->Init();
+    ai_list.push_back(local_traffic);
+}
+
+void FGAIMgr::bind() {
+}
+
+void FGAIMgr::unbind() {
+}
+
+void FGAIMgr::update(int dt) {
+    // Traverse the list of active planes and run all their update methods
+    ai_list_itr = ai_list.begin();
+    while(ai_list_itr != ai_list.end()) {
+       (*ai_list_itr)->Update();
+       ++ai_list_itr;
+    }
+}
\ No newline at end of file
diff --git a/src/ATC/AIMgr.hxx b/src/ATC/AIMgr.hxx
new file mode 100644 (file)
index 0000000..3933348
--- /dev/null
@@ -0,0 +1,96 @@
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+/*****************************************************************
+*
+* WARNING - Curt has some ideas about AI traffic so anything in here
+* may get rewritten or scrapped.  Contact Curt curt@flightgear.org 
+* before spending any time or effort on this code!!!
+*
+******************************************************************/
+
+#ifndef _FG_AIMGR_HXX
+#define _FG_AIMGR_HXX
+
+#include <Main/fgfs.hxx>
+#include <Main/fg_props.hxx>
+
+#include <list>
+
+#include "AIEntity.hxx"
+
+SG_USING_STD(list);
+
+class FGAIMgr : public FGSubsystem
+{
+
+private:
+
+    // 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.
+
+    // Position of the Users Aircraft
+    // (This may be needed to calculate the distance from the user when deciding which 3D model to render)
+    double current_lon;
+    double current_lat;
+    double current_elev;
+    // Pointers to current users position
+    SGPropertyNode *current_lon_node;
+    SGPropertyNode *current_lat_node;
+    SGPropertyNode *current_elev_node;
+
+    //FGATIS atis;
+    //FGGround ground;
+    //FGTower tower;
+    //FGApproach approach;
+    //FGDeparture departure;
+
+public:
+
+    FGAIMgr();
+    ~FGAIMgr();
+
+    void init();
+
+    void bind();
+
+    void unbind();
+
+    void update(int dt);
+
+private:
+
+    // Remove a class from the ai_list and delete it from memory
+    //void RemoveFromList(const char* id, atc_type tp);
+
+};
+
+#endif  // _FG_AIMGR_HXX
index 519ff06b769ceffc7b108b3ca52a69e808f3e4f0..e8410583596ff9e3a62d315cf17a40c3568f302f 100644 (file)
@@ -27,6 +27,12 @@ FGATC::~FGATC() {
 void FGATC::Update() {
 }
 
+void FGATC::AddPlane(string pid) {
+}
+
+int FGATC::RemovePlane() {
+}
+
 void FGATC::SetDisplay() {
 }
 
@@ -39,4 +45,24 @@ const char* FGATC::GetIdent() {
 
 atc_type FGATC::GetType() {
     return INVALID;
-}
\ No newline at end of file
+}
+
+ostream& operator << (ostream& os, atc_type atc) {
+    switch(atc) {
+    case(INVALID):
+       return(os << "INVALID");
+    case(ATIS):
+       return(os << "ATIS");
+    case(GROUND):
+       return(os << "GROUND");
+    case(TOWER):
+       return(os << "TOWER");
+    case(APPROACH):
+       return(os << "APPROACH");
+    case(DEPARTURE):
+       return(os << "DEPARTURE");
+    case(ENROUTE):
+       return(os << "ENROUTE");
+    }
+    return(os << "ERROR - Unknown switch in atc_type operator << ");
+}
index d0cd38dafb9c71651fe6023e3662ba245d87fd80..e3a573ec602be77e5086f543b7831a8e2faa4918 100644 (file)
@@ -22,6 +22,9 @@
 #ifndef _FG_ATC_HXX
 #define _FG_ATC_HXX
 
+#include <iostream>
+#include <string>
+
 // Possible types of ATC type that the radios may be tuned to.
 // INVALID implies not tuned in to anything.
 enum atc_type {
@@ -32,7 +35,9 @@ enum atc_type {
     APPROACH,
     DEPARTURE,
     ENROUTE
-};  
+}; 
+
+ostream& operator << (ostream& os, atc_type atc);
 
 class FGATC {
 
@@ -43,6 +48,12 @@ public:
     // Run the internal calculations
     virtual void Update();
 
+    // Add plane to a stack
+    virtual void AddPlane(string pid);
+
+    // Remove plane from stack
+    virtual int RemovePlane();
+
     // Indicate that this instance should output to the display if appropriate 
     virtual void SetDisplay();
 
index cf00f911c6eb467d3af4d60c5fea447dff3014d2..0bfce155daea81e5ce2563fb4acd83a5e3e7b4bc 100644 (file)
@@ -132,8 +132,75 @@ void FGATCDisplay::update(int dt) {
        }
        
     }
+
+    if(msgList.size()) {
+       //cout << "Attempting to render single message\n";
+       // We have at least one non-repeating message to process
+       msgList_itr = msgList.begin();
+       int i = 0;
+       while(msgList_itr != msgList.end()) {
+           atcMessage m = *msgList_itr;
+           //cout << "m.counter = " << m.counter << '\n';
+           if(m.counter > m.stop_count) {
+               //cout << "Stopping single message\n";
+               msgList_itr = msgList.erase(msgList_itr);
+           } else if(m.counter > m.start_count) {
+               //cout << "Displaying single message\n";
+               // Display the message
+               // FIXME - I'm sure all this opengl code should only be called once until all drawing is finished
+               SGPropertyNode *xsize_node = fgGetNode("/sim/startup/xsize");
+               SGPropertyNode *ysize_node = fgGetNode("/sim/startup/ysize");
+               int iwidth   = xsize_node->getIntValue();
+               int iheight  = ysize_node->getIntValue();
+               glMatrixMode( GL_PROJECTION );
+               glPushMatrix();
+               glLoadIdentity();
+               gluOrtho2D( 0, iwidth, 0, iheight );
+               glMatrixMode( GL_MODELVIEW );
+               glPushMatrix();
+               glLoadIdentity();
+               
+               glDisable( GL_DEPTH_TEST );
+               glDisable( GL_LIGHTING );
+       
+               glColor3f( 0.9, 0.4, 0.2 );
+       
+               guiFnt.drawString( m.msg.c_str(),
+                   100,
+                   (iheight - 40) );   // TODO - relate the distance in that the string is rendered to the string length.
+               glEnable( GL_DEPTH_TEST );
+               glEnable( GL_LIGHTING );
+               glMatrixMode( GL_PROJECTION );
+               glPopMatrix();
+               glMatrixMode( GL_MODELVIEW );
+               glPopMatrix();
+               ++m.counter;
+               msgList[i] = m;
+               ++msgList_itr;
+               ++i;
+           } else {
+               ++m.counter;
+               msgList[i] = m;
+               ++msgList_itr;
+               ++i;
+           }
+       }
+    }
 }
 
+void FGATCDisplay::RegisterSingleMessage(string msg, int delay) {
+    atcMessage m;
+    m.msg = msg;
+    m.repeating = false;
+    m.counter = 0;
+    m.start_count = delay * 30;                // Fixme - need to use actual FPS
+    m.stop_count = m.start_count + 100;                // Display for 3 - 5 seconds for now - this might have to change eg. be related to length of message in future
+    //cout << "m.stop_count = " << m.stop_count << '\n';
+    m.id = 0;
+    
+    msgList.push_back(m);
+    //cout << "Single message registered\n";
+}
 
 void FGATCDisplay::RegisterRepeatingMessage(string msg) {
     rep_msg = true;
index 86ffca10966ad2b1b19ec40ad128583ad76877d5..9c2c9805f1cd4eb0431c433ef9029b68339cdf13 100644 (file)
@@ -38,6 +38,9 @@ SG_USING_STD(string);
 struct atcMessage {
     string msg;
     bool repeating;
+    int counter;       // count of how many iterations since posting
+    int start_count;   // value of counter at which display should start
+    int stop_count;    // value of counter at which display should stop
     int id;
 };
 
@@ -70,8 +73,9 @@ public:
     // Display any registered messages
     void update(int dt);
 
-    // Register a single message for display when possible
-    void RegisterSingleMessage(string msg);    // OK - I know passing a string in and out is probably not good but it will have to do for now.
+    // Register a single message for display after a delay of delay seconds
+    // Will automatically stop displaying after a suitable interval.
+    void RegisterSingleMessage(string msg, int delay); // OK - I know passing a string in and out is probably not good but it will have to do for now.
 
     // For now we will assume only one repeating message at once
     // This is not really robust
index 619ecf71a6955108f31fa4bd3e9a4a42b466224d..5d77f197495d50e2291576e238c7cff66f575433 100644 (file)
@@ -22,6 +22,9 @@
 
 #include "ATCmgr.hxx"
 #include "atislist.hxx"
+//#include "groundlist.hxx"
+#include "towerlist.hxx"
+#include "approachlist.hxx"
 
 /*
 // periodic radio station search wrapper
@@ -31,6 +34,19 @@ static void fgATCSearch( void ) {
 */ //This wouldn't compile - including Time/event.hxx breaks it :-(
 
 FGATCMgr::FGATCMgr() {
+    comm1_ident = "";
+    comm1_atis_ident = "";
+    comm1_tower_ident = "";
+    comm1_approach_ident = "";
+    last_comm1_ident = "";
+    last_comm1_atis_ident = "";
+    last_comm1_tower_ident = "";
+    last_comm1_approach_ident = "";
+    approach_ident = "";
+    last_in_range = false;
+    comm1_atis_valid = false;
+    comm1_tower_valid = false;
+    comm1_approach_valid = false;
 }
 
 FGATCMgr::~FGATCMgr() {
@@ -53,12 +69,30 @@ void FGATCMgr::init() {
     // global_events.Register( "fgATCSearch()", fgATCSearch,
     //             fgEVENT::FG_EVENT_READY, 800);
     // For some reason the above doesn't compile - including Time/event.hxx stops compilation.
+
+    // Initialise the airport_atc_map - we'll cheat for now and just hardcode KEMT and any others that may be needed for development
+    AirportATC *a = new AirportATC;
+    a->lon = -118.034719;
+    a->lat = 34.086114;
+    a->elev = 296.0;
+    a->atis_freq = 118.75;
+    a->atis_active = false;
+    a->tower_freq = 121.2;
+    a->tower_active = false;
+    a->ground_freq = 125.9;
+    a->ground_active = false;
+
+    //a->set_by_AI = true;
+    //a->set_by_comm_search = false;
+
+    airport_atc_map[(string)"KEMT"] = a;
 }
 
 void FGATCMgr::update(int dt) {
     //Traverse the list of active stations.
     //Only update one class per update step to avoid the whole ATC system having to calculate between frames.
     //Eventually we should only update every so many steps.
+    //cout << "In FGATCMgr::update - atc_list.size = " << atc_list.size() << '\n';
     if(atc_list.size()) {
        if(atc_list_itr == atc_list.end()) {
            atc_list_itr = atc_list.begin();
@@ -75,13 +109,33 @@ void FGATCMgr::update(int dt) {
     }
     ++i;
 }
+/*
+// Remove from list only if not needed by the AI system
+void FGATCMgr::CommRemoveFromList(const char* id, atc_type tp) {
+    AirportATC a;
+    if(GetAirportATCDetails((string)id, &a)) {
+       if(a.set_by_AI) {
+           // Don't remove
+           a.set_by_comm_search = false;
+           airport_atc_map[(string)id] = a;
+           return;
+       } else {
+           // remove
+           
+*/    
 
+// Remove from list - should only be called from above or similar
 void FGATCMgr::RemoveFromList(const char* id, atc_type tp) {
+    //cout << "Requested type = " << tp << '\n';
+    //cout << "id = " << id << '\n';
     atc_list_itr = atc_list.begin();
     while(atc_list_itr != atc_list.end()) {
+       //cout << "type = " << (*atc_list_itr)->GetType() << '\n';
+       //cout << "Ident = " << (*atc_list_itr)->GetIdent() << '\n';
         if( (!strcmp((*atc_list_itr)->GetIdent(), id))
            && ((*atc_list_itr)->GetType() == tp) ) {
            //Before removing it stop it transmitting!!
+           //cout << "OBLITERATING FROM LIST!!!\n";
            (*atc_list_itr)->SetNoDisplay();
            (*atc_list_itr)->Update();
            delete (*atc_list_itr);
@@ -92,27 +146,94 @@ void FGATCMgr::RemoveFromList(const char* id, atc_type tp) {
     }
 }
 
+// Returns true if the airport is found in the map
+bool FGATCMgr::GetAirportATCDetails(string icao, AirportATC* a) {
+    if(airport_atc_map.find(icao) != airport_atc_map.end()) {
+       a = airport_atc_map[icao];
+       return(true);
+    } else {
+       return(false);
+    }
+}
+
+
+// Return a pointer to a given sort of ATC at a given airport and activate if necessary
+// ONLY CALL THIS FUNCTION AFTER FIRST CHECKING THE SERVICE EXISTS BY CALLING GetAirportATCDetails
+FGATC* FGATCMgr::GetATCPointer(string icao, atc_type type) {
+    AirportATC *a = airport_atc_map[icao];
+    //cout << "a->lon = " << a->lon << '\n';
+    //cout << "a->elev = " << a->elev << '\n';
+    //cout << "a->tower_freq = " << a->tower_freq << '\n';
+    switch(type) {
+    case TOWER:
+       if(a->tower_active) {
+           // Get the pointer from the list
+       } else {
+           FGTower* t = new FGTower;
+           if(current_towerlist->query(a->lon, a->lat, a->elev, a->tower_freq, &tower)) {
+               *t = tower;
+               atc_list.push_back(t);
+               a->tower_active = true;
+               airport_atc_map[icao] = a;
+               return(t);
+           } else {
+               cout << "ERROR - tower that should exist in FGATCMgr::GetATCPointer for airport " << icao << " not found\n";
+           }
+       }
+       break;
+    // Lets add the rest to get rid of the compiler warnings even though we don't need them yet.
+    case APPROACH:
+       break;
+    case ATIS:
+       cout << "ERROR - ATIS station should not be requested from FGATCMgr::GetATCPointer" << endl;
+       break;
+    case GROUND:
+       break;
+    case INVALID:
+       break;
+    case ENROUTE:
+       break;
+    case DEPARTURE:
+       break;
+    }
+
+    cout << "ERROR IN FGATCMgr - reached end of GetATCPointer\n";
+}
+
+
+
 void FGATCMgr::Search() {
 
     ////////////////////////////////////////////////////////////////////////
     // Comm1.
     ////////////////////////////////////////////////////////////////////////
+    //cout << "In FGATCMgr::Search() - atc_list.size = " << atc_list.size() << '\n';
 
     comm1_freq = comm1_node->getDoubleValue();
-    double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
-    double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
+    //cout << "************* comm1_freq = " << comm1_freq << '\n';
+    double lon = lon_node->getDoubleValue();
+    double lat = lat_node->getDoubleValue();
     double elev = elev_node->getDoubleValue() * SG_FEET_TO_METER;
 
+    // Store the comm1_type
+    //atc_type old_comm1_type = comm1_type;
+
+// We must be able to generalise some of the repetetive searching below!
+
     //Search for ATIS first
     if(current_atislist->query(lon, lat, elev, comm1_freq, &atis)) {
        //cout << "atis found in radiostack search !!!!" << endl;
-       comm1_ident = atis.GetIdent();
-       comm1_valid = true;
-       if(last_comm1_ident != comm1_ident) {
-           if(last_comm1_ident != "") {
-               RemoveFromList(last_comm1_ident, ATIS);
+       //cout << "last_comm1_atis_ident = " << last_comm1_atis_ident << '\n';
+       //cout << "comm1_type " << comm1_type << '\n';
+       comm1_atis_ident = atis.GetIdent();
+       comm1_atis_valid = true;
+       if(last_comm1_atis_ident != comm1_atis_ident) {
+           if(last_comm1_atis_ident != "") {
+               RemoveFromList(last_comm1_atis_ident, ATIS);
            }
-           last_comm1_ident = comm1_ident;
+           last_comm1_atis_ident = comm1_atis_ident;
+           //cout << "last_comm1_atis_ident = " << last_comm1_atis_ident << '\n';
+           comm1_type = ATIS;
            comm1_elev = atis.get_elev();
            comm1_range = FG_ATIS_DEFAULT_RANGE;
            comm1_effective_range = comm1_range;
@@ -125,15 +246,168 @@ void FGATCMgr::Search() {
            atc_list.push_back(a);
            //cout << "Found a new atis station in range" << endl;
            //cout << " id = " << atis.GetIdent() << endl;
+           return;  //This rather assumes that we never have more than one type of station in range.
+       }
+    } else {
+       if(comm1_atis_valid) {
+           //cout << "Removing ATIS " << comm1_atis_ident << " from list\n";
+           RemoveFromList(comm1_atis_ident, ATIS);
+           comm1_atis_valid = false;
+           if(comm1_type == ATIS) {
+               comm1_type = INVALID;
+           }
+           comm1_atis_ident = "";
+           //comm1_trans_ident = "";
+           last_comm1_atis_ident = "";
+       }
+       //cout << "not picking up atis" << endl;
+    }
+
+    //Next search for tower
+    //cout << "comm1_freq = " << comm1_freq << '\n';
+    if(current_towerlist->query(lon, lat, elev, comm1_freq, &tower)) {
+       //cout << "tower found in radiostack search !!!!" << endl;
+       comm1_tower_ident = tower.GetIdent();
+       //cout << "comm1_tower_ident = " << comm1_tower_ident << '\n';
+       comm1_tower_valid = true;
+       if(last_comm1_tower_ident != comm1_tower_ident) {
+           if(last_comm1_tower_ident != "") {
+               RemoveFromList(last_comm1_tower_ident, TOWER);
+           }
+           last_comm1_tower_ident = comm1_tower_ident;
+           comm1_type = TOWER;
+           comm1_elev = tower.get_elev();
+           comm1_range = FG_TOWER_DEFAULT_RANGE;
+           comm1_effective_range = comm1_range;
+           comm1_x = tower.get_x();
+           comm1_y = tower.get_y();
+           comm1_z = tower.get_z();
+           FGTower* t = new FGTower;
+           *t = tower;
+           t->SetDisplay();
+           atc_list.push_back(t);
+           //cout << "Found a new tower station in range" << endl;
+           //cout << " id = " << tower.GetIdent() << endl;
+           return;  //This rather assumes that we never have more than one type of station in range.
+       }
+    } else {
+       if(comm1_tower_valid) {
+           //cout << "removing tower\n";
+           RemoveFromList(comm1_tower_ident, TOWER);
+           //comm1_valid = false;
+           if(comm1_type == TOWER) {
+               comm1_type = INVALID;   // Only invalidate if we haven't switched it to something else
+           }
+           comm1_tower_valid = false;
+           comm1_tower_ident = "";
+           last_comm1_tower_ident = "";
+           //comm1_ident = "";
+           //comm1_trans_ident = "";
+           //last_comm1_ident = "";
+       }
+       //cout << "not picking up tower" << endl;
+    }
+/*
+    //Next search for Ground control
+    if(current_groundlist->query(lon, lat, elev, comm1_freq, &ground)) {
+       //cout << "Ground Control found in radiostack search !!!!" << endl;
+       comm1_ident = ground.GetIdent();
+       comm1_valid = true;
+       if((last_comm1_ident != comm1_ident) || (comm1_type != GROUND)) {
+           if(last_comm1_ident != "") {
+               RemoveFromList(last_comm1_ident, GROUND);
+           }
+           last_comm1_ident = comm1_ident;
+           comm1_type = GROUND;
+           comm1_elev = ground.get_elev();
+           comm1_range = FG_GROUND_DEFAULT_RANGE;
+           comm1_effective_range = comm1_range;
+           comm1_x = ground.get_x();
+           comm1_y = ground.get_y();
+           comm1_z = ground.get_z();
+           FGGround* g = new FGGround;
+           *g = ground;
+           g->SetDisplay();
+           atc_list.push_back(g);
+           // For now we will automatically make contact with ground when the radio is tuned.
+           // This rather assumes that the user tunes the radio at the appropriate place
+           // (ie. having just turned off the runway) and only uses ground control on arrival
+           // but its a start!
+           g->NewArrival(current_plane);
+           //cout << "Found a new ground station in range" << endl;
+           //cout << " id = " << ground.GetIdent() << endl;
+           return;  //This rather assumes that we never have more than one type of station in range.
        }
     } else {
-       if(comm1_valid) {
-           RemoveFromList(comm1_ident, ATIS);
+       if((comm1_valid) && (comm1_type == GROUND)) {
+           RemoveFromList(comm1_ident, GROUND);
            comm1_valid = false;
+           comm1_type = INVALID;
            comm1_ident = "";
            //comm1_trans_ident = "";
            last_comm1_ident = "";
        }
-       //cout << "not picking up atis" << endl;
+       //cout << "not picking up ground control" << endl;
+    }
+*/
+// ================================================================================
+// Search for Approach stations
+// ================================================================================
+    // init number of approach stations reachable by plane
+    int  num_app = 0;
+
+    // search stations in range
+    current_approachlist->query_bck(lon, lat, elev, approaches, max_app, num_app);
+    if (num_app != 0) {
+      //cout << num_app << " approaches found in radiostack search !!!!" << endl;
+
+      for ( int i=0; i<num_app; i++ ) {
+       bool new_app = true;
+       approach_ident = approaches[i].GetIdent();
+
+       // check if station already exists on ATC stack
+       atc_list_itr = atc_list.begin();
+       while(atc_list_itr != atc_list.end()) {
+         //cout << "ATC list: " << (*atc_list_itr)->GetIdent() << endl;
+         if((!strcmp((*atc_list_itr)->GetIdent(), approach_ident))
+            && ((*atc_list_itr)->GetType() == APPROACH) ) {
+           new_app = false;
+           string pid = "Player";
+           (*atc_list_itr)->AddPlane(pid);
+           (*atc_list_itr)->Update();
+           break;
+         }
+         ++atc_list_itr;
+       }
+       // generate new Approach on ATC stack
+       if (new_app) {
+         FGApproach* a = new FGApproach;
+         *a = approaches[i];
+         string pid = "Player";
+         a->AddPlane(pid);
+         a->Update();
+         a->SetDisplay();
+         atc_list.push_back(a);
+         //cout << "Found a new approach station in range: Id = " 
+         //     << approaches[i].GetIdent() << endl;
+       }
+      }
+    }
+
+    // remove planes which are out of range
+    atc_list_itr = atc_list.begin();
+    while(atc_list_itr != atc_list.end()) {
+      if((*atc_list_itr)->GetType() == APPROACH ) {
+       int np = (*atc_list_itr)->RemovePlane();
+       // if approach has no planes left remove it from ATC list
+       if ( np == 0) {
+         (*atc_list_itr)->SetNoDisplay();
+         (*atc_list_itr)->Update();
+         delete (*atc_list_itr);
+         atc_list_itr = atc_list.erase(atc_list_itr);
+         break;     // the other stations will be checked next time
+       }
+      }
+      ++atc_list_itr;
     }
 }
index 66dc490dbdb90459fc5e613d58309e24d17d3650..03ac392e6c426d961aa5a770485e16e95dbee1fa 100644 (file)
 #include <Main/fgfs.hxx>
 #include <Main/fg_props.hxx>
 
+#include <string>
 #include <list>
+#include <map>
 
 #include "atis.hxx"
+#include "tower.hxx"
+#include "approach.hxx"
+//#include "ground.hxx"
 #include "ATC.hxx"
 
+SG_USING_STD(string);
 SG_USING_STD(list);
+SG_USING_STD(map);
+
+const int max_app = 20;
+
+// Structure for holding details of the ATC frequencies at a given airport, and whether they are in the active list or not.
+// These can then be cross referenced with the [atis][tower][etc]lists which are stored by frequency.
+// Non-available services are denoted by a frequency of zero.
+// Eventually the whole ATC data structures may have to be rethought if we turn out to be massive memory hogs!!
+struct AirportATC {
+    float lon;
+    float lat;
+    float elev;
+    float atis_freq;
+    bool atis_active;
+    float tower_freq;
+    bool tower_active;
+    float ground_freq;
+    bool ground_active;
+    //float approach_freq;
+    //bool approach_active;
+    //float departure_freq;
+    //bool departure_active;
+
+    // Flags to ensure the stations don't get wrongly deactivated
+    bool set_by_AI;    // true when the AI manager has activated this station
+    bool set_by_comm_search;   // true when the comm_search has activated this station
+};
 
 class FGATCMgr : public FGSubsystem
 {
 
 private:
 
+    // A map of airport ID vs frequencies and ATC provision
+    typedef map < string, AirportATC* > airport_atc_map_type;
+    typedef airport_atc_map_type::iterator airport_atc_map_iterator;
+    typedef airport_atc_map_type::const_iterator airport_atc_map_const_iterator;
+
+    airport_atc_map_type airport_atc_map;
+    airport_atc_map_iterator airport_atc_map_itr;
+
     // A list of pointers to all currently active ATC classes
     typedef list <FGATC*> atc_list_type;
     typedef atc_list_type::iterator atc_list_iterator;
@@ -55,6 +96,7 @@ private:
     double lat;
     double elev;
 
+    // Type of ATC control that the user's radios are tuned to.
     atc_type comm1_type;
     atc_type comm2_type;
 
@@ -75,8 +117,19 @@ private:
     double comm1_x, comm1_y, comm1_z, comm1_elev;
     double comm1_range, comm1_effective_range;
     bool comm1_valid; 
+    bool comm1_atis_valid;
+    bool comm1_tower_valid;
+    bool comm1_approach_valid;
     const char* comm1_ident;
+    const char* comm1_atis_ident;
+    const char* comm1_tower_ident;
+    const char* comm1_approach_ident;
     const char* last_comm1_ident;
+    const char* last_comm1_atis_ident;
+    const char* last_comm1_tower_ident;
+    const char* last_comm1_approach_ident;
+    const char* approach_ident;
+    bool last_in_range;
     double comm2_x, comm2_y, comm2_z, comm2_elev;
     double comm2_range, comm2_effective_range;
     bool comm2_valid;
@@ -84,9 +137,10 @@ private:
     const char* last_comm2_ident;
 
     FGATIS atis;
-    //FGTower tower;
     //FGGround ground;
-    //FGApproach approach;
+    FGTower tower;
+    FGApproach approaches[max_app];
+    FGApproach approach;
     //FGDeparture departure;
 
 public:
@@ -102,6 +156,12 @@ public:
 
     void update(int dt);
 
+    // Returns true if the airport is found in the map
+    bool GetAirportATCDetails(string icao, AirportATC* a);
+
+    // Return a pointer to a given sort of ATC at a given airport and activate if necessary
+    FGATC* GetATCPointer(string icao, atc_type type);
+
 private:
 
     // Remove a class from the atc_list and delete it from memory
diff --git a/src/ATC/ATCutils.cxx b/src/ATC/ATCutils.cxx
new file mode 100644 (file)
index 0000000..e69de29
index 1d6e1f4d60f5f7eda7bc565570fcc8fc4a35e3bf..d72f2bbc8f1b0a7a2c8bf23cad306513d591a5c0 100644 (file)
@@ -2,10 +2,15 @@ noinst_LIBRARIES = libATC.a
 
 libATC_a_SOURCES = \
        ATC.hxx ATC.cxx \
+       atis.hxx atis.cxx atislist.hxx atislist.cxx \
+       tower.hxx tower.cxx towerlist.hxx towerlist.cxx \
+       approach.hxx approach.cxx approachlist.hxx approachlist.cxx \
        ATCdisplay.hxx ATCdisplay.cxx \
        ATCmgr.hxx ATCmgr.cxx \
-       atis.cxx atis.hxx \
-       atislist.hxx atislist.cxx
+       ATCutils.hxx ATCutils.cxx \
+       AIMgr.hxx AIMgr.cxx \
+       AIEntity.hxx AIEntity.cxx \
+       AILocalTraffic.hxx AILocalTraffic.cxx
 
 if OLD_AUTOMAKE
 INCLUDES += -I$(top_srcdir) -I$(top_srcdir)/src
diff --git a/src/ATC/approach.cxx b/src/ATC/approach.cxx
new file mode 100644 (file)
index 0000000..5d96706
--- /dev/null
@@ -0,0 +1,366 @@
+// FGApproach - a class to provide approach control at larger airports.
+//
+// Written by Alexander Kappes, started March 2002.
+//
+// Copyright (C) 2002  Alexander Kappes
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#include "approach.hxx"
+#include "ATCdisplay.hxx"
+#include <Airports/runways.hxx>
+
+#include <simgear/misc/sg_path.hxx>
+#include <WeatherCM/FGLocalWeatherDatabase.h>
+
+
+//Constructor
+FGApproach::FGApproach(){
+  comm1_node = fgGetNode("/radios/comm[0]/frequencies/selected-mhz", true);
+  comm2_node = fgGetNode("/radios/comm[1]/frequencies/selected-mhz", true);
+
+  num_planes = 0;
+  lon_node = fgGetNode("/position/longitude-deg", true);
+  lat_node = fgGetNode("/position/latitude-deg", true);
+  elev_node = fgGetNode("/position/altitude-ft", true);
+  first = true;
+  active_runway = "";
+  for ( int i=0; i<max_planes; i++) {
+    planes[i].contact = 0;
+    planes[i].wpn     = 0;
+    planes[i].dnwp    = -999.;
+    planes[i].on_crs  = true;
+  }
+}
+
+//Destructor
+FGApproach::~FGApproach(){
+}
+
+void FGApproach::Init() {
+  display    = false;
+}
+
+// ============================================================================
+// the main update function
+// ============================================================================
+void FGApproach::Update() {
+
+  int wpn;
+  double course, d;
+
+  update_plane_dat();
+  if ( active_runway == "" ) get_active_runway();
+  
+  for ( int i=0; i<num_planes; i++ ) {
+    
+    if ( planes[i].contact == 0) {
+      double comm1_freq = comm1_node->getDoubleValue();
+      if ( (int)(comm1_freq*100.0 + 0.5) == freq ) planes[i].contact = 1;
+    }
+    else if ( planes[i].contact == 1 ) {
+      if ( planes[i].wpn == 0 ) {    // calculate initial waypoints
+       wpn = planes[i].wpn;
+       // airport
+       planes[i].wpts[wpn][0] = active_rw_hdg;
+       planes[i].wpts[wpn][1] = 0.0;
+       planes[i].wpts[wpn][2] = elev;
+       planes[i].wpts[wpn][4] = 0.0;
+       planes[i].wpts[wpn][5] = 0.0;
+       wpn += 1;
+
+       planes[i].wpts[wpn][0] = active_rw_hdg + 180.0;
+       if ( planes[i].wpts[wpn][0] > 360.0 ) planes[i].wpts[wpn][0] -= 360.0;
+       planes[i].wpts[wpn][1] = 5;
+       planes[i].wpts[wpn][2] = elev + 1000.0;
+       calc_hd_course_dist(planes[i].wpts[wpn][0],   planes[i].wpts[wpn][1],
+                           planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
+                           &course, &d);
+       planes[i].wpts[wpn][4] = course;
+       planes[i].wpts[wpn][5] = d;
+       wpn += 1;
+       
+       planes[i].wpts[wpn][0] = planes[i].brg;
+       planes[i].wpts[wpn][1] = planes[i].dist;
+       planes[i].wpts[wpn][2] = planes[i].alt;
+       calc_hd_course_dist(planes[i].wpts[wpn][0],   planes[i].wpts[wpn][1],
+                           planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
+                           &course, &d);
+       planes[i].wpts[wpn][4] = course;
+       planes[i].wpts[wpn][5] = d;
+       wpn += 1;
+
+       planes[i].wpn = wpn;
+
+       planes[i].ahdg = planes[i].wpts[wpn-1][4];
+       cout << endl;
+       cout << "Contact " << planes[i].wpn << endl;
+       cout << "Turn to heading   = " << (int)(planes[i].ahdg) << endl;
+       cout << endl;
+       planes[i].on_crs = true;
+      }
+
+      // reached waypoint?
+      if ( fabs(planes[i].dnc) < 0.3 && planes[i].dnwp < 1.0 ) {
+       planes[i].wpn -= 1;
+       wpn = planes[i].wpn-1;
+       planes[i].ahdg = planes[i].wpts[wpn][4];
+       cout << endl;
+       cout << "Next waypoint = " << planes[i].wpn << endl;
+       cout << "New heading   = " << planes[i].ahdg << endl;
+       cout << endl;
+       planes[i].on_crs = true;
+      }
+
+      // update assigned parameters
+      wpn = planes[i].wpn-1;            // this is the current waypoint
+
+      planes[i].dcc  = calc_psl_dist(planes[i].brg, planes[i].dist,
+                                    planes[i].wpts[wpn][0], planes[i].wpts[wpn][1],
+                                    planes[i].wpts[wpn][4]);
+      planes[i].dnc  = calc_psl_dist(planes[i].brg, planes[i].dist,
+                                    planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
+                                    planes[i].wpts[wpn-1][4]);
+      calc_hd_course_dist(planes[i].brg, planes[i].dist, 
+                         planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
+                         &course, &d);
+      planes[i].dnwp = d;
+
+      //cout << planes[i].brg << " " << planes[i].dist << " " << planes[i].wpts[wpn+1][0] 
+      //<< " " << planes[i].wpts[wpn+1][1] << " " << planes[i].wpts[wpn+1][4] 
+      //cout << " distance to current course = " << planes[i].dcc << endl;
+
+      // come off course ?
+      if ( fabs(planes[i].dcc) > 0.5 && planes[i].on_crs) {
+       wpn = wpn-1;
+       if ( planes[i].wpts[wpn][4] < 0) {
+         planes[i].ahdg += 30.0;
+       }
+       else {
+         planes[i].ahdg -= 30.0;
+       }
+       planes[i].on_crs = false;
+
+       cout << endl;
+       cout << "Your are " << planes[i].dcc << " miles off the asigned course: " << endl;
+       cout << "New heading = " << (int)(planes[i].ahdg) << endl;
+       cout << endl;
+      }
+      else if ( fabs(planes[i].dcc) < 0.1 && !planes[i].on_crs) {
+       planes[i].ahdg = fabs(planes[i].wpts[wpn][4]);
+       planes[i].on_crs = true;
+       
+       cout << endl;
+       cout << "New heading = " << (int)(planes[i].ahdg) << endl;
+       cout << endl;
+       }
+      
+      // In range of tower?
+      if ( planes[i].wpn == 2 && planes[i].dnwp < 3. ) {
+       cout << endl;
+       cout << "Contact Tower";
+       cout << endl;
+       planes[i].contact = 2;
+      }
+    }
+  }
+
+}
+
+// ============================================================================
+// get active runway
+// ============================================================================
+void FGApproach::get_active_runway() {
+
+  sgVec3 position = { lat, lon, elev };
+  FGPhysicalProperty stationweather = WeatherDatabase->get(position);
+
+  SGPath path( globals->get_fg_root() );
+  path.append( "Airports" );
+  path.append( "runways.mk4" );
+  FGRunways runways( path.c_str() );
+  
+  //Set the heading to into the wind
+  double wind_x = stationweather.Wind[0];
+  double wind_y = stationweather.Wind[1];
+  
+  double speed = sqrt( wind_x*wind_x + wind_y*wind_y ) * SG_METER_TO_NM / (60.0*60.0);
+  double hdg;
+  
+  //If no wind use 270degrees
+  if(speed == 0) {
+    hdg = 270;
+  } else {
+    // //normalize the wind to get the direction
+    //wind_x /= speed; wind_y /= speed;
+    
+    hdg = - atan2 ( wind_x, wind_y ) * SG_RADIANS_TO_DEGREES ;
+    if (hdg < 0.0)
+      hdg += 360.0;
+  }
+  
+  FGRunway runway;
+  if ( runways.search( ident, int(hdg), &runway) ) {
+    active_runway = runway.rwy_no;
+    active_rw_hdg = runway.heading;
+    //cout << "Active runway is: " << active_runway << "  heading = " 
+    // << active_rw_hdg << endl;
+  }
+  else cout << "FGRunways search failed" << endl;
+
+}
+
+// ========================================================================
+// update infos about plane
+// ========================================================================
+void FGApproach::update_plane_dat() {
+  
+  //cout << "Update Approach " << ident << "   " << num_planes << " registered" << endl;
+  // update plane positions
+  for (int i=0; i<num_planes; i++) {
+    planes[i].lon = lon_node->getDoubleValue();
+    planes[i].lat = lat_node->getDoubleValue();
+    planes[i].alt = elev_node->getDoubleValue();
+//    Point3D aircraft = sgGeodToCart( Point3D(planes[i].lon*SGD_DEGREES_TO_RADIANS, 
+//                                          planes[i].lat*SGD_DEGREES_TO_RADIANS, 
+//                                          planes[i].alt*SG_FEET_TO_METER) );
+    double course, distance;
+    calc_gc_course_dist(Point3D(lon*SGD_DEGREES_TO_RADIANS, lat*SGD_DEGREES_TO_RADIANS, 0.0),
+                       Point3D(planes[i].lon*SGD_DEGREES_TO_RADIANS,planes[i].lat*SGD_DEGREES_TO_RADIANS, 0.0 ),
+                       &course, &distance);
+    planes[i].dist = distance/SG_NM_TO_METER;
+    planes[i].brg  = 360.0-course*SGD_RADIANS_TO_DEGREES;
+
+    //cout << "Plane Id: " << planes[i].ident << "  Distance to " << ident 
+    //<< " is " << planes[i].dist << " m" << endl;
+    
+    //if (first) {
+    //transmission = ident;
+    //globals->get_ATC_display()->RegisterRepeatingMessage(transmission);
+    //first = false;
+    //}
+  }   
+}
+
+// =======================================================================
+// Add plane to Approach list
+// =======================================================================
+void FGApproach::AddPlane(string pid) {
+  for ( int i=0; i<num_planes; i++) {
+    if ( planes[i].ident == pid) {
+      //cout << "Plane already registered: " << ident << " " << num_planes << endl;
+      return;
+    }
+  }
+  planes[num_planes].ident = pid;
+  ++num_planes;
+  //cout << "Plane added to list: " << ident << " " << num_planes << endl;
+  return;
+}
+
+// ========================================================================
+// closest distance between a point and a straigt line in 2 dim.
+// ========================================================================
+double FGApproach::calc_psl_dist(const double &h1, const double &d1,
+                                const double &h2, const double &d2,
+                                const double &h3)
+{
+  double a1 = h1 * SGD_DEGREES_TO_RADIANS;
+  double a2 = h2 * SGD_DEGREES_TO_RADIANS;
+  double a3 = h3 * SGD_DEGREES_TO_RADIANS;
+  double x1 = cos(a1) * d1;
+  double y1 = sin(a1) * d1;
+  double x2 = cos(a2) * d2;
+  double y2 = sin(a2) * d2;
+  double x3 = cos(a3);
+  double y3 = sin(a3);
+  
+  // formula: dis = sqrt( (v1-v2)**2 - ((v1-v2)*v3)**2 ); vi = (xi,yi)
+  double val1   = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
+  double val2   = ((x1-x2)*x3 + (y1-y2)*y3) * ((x1-x2)*x3 + (y1-y2)*y3);
+  double dis    = val1 - val2;
+  // now get sign for offset 
+  //cout << x1 << " " << x2 << " " << y1 << " " << y2 << " " 
+  //     << x3 << " " << y3 << " " 
+  //     << val1 << " " << val2 << " " << dis << endl;
+  x3 *= sqrt(val2);
+  y3 *= sqrt(val2);
+  if ( x3*(x1-x2) < 0.0 && y3*(y1-y2) < 0.0) {
+    x3 *= -1.0;
+    y3 *= -1.0;
+  }
+  //cout << x3 << " " << y3 << endl;
+  double dis1   = x1-x2-x3;
+  double dis2   = y1-y2-y3;
+  dis = sqrt(dis);
+  if (atan2(dis2,dis1) < a3) dis *= -1.0;
+  //cout << dis1 << " " << dis2 << " " << atan2(dis2,dis1)*SGD_RADIANS_TO_DEGREES << " " << h3
+  //     << " " << sqrt(dis1*dis1 + dis2*dis2) << " " << dis << endl;
+  //cout << atan2(dis2,dis1)*SGD_RADIANS_TO_DEGREES << " " << dis << endl;
+
+  return dis;
+}
+
+// ========================================================================
+// get heading and distance between two points; point1 ---> point2
+// ========================================================================
+void FGApproach::calc_hd_course_dist(const double &h1, const double &d1, 
+                                    const double &h2, const double &d2,
+                                    double *course, double *dist)
+{
+  double a1 = h1 * SGD_DEGREES_TO_RADIANS;
+  double a2 = h2 * SGD_DEGREES_TO_RADIANS;
+  double x1 = cos(a1) * d1;
+  double y1 = sin(a1) * d1;
+  double x2 = cos(a2) * d2;
+  double y2 = sin(a2) * d2;
+          
+  *dist   = sqrt( (y2-y1)*(y2-y1) + (x2-x1)*(x2-x1) );
+  *course = atan2( (y2-y1), (x2-x1) ) * SGD_RADIANS_TO_DEGREES;
+  if ( *course < 0 ) *course = *course+360;
+  //cout << x1 << " " << y1 << " " << x2 << " " << y2 << " " << *dist << " " << *course << endl;
+}
+
+
+
+int FGApproach::RemovePlane() {
+
+  // first check if anything has to be done
+  bool rmplane = false;
+  for (int i=0; i<num_planes; i++) {
+    if (planes[i].dist > range*SG_NM_TO_METER) {
+      rmplane = true;
+      break;
+    }
+  }
+  if (!rmplane) return num_planes;
+
+  // now make a copy of the plane list
+  PlaneApp tmp[max_planes];
+  for (int i=0; i<num_planes; i++) {
+    tmp[i] = planes[i];
+  }
+  
+  int np = 0;
+  // now check which planes are still in range
+  for (int i=0; i<num_planes; i++) {
+    if (tmp[i].dist <= range*SG_NM_TO_METER) {
+      planes[np] = tmp[i];
+      np += 1;
+    }
+  }
+  num_planes = np;
+  return num_planes;
+}
\ No newline at end of file
diff --git a/src/ATC/approach.hxx b/src/ATC/approach.hxx
new file mode 100644 (file)
index 0000000..8376ab3
--- /dev/null
@@ -0,0 +1,266 @@
+// approach.hxx -- Approach class
+//
+// Written by Alexander Kappes, started March 2002.
+//
+// Copyright (C) 2002  Alexander Kappes
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+#ifndef _FG_APPROACH_HXX
+#define _FG_APPROACH_HXX
+
+#include <stdio.h>
+
+#include <simgear/compiler.h>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/misc/sgstream.hxx>
+#include <simgear/magvar/magvar.hxx>
+#include <simgear/timing/sg_time.hxx>
+#include <simgear/bucket/newbucket.hxx>
+
+#include <Main/fg_props.hxx>
+
+#ifdef SG_HAVE_STD_INCLUDES
+#  include <istream>
+#include <iomanip>
+#elif defined( SG_HAVE_NATIVE_SGI_COMPILERS )
+#  include <iostream.h>
+#elif defined( __BORLANDC__ )
+#  include <iostream>
+#else
+#  include <istream.h>
+#include <iomanip.h>
+#endif
+
+#if ! defined( SG_HAVE_NATIVE_SGI_COMPILERS )
+SG_USING_STD(istream);
+#endif
+
+SG_USING_STD(string);
+
+#include "ATC.hxx"
+
+//DCL - a complete guess for now.
+#define FG_APPROACH_DEFAULT_RANGE 100
+
+// Contains all information about a plane that the approach control needs
+const int max_planes = 20;  // max number of planes on the stack
+const int max_wp = 10;      // max number of waypoints for approach phase
+struct PlaneApp {
+
+  // variables for plane if it's on the radar
+  string ident;          // indentification of plane
+  double lon;            // longitude in degrees
+  double lat;            // latitude in degrees
+  double alt;            // Altitute above sea level in feet
+  double hdg;            // heading of plane in degrees
+  double dist;           // distance to airport in miles
+  double brg;            // bearing relative to airport in degrees
+  double spd;            // speed above ground
+  int    contact;        // contact with approach established?
+                         // 0 = no contact yet
+                         // 1 = in contact
+                         // 2 = handed off to tower
+
+  // additional variables if contact has been established
+  int    wpn;                 // number of waypoints
+  double wpts[max_wp][6];     // assigned waypoints for approach phase 
+                              // first wp in list is airport
+                              // last waypoint point at which contact was established
+                              // second index: 0 = bearing to airport
+                              // second index: 1 = distance to aiport
+                              // second index: 2 = alt 
+                              // second index: 3 = ETA
+                              // second index: 4 = heading to next waypoint
+                              // second index: 5 = distance to next waypoint
+
+  double dnwp;           // distance to next waypoint
+  double dcc;            // closest distance to current assigned course
+  double dnc;            // closest distance to course from next to next to next wp
+  double aalt;           // assigned alt at next waypoint
+  double ahdg;           // assigned heading
+  bool   on_crs;         // is the plane on course?
+  double tlm;            // time when last message was sent
+};
+
+
+class FGApproach : public FGATC {
+
+  char     type;
+  double   lon, lat;
+  double   elev;
+  double   x, y, z;
+  int      freq;
+  int      bucket;
+  double   range;
+
+  string active_runway;         
+  double active_rw_hdg;
+
+  bool     display;            // Flag to indicate whether we should be outputting to the display.
+  bool     displaying;         // Flag to indicate whether we are outputting to the display.
+  string   ident;              // Code of the airport its at.
+  string   name;               // Name transmitted in the broadcast.
+  int      num_planes;          // number of planes on the stack
+  PlaneApp planes[max_planes];  // Array of planes
+  string   transmission;
+  bool     first;
+
+  SGPropertyNode *comm1_node;
+  SGPropertyNode *comm2_node;
+
+  // for failure modeling
+  string trans_ident;  // transmitted ident
+  bool approach_failed;        // approach failed?
+
+public:
+
+  FGApproach(void);
+  ~FGApproach(void);
+
+  void Init();
+
+  void Update();
+
+  // Add new plane to stack if not already registered 
+  // Input:  pid - id of plane (name) 
+  // Output: "true" if added; "false" if already existend
+  void AddPlane(string pid);
+
+  // Remove plane from stack if out of range
+  int RemovePlane();
+  
+  //Indicate that this instance should be outputting to the ATC display
+  inline void SetDisplay(void) {display = true;}
+  
+  //Indicate that this instance should not be outputting to the ATC display
+  inline void SetNoDisplay(void) {display = false;}
+  
+  inline char get_type() const { return type; }
+  inline double get_lon() const { return lon; }
+  inline double get_lat() const { return lat; }
+  inline double get_elev() const { return elev; }
+  inline double get_x() const { return x; }
+  inline double get_y() const { return y; }
+  inline double get_z() const { return z; }
+  inline double get_bucket() const { return bucket; }
+  inline int get_freq() const { return freq; }
+  inline double get_range() const { return range; }
+  inline int get_pnum() const { return num_planes; }
+  inline const char* GetIdent() { return ident.c_str(); }
+  inline string get_trans_ident() { return trans_ident; }
+  inline string get_name() { return name; }
+  inline atc_type GetType() { return APPROACH; }
+  
+private:
+
+  void update_plane_dat();
+
+  void get_active_runway();
+
+// ========================================================================
+// get heading and distance between two points; point2 ---> point1
+// input: point1 = heading in degrees, distance
+// input: point2 = heading in degrees, distance
+// ouput: course in degrees, distance
+// ========================================================================
+  void calc_hd_course_dist(const double &h1, const double &d1,
+                          const double &h2, const double &d2,
+                          double *course, double *dist);
+
+// ========================================================================
+// closest distance between a point and a straigt line in 2 dim.
+// the input variables are given in (heading, distance) 
+// relative to a common point
+// input:  point        = heading in degrees, distance
+// input:  straigt line = anker vector (heading in degrees, distance), 
+//                        heading of direction vector
+// output: distance
+// ========================================================================
+  double calc_psl_dist(const double &h1, const double &d1,
+                      const double &h2, const double &d2,
+                      const double &h3);
+
+  // Pointers to current users position
+  SGPropertyNode *lon_node;
+  SGPropertyNode *lat_node;
+  SGPropertyNode *elev_node;
+  
+  //Update the transmission string
+  void UpdateTransmission(void);
+  
+  friend istream& operator>> ( istream&, FGApproach& );
+};
+
+
+inline istream&
+operator >> ( istream& in, FGApproach& a )
+{
+    double f;
+    char ch;
+
+    static bool first_time = true;
+    static double julian_date = 0;
+    static const double MJD0    = 2415020.0;
+    if ( first_time ) {
+       julian_date = sgTimeCurrentMJD(0, 0) + MJD0;
+       first_time = false;
+    }
+
+    in >> a.type;
+    
+    if ( a.type == '[' )
+      return in >> skipeol;
+
+    in >> a.lat >> a.lon >> a.elev >> f >> a.range 
+       >> a.ident;
+
+    a.name = "";
+    in >> ch;
+    a.name += ch;
+    while(1) {
+       //in >> noskipws
+       in.unsetf(ios::skipws);
+       in >> ch;
+       a.name += ch;
+       if((ch == '"') || (ch == 0x0A)) {
+           break;
+       }   // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the "
+    }
+    in.setf(ios::skipws);
+    //cout << "approach.name = " << a.name << '\n';
+
+    a.freq = (int)(f*100.0 + 0.5);
+
+    // generate cartesian coordinates
+    Point3D geod( a.lon * SGD_DEGREES_TO_RADIANS , a.lat * SGD_DEGREES_TO_RADIANS, 
+                 a.elev * SG_FEET_TO_METER );
+    Point3D cart = sgGeodToCart( geod );
+    a.x = cart.x();
+    a.y = cart.y();
+    a.z = cart.z();
+
+    // get bucket number
+    SGBucket buck(a.lon, a.lat);
+    a.bucket = buck.gen_index();
+
+    a.trans_ident = a.ident;
+    a.approach_failed = false;
+
+    return in >> skipeol;
+}
+
+#endif // _FG_APPROACH_HXX
\ No newline at end of file
diff --git a/src/ATC/approachlist.cxx b/src/ATC/approachlist.cxx
new file mode 100644 (file)
index 0000000..db399e5
--- /dev/null
@@ -0,0 +1,234 @@
+// approachlist.cxx -- Approach data management class
+//
+// Written by Alexander Kappes, started March 2002.
+// Based on navlist.cxx by Curtis Olson, started April 2000.
+//
+// Copyright (C) 2000  Curtis L. Olson - curt@flightgear.org
+//
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sgstream.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+
+#include "approachlist.hxx"
+
+
+FGApproachList *current_approachlist;
+
+
+// Constructor
+FGApproachList::FGApproachList( void ) {
+}
+
+
+// Destructor
+FGApproachList::~FGApproachList( void ) {
+}
+
+
+// load the approach data and build the map
+bool FGApproachList::init( SGPath path ) {
+
+    approachlist_freq.erase( approachlist_freq.begin(), approachlist_freq.end() );
+    approachlist_bck.erase( approachlist_bck.begin(), approachlist_bck.end() );
+
+    sg_gzifstream in( path.str() );
+    if ( !in.is_open() ) {
+        SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
+        exit(-1);
+    }
+
+    // read in each line of the file
+
+    // in >> skipeol;
+    in >> skipcomment;
+
+    cout << " APPROACH " << endl;
+#ifdef __MWERKS__
+    char c = 0;
+    while ( in.get(c) && c != '\0' ) {
+        in.putback(c);
+#else
+    while ( !in.eof() ) {
+#endif
+
+       FGApproach a;
+        in >> a;
+       if ( a.get_type() == '[' ) {
+           break;
+       }
+       //cout << " type = " << a.get_type() << endl;
+       //cout << " lon = " << a.get_lon() << endl;
+       //cout << " lat = " << a.get_lat() << endl;
+       //cout << " elev = " << a.get_elev() << endl;
+       //cout << " freq = " << a.get_freq() << endl;
+       //cout << " Airport Code = " << a.GetIdent() << endl; 
+       //cout << " Name = " << a.get_name() << endl; 
+
+       approachlist_freq[a.get_freq()].push_back(a);
+       approachlist_bck[a.get_bucket()].push_back(a);
+        in >> skipcomment;
+
+    }
+
+    return true;
+}
+
+
+// query the database for the specified frequency, lon and lat are in
+// degrees, elev is in meters
+bool FGApproachList::query_freq( double lon, double lat, double elev, double freq,
+                           FGApproach *a )
+{
+  lon *= SGD_DEGREES_TO_RADIANS;
+  lat *= SGD_DEGREES_TO_RADIANS;
+
+  approach_list_type stations = approachlist_freq[(int)(freq*100.0 + 0.5)];
+  
+  approach_list_iterator current = stations.begin();
+  approach_list_iterator last = stations.end();
+  
+  // double az1, az2, s;
+  Point3D aircraft = sgGeodToCart( Point3D(lon, lat, elev) );
+  Point3D station;
+  double d;
+  for ( ; current != last ; ++current ) {
+    //cout << "testing " << current->GetIdent() << endl;
+    station = Point3D(current->get_x(), current->get_y(), current->get_z());
+    //cout << "aircraft = " << aircraft << endl;
+    //cout << "station = " << station << endl;
+    
+    d = aircraft.distance3Dsquared( station );
+    
+    //cout << "  dist = " << sqrt(d)
+    //     << "  range = " << current->get_range() * SG_NM_TO_METER << endl;
+    //cout << "  Aircraft: lon = " << lon << "  lat = " << lat 
+    //     << "  elev = " << elev << endl;
+    //cout << "  Airport:  lon = " << current->get_lon() 
+    //     << "  lat = " << current->get_lat() 
+    //     << "  elev = " << current->get_elev() 
+    //     << "  z = " << current->get_z() << endl;
+    
+    // match up to twice the published range so we can model
+    // reduced signal strength
+    if ( d < (2 * current->get_range() * SG_NM_TO_METER 
+             * 2 * current->get_range() * SG_NM_TO_METER ) ) {
+      //cout << "matched = " << current->GetIdent() << endl;
+      *a = *current;
+      return true;
+    }
+  }
+  return false;
+}
+
+// query the database for the specified frequency, lon and lat are in
+// degrees, elev is in meters
+bool FGApproachList::query_bck( double lon, double lat, double elev, FGApproach *a, 
+                               int max_app, int &num_app)
+{
+
+  // get bucket number for plane position
+  SGBucket buck(lon, lat);
+
+  //cout << "plane bucket" << bucket << endl;
+
+  // get neigboring buckets
+  double max_range = 100;
+  int bx = int ( max_range*SG_NM_TO_METER / buck.get_width_m() / 2);
+  int by = int ( max_range*SG_NM_TO_METER / buck.get_height_m() / 2 );
+
+  // loop over bucket range 
+  for ( int i=-bx; i<bx; i++) {
+    for ( int j=-by; j<by; j++) {
+      buck = sgBucketOffset(lon, lat, i, j);
+      long int bucket = buck.gen_index();
+      //cout << "bucket number = " << bucket << endl;
+      approach_list_type stations = approachlist_bck[bucket];
+      approach_list_iterator current = stations.begin();
+      approach_list_iterator last = stations.end();
+      double rlon = lon * SGD_DEGREES_TO_RADIANS;
+      double rlat = lat * SGD_DEGREES_TO_RADIANS;
+      // double az1, az2, s;
+      Point3D aircraft = sgGeodToCart( Point3D(rlon, rlat, elev) );
+      Point3D station;
+      double d;
+      for ( ; current != last ; ++current ) {
+       station = Point3D(current->get_x(), current->get_y(), current->get_z());
+       d = aircraft.distance3Dsquared( station );
+       /*
+         cout << "  dist = " << sqrt(d)
+         << "  range = " << current->get_range() * SG_NM_TO_METER << endl;
+         cout << "  Aircraft: lon = " << lon
+         << "  lat = " << lat/SGD_DEGREES_TO_RADIANS 
+         << "  bucket = " << bucket
+         << "  elev = " << elev << endl;
+         cout << "  Airport:  Id = " << current->GetIdent() 
+         << "  lon = " << current->get_lon() 
+         << "  lat = " << current->get_lat() 
+         << "  elev = " << current->get_elev() 
+         << "  bucket = " << current->get_bucket() 
+         << "  z = " << current->get_z() << endl;
+       */
+       // match up to twice the published range so we can model
+       // reduced signal strength
+       if ( d < (current->get_range() * SG_NM_TO_METER 
+                 * current->get_range() * SG_NM_TO_METER ) ) {
+         //cout << "matched = " << current->GetIdent() << endl;
+         if (num_app < max_app) {
+           a[num_app] = *current;
+           num_app += 1;
+         }
+         else {
+           cout << "Approachlist error: Too many stations in range" << endl; 
+         }
+         
+         //return true;
+       }
+      }
+    }
+  }
+  return true; //DCL - added this to prevent a compiler warning
+}
+
+
+bool FGApproachList::get_name( string apt_id )
+{
+  string name;
+  double freq = 125.22;
+
+  approach_list_type stations = approachlist_freq[(int)(freq*100.0 + 0.5)];
+  
+  approach_list_iterator current = stations.begin();
+  approach_list_iterator last    = stations.end();
+
+  if(current != last) {  
+    cout << "ApproachList" << endl;
+    cout << "name" << current->get_name() << endl;
+    cout << "bucket" << current->get_bucket() << endl;
+  }
+
+  return 0;
+
+}
\ No newline at end of file
diff --git a/src/ATC/approachlist.hxx b/src/ATC/approachlist.hxx
new file mode 100644 (file)
index 0000000..cc53bd4
--- /dev/null
@@ -0,0 +1,79 @@
+// approachlist.hxx -- approach management class
+//
+// Written by Alexander Kappes, started March 2002.
+// Based on navlist.hxx by Curtis Olson, started April 2000.
+//
+// Copyright (C) 2000  Curtis L. Olson - curt@flightgear.org
+//
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+
+#ifndef _FG_APPROACHLIST_HXX
+#define _FG_APPROACHLIST_HXX
+
+
+#include <simgear/compiler.h>
+#include <simgear/misc/sg_path.hxx>
+
+#include <map>
+#include <vector>
+
+#include "approach.hxx"
+
+SG_USING_STD(map);
+SG_USING_STD(vector);
+
+
+class FGApproachList {
+
+  // convenience types
+  typedef vector < FGApproach > approach_list_type;
+  typedef approach_list_type::iterator approach_list_iterator;
+  typedef approach_list_type::const_iterator approach_list_const_iterator;
+  
+  // typedef map < int, approach_list_type, less<int> > approach_map_type;
+  typedef map < int, approach_list_type > approach_map_type;
+  typedef approach_map_type::iterator approach_map_iterator;
+  typedef approach_map_type::const_iterator approach_map_const_iterator;
+  
+  approach_map_type approachlist_freq;
+  approach_map_type approachlist_bck;
+  
+public:
+  
+  FGApproachList();
+  ~FGApproachList();
+  
+  // load the approach data and build the map
+  bool init( SGPath path );
+  
+  // query the database for the specified frequency, lon and lat are
+  // in degrees, elev is in meters
+  bool query_freq( double lon, double lat, double elev, double freq, FGApproach *a );
+
+  // query the database for the specified bucket number, lon and lat are
+  // in degrees
+  bool query_bck( double lon, double lat, double elev, FGApproach *a, int max_app, int &num_app );
+
+  bool get_name( string apt_id  );
+
+};
+
+
+extern FGApproachList *current_approachlist;
+
+
+#endif // _FG_APPROACHLIST_HXX
index 1245dad32c9542ad1da7902227098781e60227a6..08e440fec85fa851b3191aa795f11a738637cde2 100644 (file)
@@ -1,4 +1,4 @@
-// atislist.cxx -- navaids management class
+// atislist.cxx -- ATIS data management class
 //
 // Written by David Luff, started October 2001.
 // Based on navlist.cxx by Curtis Olson, started April 2000.
@@ -58,18 +58,14 @@ bool FGATISList::init( SGPath path ) {
 
     // read in each line of the file
 
-    // in >> skipeol;
     in >> skipcomment;
 
-    // double min = 100000;
-    // double max = 0;
-
 #ifdef __MWERKS__
     char c = 0;
     while ( in.get(c) && c != '\0' ) {
         in.putback(c);
 #else
-    while ( ! in.eof() ) {
+    while ( !in.eof() ) {
 #endif
     
         FGATIS a;
@@ -89,19 +85,8 @@ bool FGATISList::init( SGPath path ) {
         atislist[a.get_freq()].push_back(a);
         in >> skipcomment;
 
-       /* if ( a.get_type() != 'N' ) {
-           if ( a.get_freq() < min ) {
-               min = a.get_freq();
-           }
-           if ( a.get_freq() > max ) {
-               max = a.get_freq();
-           }
-       } */
     }
 
-    // cout << "min freq = " << min << endl;
-    // cout << "max freq = " << max << endl;
-
     return true;
 }
 
@@ -111,6 +96,9 @@ bool FGATISList::init( SGPath path ) {
 bool FGATISList::query( double lon, double lat, double elev, double freq,
                       FGATIS *a )
 {
+    lon *= SGD_DEGREES_TO_RADIANS;
+    lat *= SGD_DEGREES_TO_RADIANS;
+
     atis_list_type stations = atislist[(int)(freq*100.0 + 0.5)];
 
     atis_list_iterator current = stations.begin();
diff --git a/src/ATC/ground.cxx b/src/ATC/ground.cxx
new file mode 100644 (file)
index 0000000..d70957e
--- /dev/null
@@ -0,0 +1,80 @@
+// FGGround - a class to provide ground control at larger airports.
+//
+// Written by David Luff, started March 2002.
+//
+// Copyright (C) 2002  David C. Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#include "ground.hxx"
+
+void FGGround::Init() {
+    display = false;
+}
+
+void FGGround::Update() {
+    // 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. 
+}
+
+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.
+}
\ No newline at end of file
diff --git a/src/ATC/ground.hxx b/src/ATC/ground.hxx
new file mode 100644 (file)
index 0000000..18dd100
--- /dev/null
@@ -0,0 +1,150 @@
+// FGGround - a class to provide ground control at larger airports.
+//
+// Written by David Luff, started March 2002.
+//
+// Copyright (C) 2002  David C. Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#ifndef _FG_GROUND_HXX
+#define _FG_GROUND_HXX
+
+#include <vector>
+#include <list>
+//#include <map>
+
+SG_USING_STD(vector);
+SG_USING_STD(list);
+//SG_USING_STD(map);
+
+//////////////////////////////////////////////////////
+// Types for the logical network data structure
+typedef enum arc_type {
+    RUNWAY,
+    TAXIWAY
+};
+
+typedef enum node_type {
+    GATE,
+    APRON,
+    HOLD
+};
+
+typedef struct arc {
+    int distance;
+    char* name;
+    arc_type type;
+};
+
+typedef list<arc> arc_list_type;
+typedef arc_list_type::iterator arc_list_itr;
+typedef arc_list_type::const_iterator arc_list_const_itr; 
+
+typedef struct node {
+    point pos;
+    char* name;
+    node_type node;
+    arc_list arcs;
+};
+
+typedef vector<node> node_array_type;
+typedef node_array_type::iterator node_array_itr;
+typedef node_array_type::const_iterator node_array_const_itr;
+// end logical network types
+///////////////////////////////////////////////////////
+
+// somewhere in the ATC/AI system we are going to have defined something like
+// typedef struct plane_rec
+// list <PlaneRec> plane_rec_list_type
+
+// A more specialist plane rec to include ground information
+typedef struct ground_rec {
+    plane_rec plane;
+    point current_pos;
+    node destination;
+    node last_clearance;
+    bool cleared;  // set true when the plane has been cleared to somewhere
+    bool incoming; //true for arrivals, false for departures
+    // status?
+    // Almost certainly need to add more here
+};
+
+typedef list<ground_rec*> ground_rec_list;
+typedef ground_rec_list::iterator ground_rec_list_itr;
+typedef ground_rec_list::const_iterator ground_rec_list_const_itr;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// FGGround
+//
+///////////////////////////////////////////////////////////////////////////////
+class FGGround : public FGATC {
+
+private:
+
+    // Need a data structure to hold details of the various active planes
+    // Need a data structure to hold details of the logical network
+    // including which gates are filled - or possibly another data structure
+    // with the positions of the inactive planes.
+    // Need a data structure to hold outstanding communications from aircraft.
+    // Possibly need a data structure to hold outstanding communications to aircraft.
+
+    // logical network
+    node_array_type network;
+
+    // Planes currently active
+    ground_rec_list ground_traffic;
+
+public:
+
+    void Init();
+
+    void Update();
+
+    inline void SetDisplay() {display = true;}
+    inline void SetNoDisplay() {display = false;}
+
+    // Its possible that NewArrival and NewDeparture should simply be rolled into Request.
+
+    // 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 NewDeparture(plane_rec plane);
+
+    // 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);
+
+private:
+
+    // Find the shortest route through the logical network between two points.
+    FindShortestRoute(point a, point b);
+
+    // Project a point in WGS84 lat/lon onto the local gnomonic.
+    ConvertWGS84ToXY(sgVec3 wgs84, point xy);
+
+    // Assign a gate or parking location to a new arrival
+    AssignGate(ground_rec &g);
+
+    // Generate the next clearance for an airplane
+    NextClearance(ground_rec &g);
+
+};
+
+#endif  //_FG_GROUND_HXX
\ No newline at end of file
diff --git a/src/ATC/runways.cxx b/src/ATC/runways.cxx
new file mode 100644 (file)
index 0000000..d45b5ab
--- /dev/null
@@ -0,0 +1,496 @@
+// runways.hxx -- a simple class to manage airport runway info
+//
+// Written by Curtis Olson, started August 2000.
+//
+// Copyright (C) 2000  Curtis L. Olson  - curt@flightgear.org
+//
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <math.h>               // fabs()
+#include <stdio.h>              // sprintf()
+
+#include <simgear/compiler.h>
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sgstream.hxx>
+
+#include STL_STRING
+#include STL_FUNCTIONAL
+#include STL_ALGORITHM
+
+#include "runways.hxx"
+
+SG_USING_NAMESPACE(std);
+
+
+FGRunway::FGRunway() {
+}
+
+
+FGRunway::~FGRunway() {
+}
+
+
+FGRunways::FGRunways( const string& file ) {
+    // open the specified database readonly
+    storage = new c4_Storage( file.c_str(), false );
+
+    if ( !storage->Strategy().IsValid() ) {
+       SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << file );
+       exit(-1);
+    }
+
+    vRunway = new c4_View;
+    *vRunway = 
+       storage->GetAs("runway[ID:S,Rwy:S,Longitude:F,Latitude:F,Heading:F,Length:F,Width:F,SurfaceFlags:S,End1Flags:S,End2Flags:S]");
+
+    next_index = 0;
+}
+
+
+// search for the specified apt id
+bool FGRunways::search( const string& aptid, FGRunway* r ) {
+    c4_StringProp pID ("ID");
+    c4_StringProp pRwy ("Rwy");
+    c4_FloatProp pLon ("Longitude");
+    c4_FloatProp pLat ("Latitude");
+    c4_FloatProp pHdg ("Heading");
+    c4_FloatProp pLen ("Length");
+    c4_FloatProp pWid ("Width");
+    c4_StringProp pSurf ("SurfaceFlags");
+    c4_StringProp pEnd1 ("End1Flags");
+    c4_StringProp pEnd2 ("End2Flags");
+
+    int index = vRunway->Find(pID[aptid.c_str()]);
+    // cout << "index = " << index << endl;
+
+    if ( index == -1 ) {
+       return false;
+    }
+
+    next_index = index + 1;
+
+    c4_RowRef row = vRunway->GetAt(index);
+
+    r->id =      (const char *) pID(row);
+    r->rwy_no =  (const char *) pRwy(row);
+    r->lon =     (double) pLon(row);
+    r->lat =     (double) pLat(row);
+    r->heading = (double) pHdg(row);
+    r->length =  (double) pLen(row);
+    r->width =   (double) pWid(row);
+    r->surface_flags = (const char *) pSurf(row);
+    r->end1_flags =    (const char *) pEnd1(row);
+    r->end2_flags =    (const char *) pEnd2(row);
+
+    return true;
+}
+
+
+// search for the specified apt id and runway no
+bool FGRunways::search( const string& aptid, const string& rwyno, FGRunway* r )
+{
+    c4_StringProp pID ("ID");
+    c4_StringProp pRwy ("Rwy");
+    c4_FloatProp pLon ("Longitude");
+    c4_FloatProp pLat ("Latitude");
+    c4_FloatProp pHdg ("Heading");
+    c4_FloatProp pLen ("Length");
+    c4_FloatProp pWid ("Width");
+    c4_StringProp pSurf ("SurfaceFlags");
+    c4_StringProp pEnd1 ("End1Flags");
+    c4_StringProp pEnd2 ("End2Flags");
+
+    int index = vRunway->Find(pID[aptid.c_str()]);
+    // cout << "index = " << index << endl;
+
+    if ( index == -1 ) {
+       return false;
+    }
+
+    c4_RowRef row = vRunway->GetAt(index);
+    string rowid = (const char *) pID(row);
+    string rowrwyno = (const char *) pRwy(row);
+    while ( rowid == aptid ) {
+        next_index = index + 1;
+
+        if ( rowrwyno == rwyno ) {
+            r->id =      (const char *) pID(row);
+            r->rwy_no =  (const char *) pRwy(row);
+            r->lon =     (double) pLon(row);
+            r->lat =     (double) pLat(row);
+            r->heading = (double) pHdg(row);
+            r->length =  (double) pLen(row);
+            r->width =   (double) pWid(row);
+            r->surface_flags = (const char *) pSurf(row);
+            r->end1_flags =    (const char *) pEnd1(row);
+            r->end2_flags =    (const char *) pEnd2(row);
+
+            return true;
+        }
+
+        index++;
+        c4_RowRef row = vRunway->GetAt(index);
+        string rowid = (const char *) pID(row);
+        string rowrwyno = (const char *) pRwy(row);
+    }
+
+    return false;
+}
+
+
+FGRunway FGRunways::search( const string& aptid ) {
+    FGRunway a;
+    search( aptid, &a );
+    return a;
+}
+
+
+// search for the specified id
+bool FGRunways::next( FGRunway* r ) {
+    c4_StringProp pID ("ID");
+    c4_StringProp pRwy ("Rwy");
+    c4_FloatProp pLon ("Longitude");
+    c4_FloatProp pLat ("Latitude");
+    c4_FloatProp pHdg ("Heading");
+    c4_FloatProp pLen ("Length");
+    c4_FloatProp pWid ("Width");
+    c4_StringProp pSurf ("SurfaceFlags");
+    c4_StringProp pEnd1 ("End1Flags");
+    c4_StringProp pEnd2 ("End2Flags");
+
+    int size = vRunway->GetSize();
+    // cout << "total records = " << size << endl;
+
+    int index = next_index;
+    // cout << "index = " << index << endl;
+
+    if ( index == -1 || index >= size ) {
+       return false;
+    }
+
+    next_index = index + 1;
+
+    c4_RowRef row = vRunway->GetAt(index);
+    r->id =      (const char *) pID(row);
+    r->rwy_no =  (const char *) pRwy(row);
+    r->lon =     (double) pLon(row);
+    r->lat =     (double) pLat(row);
+    r->heading = (double) pHdg(row);
+    r->length =  (double) pLen(row);
+    r->width =   (double) pWid(row);
+    r->surface_flags = (const char *) pSurf(row);
+    r->end1_flags =    (const char *) pEnd1(row);
+    r->end2_flags =    (const char *) pEnd2(row);
+
+    return true;
+}
+
+
+// Return the runway closest to a given heading
+bool FGRunways::search( const string& aptid, const int tgt_hdg,
+                        FGRunway* runway )
+{
+    FGRunway r;
+    FGRunway br;
+    double found_dir = 0.0;  
+    if ( !search( aptid, &r ) ) {
+       SG_LOG( SG_GENERAL, SG_ALERT,
+                "Failed to find " << aptid << " in database." );
+       return false;
+    }
+
+    double diff;
+    double min_diff = 360.0;
+    
+    while ( r.id == aptid ) {
+       // forward direction
+       diff = tgt_hdg - r.heading;
+       while ( diff < -180.0 ) { diff += 360.0; }
+       while ( diff >  180.0 ) { diff -= 360.0; }
+       diff = fabs(diff);
+        // SG_LOG( SG_GENERAL, SG_INFO,
+        //        "Runway " << r.rwy_no << " heading = " << r.heading <<
+        //        " diff = " << diff );
+       if ( diff < min_diff ) {
+           min_diff = diff;
+           //runway = &r;
+           br = r;
+           found_dir = 0;
+       }
+       
+       // reverse direction
+       diff = tgt_hdg - r.heading - 180.0;
+       while ( diff < -180.0 ) { diff += 360.0; }
+       while ( diff >  180.0 ) { diff -= 360.0; }
+       diff = fabs(diff);
+        // SG_LOG( SG_GENERAL, SG_INFO,
+        //        "Runway -" << r.rwy_no << " heading = " <<
+        //        r.heading + 180.0 <<
+        //        " diff = " << diff );
+       if ( diff < min_diff ) {
+           min_diff = diff;
+           //runway = &r;
+           br = r;
+           found_dir = 180.0;
+       }
+       next( &r );
+    }
+    
+    // SG_LOG( SG_GENERAL, SG_INFO, "closest runway = " << runway->rwy_no
+    //        << " + " << found_dir );
+
+    *runway = br;    
+    double heading = runway->heading + found_dir;
+    while ( heading >= 360.0 ) { heading -= 360.0; }
+    runway->heading = heading;
+    //cout << runway->heading << " " << runway->rwy_no 
+    // << " " << runway->id << endl;
+    return true;
+}
+
+
+// Return the runway number of the runway closest to a given heading
+string FGRunways::search( const string& aptid, const int tgt_hdg ) {
+    FGRunway r;
+    string rn;
+    double found_dir = 0.0;  
+    if ( !search( aptid, &r ) ) {
+       SG_LOG( SG_GENERAL, SG_ALERT,
+                "Failed to find " << aptid << " in database." );
+       return "NN";
+    }
+    
+    double diff;
+    double min_diff = 360.0;
+    
+    while ( r.id == aptid ) {
+       // forward direction
+       diff = tgt_hdg - r.heading;
+       while ( diff < -180.0 ) { diff += 360.0; }
+       while ( diff >  180.0 ) { diff -= 360.0; }
+       diff = fabs(diff);
+        // SG_LOG( SG_GENERAL, SG_INFO,
+        //        "Runway " << r.rwy_no << " heading = " << r.heading <<
+        //        " diff = " << diff );
+       if ( diff < min_diff ) {
+           min_diff = diff;
+           rn = r.rwy_no;
+           found_dir = 0;
+       }
+       
+       // reverse direction
+       diff = tgt_hdg - r.heading - 180.0;
+       while ( diff < -180.0 ) { diff += 360.0; }
+       while ( diff >  180.0 ) { diff -= 360.0; }
+       diff = fabs(diff);
+        // SG_LOG( SG_GENERAL, SG_INFO,
+        //        "Runway -" << r.rwy_no << " heading = " <<
+        //        r.heading + 180.0 <<
+        //        " diff = " << diff );
+       if ( diff < min_diff ) {
+           min_diff = diff;
+           rn = r.rwy_no;
+           found_dir = 180.0;
+       }
+       
+       next( &r );
+    }
+    
+    // SG_LOG( SG_GENERAL, SG_INFO, "closest runway = " << r.rwy_no
+    //        << " + " << found_dir );
+    // rn = r.rwy_no;
+    // cout << "In search, rn = " << rn << endl;
+    if ( found_dir == 180 ) {
+       int irn = atoi(rn.c_str());
+       irn += 18;
+       if ( irn > 36 ) {
+           irn -= 36;
+       }
+       char buf[4];            // 2 chars + string terminator + 1 for safety
+       sprintf(buf, "%i", irn);
+       rn = buf;
+    }  
+    
+    return rn;
+}
+
+
+// Destructor
+FGRunways::~FGRunways( void ) {
+    delete storage;
+}
+
+
+// Constructor
+FGRunwaysUtil::FGRunwaysUtil() {
+}
+
+
+// load the data
+int FGRunwaysUtil::load( const string& file ) {
+    FGRunway r;
+    string apt_id;
+
+    runways.erase( runways.begin(), runways.end() );
+
+    sg_gzifstream in( file );
+    if ( !in.is_open() ) {
+       SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << file );
+       exit(-1);
+    }
+
+    // skip first line of file
+    char tmp[2048];
+    in.getline( tmp, 2048 );
+
+    // read in each line of the file
+
+#ifdef __MWERKS__
+
+    in >> ::skipws;
+    char c = 0;
+    while ( in.get(c) && c != '\0' ) {
+       if ( c == 'A' ) {
+           in >> apt_id;
+           in >> skipeol;
+       } else if ( c == 'R' ) {
+           in >> r;
+           r.id = apt_id;
+           runways.push_back(r);
+       } else {
+           in >> skipeol;
+       }
+       in >> ::skipws;
+    }
+
+#else
+
+    in >> ::skipws;
+    while ( ! in.eof() ) {
+       char c = 0;
+       in.get(c);
+       if ( c == 'A' ) {
+           in >> apt_id;
+           in >> skipeol;
+       } else if ( c == 'R' ) {
+           in >> r;
+           r.id = apt_id;
+           // cout << apt_id << " " << r.rwy_no << endl;
+           runways.push_back(r);
+       } else {
+           in >> skipeol;
+       }
+       in >> ::skipws;
+    }
+
+#endif
+
+    return 1;
+}
+
+
+// save the data in gdbm format
+bool FGRunwaysUtil::dump_mk4( const string& file ) {
+    // open database for writing
+    c4_Storage storage( file.c_str(), true );
+
+    // need to do something about error handling here!
+
+    // define the properties
+    c4_StringProp pID ("ID");
+    c4_StringProp pRwy ("Rwy");
+    c4_FloatProp pLon ("Longitude");
+    c4_FloatProp pLat ("Latitude");
+    c4_FloatProp pHdg ("Heading");
+    c4_FloatProp pLen ("Length");
+    c4_FloatProp pWid ("Width");
+    c4_StringProp pSurf ("SurfaceFlags");
+    c4_StringProp pEnd1 ("End1Flags");
+    c4_StringProp pEnd2 ("End2Flags");
+
+    // Start with an empty view of the proper structure.
+    c4_View vRunway =
+       storage.GetAs("runway[ID:S,Rwy:S,Longitude:F,Latitude:F,Heading:F,Length:F,Width:F,SurfaceFlags:S,End1Flags:S,End2Flags:S]");
+
+    c4_Row row;
+
+    iterator current = runways.begin();
+    const_iterator end = runways.end();
+    while ( current != end ) {
+       // add each runway record
+       pID (row) = current->id.c_str();
+       pRwy (row) = current->rwy_no.c_str();
+       pLon (row) = current->lon;
+       pLat (row) = current->lat;
+       pHdg (row) = current->heading;
+       pLen (row) = current->length;
+       pWid (row) = current->width;
+       pSurf (row) = current->surface_flags.c_str();
+       pEnd1 (row) = current->end1_flags.c_str();
+       pEnd2 (row) = current->end2_flags.c_str();
+       vRunway.Add(row);
+
+       ++current;
+    }
+
+    // commit our changes
+    storage.Commit();
+
+    return true;
+}
+
+
+#if 0
+// search for the specified id
+bool
+FGRunwaysUtil::search( const string& id, FGRunway* a ) const
+{
+    const_iterator it = runways.find( FGRunway(id) );
+    if ( it != runways.end() )
+    {
+       *a = *it;
+       return true;
+    }
+    else
+    {
+       return false;
+    }
+}
+
+
+FGRunway
+FGRunwaysUtil::search( const string& id ) const
+{
+    FGRunway a;
+    this->search( id, &a );
+    return a;
+}
+#endif
+
+// Destructor
+FGRunwaysUtil::~FGRunwaysUtil( void ) {
+}
+
+
diff --git a/src/ATC/tower.cxx b/src/ATC/tower.cxx
new file mode 100644 (file)
index 0000000..e22713e
--- /dev/null
@@ -0,0 +1,52 @@
+// FGTower - a class to provide tower control at towered airports.
+//
+// Written by David Luff, started March 2002.
+//
+// Copyright (C) 2002  David C. Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#include <Main/globals.hxx>
+
+#include "tower.hxx"
+#include "ATCdisplay.hxx"
+
+FGTower::FGTower() {
+}
+
+FGTower::~FGTower() {
+}
+
+void FGTower::Init() {
+    display = false;
+}
+
+void FGTower::Update() {
+    // 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.
+
+    // Hardwired for testing
+    static int play = 0;
+    if(play == 200) {
+       //cout << "Registering message in tower.cxx ****************************\n";
+       //globals->get_ATC_display()->RegisterSingleMessage((string)"Cessna eight-two-zero Cleared for takeoff", 2);
+    }
+    ++play;
+}
diff --git a/src/ATC/tower.hxx b/src/ATC/tower.hxx
new file mode 100644 (file)
index 0000000..2d66b09
--- /dev/null
@@ -0,0 +1,148 @@
+// FGTower - a class to provide tower control at towered airports.
+//
+// Written by David Luff, started March 2002.
+//
+// Copyright (C) 2002  David C. Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#ifndef _FG_TOWER_HXX
+#define _FG_TOWER_HXX
+
+#include <simgear/compiler.h>
+#include <simgear/math/point3d.hxx>
+#include <simgear/misc/sgstream.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+#include <plib/sg.h>
+
+#include <string>
+
+SG_USING_STD(string);
+
+#include "ATC.hxx"
+
+//DCL - a complete guess for now.
+#define FG_TOWER_DEFAULT_RANGE 30
+
+// somewhere in the ATC/AI system we are going to have defined something like
+// struct plane_rec
+// list <PlaneRec> plane_rec_list_type
+
+class FGTower : public FGATC {
+
+private:
+
+    // Need a data structure to hold details of the various active planes
+    // or possibly another data structure with the positions of the inactive planes.
+    // Need a data structure to hold outstanding communications from aircraft.
+
+
+public:
+
+    FGTower();
+    ~FGTower();
+
+    void Init();
+
+    void Update();
+
+    inline void SetDisplay() {display = true;}
+    inline void SetNoDisplay() {display = false;}
+
+    inline char get_type() const { return type; }
+    inline double get_lon() const { return lon; }
+    inline double get_lat() const { return lat; }
+    inline double get_elev() const { return elev; }
+    inline double get_x() const { return x; }
+    inline double get_y() const { return y; }
+    inline double get_z() const { return z; }
+    inline int get_freq() const { return freq; }
+    inline int get_range() const { return range; }
+    inline const char* GetIdent() { return ident.c_str(); }
+    inline string get_trans_ident() { return trans_ident; }
+    inline atc_type GetType() { return TOWER; }
+
+    // Make a request of tower control
+    //void Request(tower_request request);
+
+private:
+
+    char type;
+    double lon, lat;
+    double elev;
+    double x, y, z;
+    int freq;
+    int range;
+    bool display;              // Flag to indicate whether we should be outputting to the ATC display.
+    bool displaying;           // Flag to indicate whether we are outputting to the ATC display.
+    string ident;              // Code of the airport its at.
+    string name;               // Name generally used in transmissions.
+
+    // for failure modeling
+    string trans_ident;                // transmitted ident
+    bool tower_failed;         // tower failed?
+
+    friend istream& operator>> ( istream&, FGTower& );
+};
+
+
+inline istream&
+operator >> ( istream& in, FGTower& t )
+{
+    double f;
+    char ch;
+
+    in >> t.type;
+    
+    if ( t.type == '[' )
+      return in >> skipeol;
+
+    in >> t.lat >> t.lon >> t.elev >> f >> t.range 
+       >> t.ident;
+
+    t.name = "";
+    in >> ch;
+    t.name += ch;
+    while(1) {
+       //in >> noskipws
+       in.unsetf(ios::skipws);
+       in >> ch;
+       t.name += ch;
+       if((ch == '"') || (ch == 0x0A)) {
+           break;
+       }   // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the "
+    }
+    in.setf(ios::skipws);
+    //cout << "tower.name = " << t.name << '\n';
+
+    t.freq = (int)(f*100.0 + 0.5);
+
+    // cout << t.ident << endl;
+
+    // generate cartesian coordinates
+    Point3D geod( t.lon * SGD_DEGREES_TO_RADIANS, t.lat * SGD_DEGREES_TO_RADIANS, t.elev );
+    Point3D cart = sgGeodToCart( geod );
+    t.x = cart.x();
+    t.y = cart.y();
+    t.z = cart.z();
+
+    t.trans_ident = t.ident;
+    t.tower_failed = false;
+
+    return in >> skipeol;
+}
+
+
+#endif  //_FG_TOWER_HXX
\ No newline at end of file
diff --git a/src/ATC/towerlist.cxx b/src/ATC/towerlist.cxx
new file mode 100644 (file)
index 0000000..ba3b4c7
--- /dev/null
@@ -0,0 +1,134 @@
+// towerlist.cxx -- ATC Tower data management class
+//
+// Written by David Luff, started March 2002.
+// Based on navlist.cxx by Curtis Olson, started April 2000.
+//
+// Copyright (C) 2000  Curtis L. Olson - curt@flightgear.org
+//
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sgstream.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/math/sg_random.h>
+
+#include "towerlist.hxx"
+
+FGTowerList *current_towerlist;
+
+// Constructor
+FGTowerList::FGTowerList( void ) {
+}
+
+
+// Destructor
+FGTowerList::~FGTowerList( void ) {
+}
+
+
+// load the navaids and build the map
+bool FGTowerList::init( SGPath path ) {
+
+    towerlist.erase( towerlist.begin(), towerlist.end() );
+
+    sg_gzifstream in( path.str() );
+    if ( !in.is_open() ) {
+        SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
+        exit(-1);
+    }
+
+    // read in each line of the file
+
+    in >> skipcomment;
+
+#ifdef __MWERKS__
+    char c = 0;
+    while ( in.get(c) && c != '\0' ) {
+        in.putback(c);
+#else
+    while ( !in.eof() ) {
+#endif
+
+       FGTower t;
+        in >> t;
+       if ( t.get_type() == '[' ) {
+           break;
+       }
+
+       //cout << "id = " << t.GetIdent() << endl;
+       //cout << " type = " << t.get_type() << endl;
+       //cout << " lon = " << t.get_lon() << endl;
+       //cout << " lat = " << t.get_lat() << endl;
+       //cout << " elev = " << t.get_elev() << endl;
+       //cout << " freq = " << t.get_freq() << endl;
+       //cout << " range = " << t.get_range() << endl;
+
+       towerlist[t.get_freq()].push_back(t);
+        in >> skipcomment;
+    }
+
+    return true;
+}
+
+
+// query the database for the specified frequency, lon and lat are in
+// degrees, elev is in meters
+bool FGTowerList::query( double lon, double lat, double elev, double freq,
+                      FGTower *t )
+{
+    lon *= SGD_DEGREES_TO_RADIANS;
+    lat *= SGD_DEGREES_TO_RADIANS;
+    //cout << "lon = " << lon << '\n';
+    //cout << "lat = " << lat << '\n';
+    //cout << "elev = " << elev << '\n';
+    //cout << "freq = " << freq << '\n';
+
+    tower_list_type stations = towerlist[(int)(freq*100.0 + 0.5)];
+
+    tower_list_iterator current = stations.begin();
+    tower_list_iterator last = stations.end();
+
+    // double az1, az2, s;
+    Point3D aircraft = sgGeodToCart( Point3D(lon, lat, elev) );
+    Point3D station;
+    double d;
+    for ( ; current != last ; ++current ) {
+       //cout << "testing " << current->GetIdent() << endl;
+       station = Point3D(current->get_x(), current->get_y(), current->get_z());
+       //cout << "aircraft = " << aircraft << endl;
+       //cout << "station = " << station << endl;
+
+       d = aircraft.distance3Dsquared( station );
+
+       //cout << "  dist = " << sqrt(d)
+       //     << "  range = " << current->get_range() * SG_NM_TO_METER << endl;
+
+       // match up to twice the published range so we can model
+       // reduced signal strength
+       if ( d < (2 * current->get_range() * SG_NM_TO_METER 
+                 * 2 * current->get_range() * SG_NM_TO_METER ) ) {
+           //cout << "matched = " << current->GetIdent() << endl;
+           *t = *current;
+           return true;
+       }
+    }
+
+    return false;
+}
\ No newline at end of file
diff --git a/src/ATC/towerlist.hxx b/src/ATC/towerlist.hxx
new file mode 100644 (file)
index 0000000..5dc6e2f
--- /dev/null
@@ -0,0 +1,74 @@
+// towerlist.hxx -- ATC Tower data management class
+//
+// Written by David Luff, started March 2002.
+// Based on navlist.hxx by Curtis Olson, started April 2000.
+//
+// Copyright (C) 2000  Curtis L. Olson - curt@flightgear.org
+//
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+#ifndef _FG_TOWERLIST_HXX
+#define _FG_TOWERLIST_HXX
+
+
+#include <simgear/compiler.h>
+#include <simgear/misc/sg_path.hxx>
+
+#include <map>
+#include <vector>
+#include <string>
+
+#include "tower.hxx"
+
+SG_USING_STD(map);
+SG_USING_STD(vector);
+SG_USING_STD(string);
+
+
+class FGTowerList {
+
+    // convenience types
+    typedef vector < FGTower > tower_list_type;
+    typedef tower_list_type::iterator tower_list_iterator;
+    typedef tower_list_type::const_iterator tower_list_const_iterator;
+
+    // Map containing FGTower keyed by frequency.
+    // A vector of FGTower is kept at each node since some may share frequency
+    typedef map < int, tower_list_type > tower_map_type;
+    typedef tower_map_type::iterator tower_map_iterator;
+    typedef tower_map_type::const_iterator tower_map_const_iterator;
+
+    tower_map_type towerlist;
+
+public:
+
+    FGTowerList();
+    ~FGTowerList();
+
+    // load all atis and build the map
+    bool init( SGPath path );
+
+    // query the database for the specified frequency, lon and lat are
+    // in degrees, elev is in meters
+    bool query( double lon, double lat, double elev, double freq, FGTower *t );
+
+};
+
+
+extern FGTowerList *current_towerlist;
+
+
+#endif // _FG_TOWERLIST_HXX
\ No newline at end of file
index 4f4abe56602633110cda9e052a61c32267e30434..4dae0a6ab7293a8630827bcd68cd63b903a9bc7b 100644 (file)
@@ -73,6 +73,9 @@
 #include <ATC/ATCdisplay.hxx>
 #include <ATC/ATCmgr.hxx>
 #include <ATC/atislist.hxx>
+#include <ATC/towerlist.hxx>
+#include <ATC/approachlist.hxx>
+#include <ATC/AIMgr.hxx>
 #include <Autopilot/auto_gui.hxx>
 #include <Autopilot/newauto.hxx>
 #include <Cockpit/cockpit.hxx>
@@ -856,13 +859,24 @@ bool fgInitSubsystems( void ) {
     // Initialize ATC list management and query systems
     ////////////////////////////////////////////////////////////////////
 
-    //DCL
     SG_LOG(SG_GENERAL, SG_INFO, "  ATIS");
     current_atislist = new FGATISList;
     SGPath p_atis( globals->get_fg_root() );
     p_atis.append( "ATC/default.atis" );
     current_atislist->init( p_atis );
 
+    SG_LOG(SG_GENERAL, SG_INFO, "  Tower");
+    current_towerlist = new FGTowerList;
+    SGPath p_tower( globals->get_fg_root() );
+    p_tower.append( "ATC/default.tower" );
+    current_towerlist->init( p_tower );
+
+    SG_LOG(SG_GENERAL, SG_INFO, "  Approach");
+    current_approachlist = new FGApproachList;
+    SGPath p_approach( globals->get_fg_root() );
+    p_approach.append( "ATC/default.approach" );
+    current_approachlist->init( p_approach );
+
     ////////////////////////////////////////////////////////////////////
     // Initialise ATC display system
     ////////////////////////////////////////////////////////////////////
@@ -877,7 +891,15 @@ bool fgInitSubsystems( void ) {
 
     SG_LOG(SG_GENERAL, SG_INFO, "  ATC Manager");
     globals->set_ATC_mgr(new FGATCMgr);
-    globals->get_ATC_mgr()->init();     
+    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();     
 
     ////////////////////////////////////////////////////////////////////
     // Initialize the built-in commands.
index 75c119313b6cab8b4f1f28f9420a370101b6d340..4bcdf73e9b641e0cf311e9c05fbf448bcb4eccee 100644 (file)
@@ -64,6 +64,7 @@ class FGFX;
 class FGViewer;
 class FGATCMgr;
 class FGATCDisplay;
+class FGAIMgr;
 
 
 /**
@@ -134,6 +135,9 @@ private:
     // ATC Renderer
     FGATCDisplay *ATC_display;
 
+    // AI manager
+    FGAIMgr *AI_mgr;
+
     // control input state
     FGControls *controls;
 
@@ -215,7 +219,10 @@ public:
     inline void set_ATC_mgr( FGATCMgr *a ) {ATC_mgr = a; }
 
     inline FGATCDisplay *get_ATC_display() const { return ATC_display; }
-    inline void set_ATC_display( FGATCDisplay *d ) {ATC_display = d; }  
+    inline void set_ATC_display( FGATCDisplay *d ) {ATC_display = d; } 
+    
+    inline FGAIMgr *get_AI_mgr() const { return AI_mgr; }
+    inline void set_AI_mgr( FGAIMgr *a ) {AI_mgr = a; }
 
     inline FGSoundMgr *get_soundmgr() const { return soundmgr; }
     inline void set_soundmgr( FGSoundMgr *sm ) { soundmgr = sm; }
index ef9d70bf7ba17005a8b2a65464f7cb840c45ef66..7ee6c4531456d0f6bec4219d4a975f9f62d0ae3c 100644 (file)
@@ -81,6 +81,8 @@
 
 #include <ATC/ATCmgr.hxx>
 #include <ATC/ATCdisplay.hxx>
+#include <ATC/AIMgr.hxx>
+
 #include <Autopilot/newauto.hxx>
 
 #include <Cockpit/cockpit.hxx>
@@ -1045,7 +1047,10 @@ static void fgMainLoop( void ) {
 #endif
 
     // Run ATC subsystem
-    globals->get_ATC_mgr()->update(1);  // FIXME: use real dt
+    globals->get_ATC_mgr()->update(1); // FIXME - use real dt.
+
+    // Run the AI subsystem
+    globals->get_AI_mgr()->update(1);  // FIXME - use real dt.
 
     // Run flight model