]> git.mxchange.org Git - flightgear.git/commitdiff
Preliminary support for AI planes from Dave Luff. This works only at
authordavid <david>
Wed, 2 Oct 2002 15:27:49 +0000 (15:27 +0000)
committerdavid <david>
Wed, 2 Oct 2002 15:27:49 +0000 (15:27 +0000)
KEMT (w120n30 scenery), and you will have to set the property
/sim/ai-traffic/enabled to 'true' to see the other plane (and tune
comm1 to 121.2 to hear the other plane's radio calls).

18 files changed:
src/ATC/AIEntity.cxx
src/ATC/AIEntity.hxx
src/ATC/AILocalTraffic.cxx
src/ATC/AILocalTraffic.hxx
src/ATC/AIMgr.cxx
src/ATC/AIMgr.hxx
src/ATC/AIPlane.cxx [new file with mode: 0644]
src/ATC/AIPlane.hxx [new file with mode: 0644]
src/ATC/ATCProjection.cxx [new file with mode: 0644]
src/ATC/ATCProjection.hxx [new file with mode: 0644]
src/ATC/ATCmgr.cxx
src/ATC/ATCmgr.hxx
src/ATC/ATCutils.cxx
src/ATC/ATCutils.hxx
src/ATC/Makefile.am
src/ATC/approachlist.cxx
src/ATC/atis.cxx
src/ATC/tower.hxx

index f3b06ff9cf904f5cb9b2f9368dcf2baf5b5c31ee..9ca86e22021b3cb7b66d537448100c119f3c42da 100644 (file)
 FGAIEntity::~FGAIEntity() {
 }
 
+void FGAIEntity::Update(double dt) {
+}
+
 // Run the internal calculations
-void FGAIEntity::Update() {
+//void FGAIEntity::Update() {
+void FGAIEntity::Transform() {
+    aip.setPosition(pos.lon(), pos.lat(), pos.elev() * SG_METER_TO_FEET);
+    aip.setOrientation(roll, pitch, hdg);
+    aip.update();    
 }
 
+/*
 void FGAIEntity::Transform() {
 
     // Translate moving object w.r.t eye
@@ -53,17 +61,17 @@ void FGAIEntity::Transform() {
     sgCoord shippos;
     FastWorldCoordinate(&shippos, sc);
     position->setTransform( &shippos );
-    globals->get_scenery()->get_scene_graph()->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 geod( pos.lon() * SGD_DEGREES_TO_RADIANS,
+                  pos.lat() * SGD_DEGREES_TO_RADIANS,
+                  pos.elev() );
        
     Point3D world_pos = sgGeodToCart( geod );
     Point3D offset = world_pos - center;
@@ -89,15 +97,15 @@ void FGAIEntity::WorldCoordinate(sgCoord *obj_pos, Point3D center) {
     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 lon_rad = pos.lon() * SGD_DEGREES_TO_RADIANS;
+    double lat_rad = pos.lat() * SGD_DEGREES_TO_RADIANS;
     double hdg_rad = hdg * SGD_DEGREES_TO_RADIANS;
 
     // setup transforms
-    Point3D geod( lon_rad, lat_rad, elev );
+    Point3D geod( lon_rad, lat_rad, pos.elev() );
        
     Point3D world_pos = sgGeodToCart( geod );
     Point3D offset = world_pos - center;
@@ -133,3 +141,4 @@ void FGAIEntity::FastWorldCoordinate(sgCoord *obj_pos, Point3D center) {
 
     sgSetCoord( obj_pos, mat );
 }
+*/
index c7262c8f17ce27d137dd849e106914540c56fe1f..f8ad8131a85f6941838b06829b59ed6abcaf358c 100644 (file)
 #ifndef _FG_AIEntity_HXX
 #define _FG_AIEntity_HXX
 
+#include <Model/model.hxx>
 #include <plib/sg.h>
 #include <plib/ssg.h>
 #include <simgear/math/point3d.hxx>
 
+
+/*****************************************************************
+*
+*  FGAIEntity - this class implements the minimum requirement
+*  for any AI entity - a position, an orientation, an associated
+*  3D model, and the ability to be moved.  It does nothing useful
+*  and all AI entities are expected to be derived from it.
+*
+******************************************************************/
 class FGAIEntity {
 
 public:
@@ -44,27 +54,29 @@ public:
     virtual ~FGAIEntity();
 
     // Run the internal calculations
-    virtual void Update();
+    virtual void Update(double dt);
 
 protected:
 
-    double lat;                //WGS84
-    double lon;                //WGS84
-    double elev;       //Meters
+    Point3D pos;       // WGS84 lat & lon in degrees, elev above sea-level in meters
+    //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
+    FGModelPlacement aip;
 
-    ssgEntity* model;
-    ssgTransform* position;
+    //ssgEntity* model;
+    //ssgTransform* position;
 
     void Transform();
 
     //void WorldCoordinate(sgCoord *obj_pos, Point3D center);
 
-    void FastWorldCoordinate(sgCoord *obj_pos, Point3D center);
+    //void FastWorldCoordinate(sgCoord *obj_pos, Point3D center);
 
 };
 
index a705d753dea9575cf037ea4592cbf9476796b88f..a4e1b821548b62a3198eaa4e1a31be8ba72cb1e7 100644 (file)
 // 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 <Model/loader.hxx>
-//#include <simgear/constants.h>
+#include <Main/location.hxx>
+#include <Scenery/scenery.hxx>
 #include <simgear/math/point3d.hxx>
 #include <simgear/math/sg_geodesy.hxx>
 #include <simgear/misc/sg_path.hxx>
 #include <string>
+#include <math.h>
 
 SG_USING_STD(string);
 
 #include "ATCmgr.hxx"
 #include "AILocalTraffic.hxx"
+#include "ATCutils.hxx"
 
 FGAILocalTraffic::FGAILocalTraffic() {
+    //Hardwire initialisation for now - a lot of this should be read in from config eventually
+    Vr = 70.0;
+    best_rate_of_climb_speed = 70.0;
+    //best_rate_of_climb;
+    //nominal_climb_speed;
+    //nominal_climb_rate;
+    //nominal_circuit_speed;
+    //min_circuit_speed;
+    //max_circuit_speed;
+    nominal_descent_rate = 500.0;
+    nominal_final_speed = 65.0;
+    //nominal_approach_speed;
+    //stall_speed_landing_config;   
+    wind_from_hdg = 0.0;
+    wind_speed_knots = 0.0; 
 }
 
 FGAILocalTraffic::~FGAILocalTraffic() {
@@ -49,34 +58,75 @@ FGAILocalTraffic::~FGAILocalTraffic() {
 void FGAILocalTraffic::Init() {
     // Hack alert - Hardwired path!!
     string planepath = "Aircraft/c172/Models/c172-dpm.ac";
-    model = globals->get_model_loader()->load_model(planepath);
-    if (model == 0) {
-       model =
-            globals->get_model_loader()
-            ->load_model("Models/Geometry/glider.ac");
-       if (model == 0)
-           cout << "Failed to load an aircraft model in AILocalTraffic\n";
+    SGPath path = globals->get_fg_root();
+    path.append(planepath);
+    aip.init(planepath.c_str());
+    aip.setVisible(true);
+    globals->get_scenery()->get_scene_graph()->addKid(aip.getSceneGraph());
+
+#define DCL_KEMT true
+//#define DCL_KPAO true
+#ifdef DCL_KEMT
+    // Hardwire to KEMT for now
+    // Hardwired points at each end of KEMT runway
+    Point3D P010(-118.037483, 34.081358, 296 * SG_FEET_TO_METER);
+    Point3D P190(-118.032308, 34.090456, 299.395263 * SG_FEET_TO_METER);
+    Point3D takeoff_end;
+    bool d010 = true;  // use this to change the hardwired runway direction
+    if(d010) {
+       rwy.threshold_pos = P010;
+       takeoff_end = P190;
+       rwy.hdg = 25.32;        //from default.apt
+       patternDirection = -1;  // Left
+       pos.setelev(rwy.threshold_pos.elev() + (-8.5 * SG_FEET_TO_METER));  // This is a complete hack - the rendered runway takes the underlying scenery elev rather than the published runway elev so I should use height above terrain or something.
     } else {
-       cout << "AILocal Traffic Model loaded successfully\n";
+       rwy.threshold_pos = P190;
+       takeoff_end = P010;
+       rwy.hdg = 205.32;
+       patternDirection = 1;   // Right
+       pos.setelev(rwy.threshold_pos.elev() + (-0.0 * SG_FEET_TO_METER));  // This is a complete hack - the rendered runway takes the underlying scenery elev rather than the published runway elev so I should use height above terrain or something.
     }
-    position = new ssgTransform;
-    position->addKid(model);
-
-    // Hardwire to KEMT
-    lat = 34.081358;
-    lon = -118.037483;
-    hdg = 0.0;
-    elev = (287.0 + 0.5) * SG_FEET_TO_METER;  // Ground is 296 so this should be above it
-    mag_hdg = -10.0;
+#else  
+    //KPAO - might be a better choice since its in the default scenery
+    //Hardwire it to the default (no wind) direction
+    Point3D threshold_end(-122.1124358, 37.45848783, 6.8 * SG_FEET_TO_METER);  // These positions are from airnav.com and don't quite seem to correspond with the sim scenery
+    Point3D takeoff_end(-122.1176522, 37.463752, 6.7 * SG_FEET_TO_METER);
+    rwy.threshold_pos = threshold_end;
+    rwy.hdg = 315.0;
+    patternDirection = 1;      // Right
+    pos.setelev(rwy.threshold_pos.elev() + (-0.0 * SG_FEET_TO_METER));  // This is a complete hack - the rendered runway takes the underlying scenery elev rather than the published runway elev so I should use height above terrain or something.
+#endif
+
+    //rwy.threshold_pos.setlat(34.081358);
+    //rwy.threshold_pos.setlon(-118.037483);
+    //rwy.mag_hdg = 12.0;
+    //rwy.mag_var = 14.0;
+    //rwy.hdg = rwy.mag_hdg + rwy.mag_var;
+    //rwy.threshold_pos.setelev(296 * SG_FEET_TO_METER);
+
+    // Initial position on threshold for now
+    // TODO - check wind / default runway
+    pos.setlat(rwy.threshold_pos.lat());
+    pos.setlon(rwy.threshold_pos.lon());
+    hdg = rwy.hdg;
+    
     pitch = 0.0;
     roll = 0.0;
-    mag_var = -14.0;
+    leg = TAKEOFF_ROLL;
+    vel = 0.0;
+    slope = 0.0;
+
+    // Now set the position of the plane and then re-get the elevation!! (Didn't work - elev always returned as zero) :-(
+    //aip.setPosition(pos.lon(), pos.lat(), pos.elev() * SG_METER_TO_FEET);
+    //cout << "*********************** elev in FGAILocalTraffic = " << aip.getFGLocation()->get_cur_elev_m() << '\n';
 
     // 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
+           freq = (double)tower->get_freq() / 100.0;
+           //cout << "***********************************AILocalTraffic freq = " << freq << '\n';
        } else {
            // Check CTAF, unicom etc
        }
@@ -84,18 +134,254 @@ void FGAILocalTraffic::Init() {
        cout << "Unable to find airport details in FGAILocalTraffic::Init()\n";
     }
 
+    // Set the projection for the local area
+    ortho.Init(rwy.threshold_pos, rwy.hdg);    
+    rwy.end1ortho = ortho.ConvertToLocal(rwy.threshold_pos);   // should come out as zero
+    // Hardwire to KEMT for now
+    rwy.end2ortho = ortho.ConvertToLocal(takeoff_end);
+    //cout << "*********************************************************************************\n";
+    //cout << "*********************************************************************************\n";
+    //cout << "*********************************************************************************\n";
+    //cout << "end1ortho = " << rwy.end1ortho << '\n';
+    //cout << "end2ortho = " << rwy.end2ortho << '\n'; // end2ortho.x() should be zero or thereabouts
+
     Transform();
 }
 
 // Run the internal calculations
-void FGAILocalTraffic::Update() {
-    hdg = mag_hdg + mag_var;
-    
+void FGAILocalTraffic::Update(double dt) {
+    std::cout << "In FGAILocalTraffic::Update\n";
+    // Hardwire flying traffic pattern for now - eventually also needs to be able to taxi to and from runway and GA parking area.
+    FlyTrafficPattern(dt);
+    Transform();
+    //cout << "elev in FGAILocalTraffic = " << aip.getFGLocation()->get_cur_elev_m() << '\n';
     // This should become if(the plane has moved) then Transform()
-    static int i = 0;
-    if(i == 60) {
-       Transform();
-       i = 0;
+}
+
+// Fly a traffic pattern
+// FIXME - far too much of the mechanics of turning, rolling, accellerating, descending etc is in here.
+//        Move it out to FGAIPlane and have FlyTrafficPattern just specify what to do, not the implementation.
+void FGAILocalTraffic::FlyTrafficPattern(double dt) {
+    // Need to differentiate between in-air (IAS governed) and on-ground (vel governed)
+    // Take-off is an interesting case - we are on the ground but takeoff speed is IAS governed.
+    bool inAir = true; // FIXME - possibly make into a class variable
+
+    static bool transmitted = false;   // FIXME - this is a hack
+
+    // WIND
+    // Wind has two effects - a mechanical one in that IAS translates to a different vel, and the hdg != track,
+    // but also a piloting effect, in that the AI must be able to descend at a different rate in order to hit the threshold.
+
+    //cout << "dt = " << dt << '\n';
+    double dist = 0;
+    // ack - I can't remember how long a rate 1 turn is meant to take.
+    double turn_time = 60.0;   // seconds - TODO - check this guess
+    double turn_circumference;
+    double turn_radius;
+    Point3D orthopos = ortho.ConvertToLocal(pos);      // ortho position of the plane
+    //cout << "runway elev = " << rwy.threshold_pos.elev() << ' ' << rwy.threshold_pos.elev() * SG_METER_TO_FEET << '\n';
+    //cout << "elev = " << pos.elev() << ' ' << pos.elev() * SG_METER_TO_FEET << '\n';
+    switch(leg) {
+    case TAKEOFF_ROLL:
+       inAir = false;
+       track = rwy.hdg;
+       if(vel < 80.0) {
+           double dveldt = 5.0;
+           vel += dveldt * dt;
+       }
+       IAS = vel + (cos((hdg - wind_from_hdg) * DCL_DEGREES_TO_RADIANS) * wind_speed_knots);
+       if(IAS >= 70) {
+           leg = CLIMBOUT;
+           pitch = 10.0;
+           IAS = best_rate_of_climb_speed;
+           slope = 7.0;
+       }
+       break;
+    case CLIMBOUT:
+       track = rwy.hdg;
+       if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 600) {
+           leg = TURN1;
+       }
+       break;
+    case TURN1:
+       track += (360.0 / turn_time) * dt * patternDirection;
+       Bank(25.0 * patternDirection);
+       if((track < (rwy.hdg - 89.0)) || (track > (rwy.hdg + 89.0))) {
+           leg = CROSSWIND;
+       }
+       break;
+    case CROSSWIND:
+       LevelWings();
+       track = rwy.hdg + (90.0 * patternDirection);
+       if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) {
+           slope = 0.0;
+           pitch = 0.0;
+           IAS = 80.0;         // FIXME - use smooth transistion to new speed
+       }
+       // turn 1000m out for now
+       if(fabs(orthopos.x()) > 980) {
+           leg = TURN2;
+       }
+       break;
+    case TURN2:
+       track += (360.0 / turn_time) * dt * patternDirection;
+       Bank(25.0 * patternDirection);
+       // just in case we didn't make height on crosswind
+       if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) {
+           slope = 0.0;
+           pitch = 0.0;
+           IAS = 80.0;         // FIXME - use smooth transistion to new speed
+       }
+       if((track < (rwy.hdg - 179.0)) || (track > (rwy.hdg + 179.0))) {
+           leg = DOWNWIND;
+           transmitted = false;
+           //roll = 0.0;
+       }
+       break;
+    case DOWNWIND:
+       LevelWings();
+       track = rwy.hdg - (180 * patternDirection);     //should tend to bring track back into the 0->360 range
+       // just in case we didn't make height on crosswind
+       if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) {
+           slope = 0.0;
+           pitch = 0.0;
+           IAS = 90.0;         // FIXME - use smooth transistion to new speed
+       }
+       if((orthopos.y() < 0) && (!transmitted)) {
+           TransmitPatternPositionReport();
+           transmitted = true;
+       }
+       if(orthopos.y() < -480) {
+           slope = -4.0;       // FIXME - calculate to descent at 500fpm and hit the threshold (taking wind into account as well!!)
+           pitch = -3.0;
+           IAS = 85.0;
+       }
+       if(orthopos.y() < -980) {
+           //roll = -20;
+           leg = TURN3;
+           transmitted = false;
+           IAS = 80.0;
+       }
+       break;
+    case TURN3:
+       track += (360.0 / turn_time) * dt * patternDirection;
+       Bank(25.0 * patternDirection);
+       if(fabs(rwy.hdg - track) < 91.0) {
+           leg = BASE;
+       }
+       break;
+    case BASE:
+       LevelWings();
+       if(!transmitted) {
+           TransmitPatternPositionReport();
+           transmitted = true;
+       }
+       track = rwy.hdg - (90 * patternDirection);
+       slope = -6.0;   // FIXME - calculate to descent at 500fpm and hit the threshold
+       pitch = -4.0;
+       IAS = 70.0;     // FIXME - slowdown gradually
+       // Try and arrange to turn nicely onto base
+       turn_circumference = IAS * 0.514444 * turn_time;        
+       //Hmmm - this is an interesting one - ground vs airspeed in relation to turn radius
+       //We'll leave it as a hack with IAS for now but it needs revisiting.
+                                                       
+       turn_radius = turn_circumference / (2.0 * DCL_PI);
+       if(fabs(orthopos.x()) < (turn_radius + 50)) {
+           leg = TURN4;
+           transmitted = false;
+           //roll = -20;
+       }
+       break;
+    case TURN4:
+       track += (360.0 / turn_time) * dt * patternDirection;
+       Bank(25.0 * patternDirection);
+       if(fabs(track - rwy.hdg) < 0.6) {
+           leg = FINAL;
+           vel = nominal_final_speed;
+       }
+       break;
+    case FINAL:
+       LevelWings();
+       if(!transmitted) {
+           TransmitPatternPositionReport();
+           transmitted = true;
+       }
+       // Try and track the extended centreline
+       track = rwy.hdg - (0.2 * orthopos.x());
+       //cout << "orthopos.x() = " << orthopos.x() << " hdg = " << hdg << '\n';
+       if(pos.elev() <= rwy.threshold_pos.elev()) {
+           pos.setelev(rwy.threshold_pos.elev());// + (-8.5 * SG_FEET_TO_METER));  // This is a complete hack - the rendered runway takes the underlying scenery elev rather than the published runway elev so I should use height above terrain or something.
+           slope = 0.0;
+           pitch = 0.0;
+           leg = LANDING_ROLL;
+       }
+       break;
+    case LANDING_ROLL:
+       inAir = false;
+       track = rwy.hdg;
+       double dveldt = -5.0;
+       vel += dveldt * dt;
+       if(vel <= 15.0) {
+           leg = TAKEOFF_ROLL;
+       }
+       break;
     }
-    i++; 
+
+    yaw = 0.0; //yaw = f(track, wind);
+    hdg = track + yaw;
+    // Apply wind to ground-relative velocity if in the air
+    if(inAir) {
+       vel = IAS - (cos((hdg - wind_from_hdg) * DCL_DEGREES_TO_RADIANS) * wind_speed_knots);
+    }
+    dist = vel * 0.514444 * dt;
+    pos = dclUpdatePosition(pos, track, slope, dist);
+}
+
+void FGAILocalTraffic::TransmitPatternPositionReport(void) {
+    // airport name + "traffic" + airplane callsign + pattern direction + pattern leg + rwy + ?
+    string trns = "";
+
+    trns += tower->get_name();
+    trns += " Traffic ";
+    // FIXME - add the callsign to the class variables
+    trns += "Trainer-two-five-charlie ";
+    if(patternDirection == 1) {
+       trns += "right ";
+    } else {
+       trns += "left ";
+    }
+
+    // We could probably get rid of this whole switch statement and just pass a string containing the leg from the FlyPattern function.
+    switch(leg) {      // We'll assume that transmissions in turns are intended for next leg - do pilots ever call out that they are in the turn?
+    case TURN1:
+       // Fall through to CROSSWIND
+    case CROSSWIND:    // I don't think this case will be used here but it can't hurt to leave it in
+       trns += "crosswind ";
+       break;
+    case TURN2:
+       // Fall through to DOWNWIND
+    case DOWNWIND:
+       trns += "downwind ";
+       break;
+    case TURN3:
+       // Fall through to BASE
+    case BASE:
+       trns += "base ";
+       break;
+    case TURN4:
+       // Fall through to FINAL
+    case FINAL:                // maybe this should include long/short final if appropriate?
+       trns += "final ";
+       break;
+    default:           // Hopefully this won't be used
+       trns += "pattern ";
+       break;
+    }
+    // FIXME - I've hardwired the runway call as well!! (We could work this out from rwy heading and mag deviation)
+    trns += convertNumToSpokenString(1);
+
+    // And add the airport name again
+    trns += tower->get_name();
+    
+    Transmit(trns);
 }
index 8d2e6d53757c1c77efd8f5785311dfb93ff22bbd..79e4bded250961a5d4ed5a7fbe96e86a13e86f59 100644 (file)
 #include <simgear/math/point3d.hxx>
 
 #include "tower.hxx"
-#include "AIEntity.hxx"
+#include "AIPlane.hxx"
+#include "ATCProjection.hxx"
 
-typedef enum pattern_leg {
+typedef enum PatternLeg {
     TAKEOFF_ROLL,
-    OUTWARD,
+    CLIMBOUT,
     TURN1,
     CROSSWIND,
     TURN2,
@@ -51,7 +52,23 @@ typedef enum pattern_leg {
     LANDING_ROLL
 };
 
-class FGAILocalTraffic : public FGAIEntity {
+// perhaps we could use an FGRunway instead of this
+typedef struct RunwayDetails {
+    Point3D threshold_pos;
+    Point3D end1ortho; // ortho projection end1 (the threshold ATM)
+    Point3D end2ortho; // ortho projection end2 (the take off end in the current hardwired scheme)
+    double mag_hdg;
+    double mag_var;
+    double hdg;                // true runway heading
+};
+
+typedef struct StartofDescent {
+    PatternLeg leg;
+    double orthopos_x;
+    double orthopos_y;
+};
+
+class FGAILocalTraffic : public FGAIPlane {
 
 public:
 
@@ -62,24 +79,27 @@ public:
     void Init();
 
     // Run the internal calculations
-    void Update();
+    void Update(double dt);
+
+protected:
+
+    // Attempt to enter the traffic pattern in a reasonably intelligent manner
+    void EnterTrafficPattern(double dt);
 
 private:
+    FGATCAlignedProjection ortho;      // Orthogonal mapping of the local area with the threshold at the origin
+                                       // and the runway aligned with the y axis.
 
+    // Airport/runway/pattern details
     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.
+    RunwayDetails rwy;
+    double patternDirection;   // 1 for right, -1 for left (This is double because we multiply/divide turn rates
+                               // with it to get RH/LH turns - DON'T convert it to int under ANY circumstances!!
+    double glideAngle;         // Assumed to be visual glidepath angle for FGAILocalTraffic - can be found at www.airnav.com
+    // Its conceivable that patternDirection and glidePath could be moved into the RunwayDetails structure.
 
-    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
+    // Performance characteristics of the plane in knots and ft/min - some of this might get moved out into FGAIPlane
     double Vr;
     double best_rate_of_climb_speed;
     double best_rate_of_climb;
@@ -90,11 +110,27 @@ private:
     double max_circuit_speed;
     double nominal_descent_rate;
     double nominal_approach_speed;
+    double nominal_final_speed;
     double stall_speed_landing_config;
 
-    // OK, what do we need to know whilst flying the pattern
-    pattern_leg leg;
+    // environment - some of this might get moved into FGAIPlane
+    double wind_from_hdg;      // degrees
+    double wind_speed_knots;   // knots
+
+    // Pattern details that (may) change
+    int numInPattern;          // Number of planes in the pattern (this might get more complicated if high performance GA aircraft fly a higher pattern eventually)
+    int numAhead;              // More importantly - how many of them are ahead of us?
+    double distToNext;         // And even more importantly, how near are we getting to the one immediately ahead?
+    PatternLeg leg;            // Out current position in the pattern
+    StartofDescent SoD;                // Start of descent calculated wrt wind, pattern size & altitude, glideslope etc
+
+    void FlyTrafficPattern(double dt);
+
+    // TODO - need to add something to define what option we are flying - Touch and go / Stop and go / Landing properly / others?
+
+    void TransmitPatternPositionReport();
 
+    void CalculateStartofDescent();
 };
 
 #endif  // _FG_AILocalTraffic_HXX
index e99f099946fbd10a985523170ef597d612574ce6..a7851b60b9296dd29151e33ec7bce6c76b298cec 100644 (file)
@@ -60,11 +60,18 @@ void FGAIMgr::bind() {
 void FGAIMgr::unbind() {
 }
 
-void FGAIMgr::update(int dt) {
+void FGAIMgr::update(double dt) {
+    // Don't update any planes for first 50 runs through - this avoids some possible initialisation anomalies
+    static int i = 0;
+    i++;
+    if(i < 50) {
+       return;
+    }
+
     // 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)->Update(dt);
        ++ai_list_itr;
     }
 }
index 3933348f88338b1dac7340095c3cb51321fc04b7..35276aeaedd5c14776f5615a7f0c4f673f9f5ff5 100644 (file)
@@ -84,7 +84,7 @@ public:
 
     void unbind();
 
-    void update(int dt);
+    void update(double dt);
 
 private:
 
diff --git a/src/ATC/AIPlane.cxx b/src/ATC/AIPlane.cxx
new file mode 100644 (file)
index 0000000..c3a25ec
--- /dev/null
@@ -0,0 +1,77 @@
+// FGAIPlane - abstract base class for an AI plane
+//
+// Written by David Luff, started 2002.
+//
+// Copyright (C) 2002  David C. Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 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 <Main/fg_props.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 <math.h>
+#include <string>
+SG_USING_STD(string);
+
+
+#include "AIPlane.hxx"
+#include "ATCdisplay.hxx"
+
+FGAIPlane::~FGAIPlane() {
+}
+
+void FGAIPlane::Update(double dt) {
+}
+
+void FGAIPlane::Bank(double angle) {
+    // This *should* bank us smoothly to any angle
+    if(fabs(roll - angle) > 0.6) {
+       roll -= ((roll - angle)/fabs(roll - angle));  
+    }
+}
+
+// Duplication of Bank(0.0) really - should I cut this?
+void FGAIPlane::LevelWings(void) {
+    // bring the plane back to level smoothly (this should work to come out of either bank)
+    if(fabs(roll) > 0.6) {
+       roll -= (roll/fabs(roll));
+    }
+}
+
+void FGAIPlane::Transmit(string msg) {
+    double user_freq0 = fgGetDouble("/radios/comm[0]/frequencies/selected-mhz");
+    //double user_freq0 = ("/radios/comm[0]/frequencies/selected-mhz");
+    //comm1 is not used yet.
+
+    if(freq == user_freq0) {
+       // we are on the same frequency, so check distance to the user plane
+       if(1) {
+           // For now (testing) assume in range !!!
+           globals->get_ATC_display()->RegisterSingleMessage(msg, 0);
+       }
+    }
+}
diff --git a/src/ATC/AIPlane.hxx b/src/ATC/AIPlane.hxx
new file mode 100644 (file)
index 0000000..bb58f20
--- /dev/null
@@ -0,0 +1,87 @@
+// FGAIPlane - abstract base class for an AI plane
+//
+// Written by David Luff, started 2002.
+//
+// Copyright (C) 2002  David C. Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 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_AI_PLANE_HXX
+#define _FG_AI_PLANE_HXX
+
+#include <Model/model.hxx>
+#include <plib/sg.h>
+#include <plib/ssg.h>
+#include <simgear/math/point3d.hxx>
+
+#include "AIEntity.hxx"
+
+
+/*****************************************************************
+*
+*  FGAIPlane - this class is derived from FGAIEntity and adds the 
+*  practical requirement for an AI plane - the ability to send radio
+*  communication, and simple performance details for the actual AI
+*  implementation to use.  The AI implementation is expected to be
+*  in derived classes - this class does nothing useful on its own.
+*
+******************************************************************/
+class FGAIPlane : public FGAIEntity {
+
+public:
+
+    virtual ~FGAIPlane();
+
+    // Run the internal calculations
+    virtual void Update(double dt);
+
+protected:
+
+    double mag_hdg;    // degrees - the heading that the physical aircraft is *pointing*
+    double track;      // track that the physical aircraft is *following* - degrees relative to *true* north
+    double yaw;
+    double mag_var;    // degrees
+    double IAS;                // Indicated airspeed in knots
+    double vel;                // velocity along track in knots
+    double vel_si;     // velocity along track in m/s
+    double slope;      // Actual slope that the plane is flying (degrees) - +ve is uphill
+    double AoA;                // degrees - difference between slope and pitch
+    // We'll assume that the plane doesn't yaw or slip - the track/heading difference is to allow for wind
+
+    double freq;       // The comm frequency that we're operating on
+
+    // We need some way for this class to display its radio transmissions if on the 
+    // same frequency and in the vicinity of the user's aircraft
+    // This may need to be done independently of ATC eg CTAF
+    // Make radio transmission - this simply sends the transmission for physical rendering if the users
+    // aircraft is on the same frequency and in range.  It is up to the derived classes to let ATC know
+    // what is going on.
+    void Transmit(string msg);
+
+    void Bank(double angle);
+    void LevelWings(void);
+
+};
+
+#endif  // _FG_AI_PLANE_HXX
+
diff --git a/src/ATC/ATCProjection.cxx b/src/ATC/ATCProjection.cxx
new file mode 100644 (file)
index 0000000..9429350
--- /dev/null
@@ -0,0 +1,76 @@
+#include "ATCProjection.hxx"
+#include <math.h>
+#include <simgear/constants.h>
+
+#define DCL_PI  3.1415926535f
+//#define SG_PI  ((SGfloat) M_PI)
+#define DCL_DEGREES_TO_RADIANS  (DCL_PI/180.0)
+#define DCL_RADIANS_TO_DEGREES  (180.0/DCL_PI)
+
+FGATCProjection::FGATCProjection() {
+    origin.setlat(0.0);
+    origin.setlon(0.0);
+    origin.setelev(0.0);
+    correction_factor = cos(origin.lat() * DCL_DEGREES_TO_RADIANS);
+}
+
+FGATCProjection::~FGATCProjection() {
+}
+
+void FGATCProjection::Init(Point3D centre) {
+    origin = centre;
+    correction_factor = cos(origin.lat() * DCL_DEGREES_TO_RADIANS);
+}
+
+Point3D FGATCProjection::ConvertToLocal(Point3D pt) {
+    double delta_lat = pt.lat() - origin.lat();
+    double delta_lon = pt.lon() - origin.lon();
+
+    double y = sin(delta_lat * DCL_DEGREES_TO_RADIANS) * SG_EQUATORIAL_RADIUS_M;
+    double x = sin(delta_lon * DCL_DEGREES_TO_RADIANS) * SG_EQUATORIAL_RADIUS_M * correction_factor;
+
+    return(Point3D(x,y,0.0));
+}
+
+Point3D FGATCProjection::ConvertFromLocal(Point3D pt) {
+    return(Point3D(0,0,0));
+}
+
+/**********************************************************************************/
+
+FGATCAlignedProjection::FGATCAlignedProjection() {
+    origin.setlat(0.0);
+    origin.setlon(0.0);
+    origin.setelev(0.0);
+    correction_factor = cos(origin.lat() * DCL_DEGREES_TO_RADIANS);
+}
+
+FGATCAlignedProjection::~FGATCAlignedProjection() {
+}
+
+void FGATCAlignedProjection::Init(Point3D centre, double heading) {
+    origin = centre;
+    theta = heading * DCL_DEGREES_TO_RADIANS;
+    correction_factor = cos(origin.lat() * DCL_DEGREES_TO_RADIANS);
+}
+
+Point3D FGATCAlignedProjection::ConvertToLocal(Point3D pt) {
+    // convert from lat/lon to orthogonal
+    double delta_lat = pt.lat() - origin.lat();
+    double delta_lon = pt.lon() - origin.lon();
+    double y = sin(delta_lat * DCL_DEGREES_TO_RADIANS) * SG_EQUATORIAL_RADIUS_M;
+    double x = sin(delta_lon * DCL_DEGREES_TO_RADIANS) * SG_EQUATORIAL_RADIUS_M * correction_factor;
+    //cout << "Before alignment, x = " << x << " y = " << y << '\n';
+
+    // Align
+    double xbar = x;
+    x = x*cos(theta) - y*sin(theta);
+    y = (xbar*sin(theta)) + (y*cos(theta));
+    //cout << "After alignment, x = " << x << " y = " << y << '\n';
+
+    return(Point3D(x,y,0.0));
+}
+
+Point3D FGATCAlignedProjection::ConvertFromLocal(Point3D pt) {
+    return(Point3D(0,0,0));
+}
diff --git a/src/ATC/ATCProjection.hxx b/src/ATC/ATCProjection.hxx
new file mode 100644 (file)
index 0000000..d5cf306
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef _FG_ATC_PROJECTION_HXX
+#define _FG_ATC_PROJECTION_HXX
+
+#include <simgear/math/point3d.hxx>
+
+// FGATCProjection - a class to project an area local to an airport onto an orthogonal co-ordinate system
+class FGATCProjection {
+
+public:
+    FGATCProjection();
+    ~FGATCProjection();
+
+    void Init(Point3D centre);
+
+    // Convert a lat/lon co-ordinate to the local projection
+    Point3D ConvertToLocal(Point3D pt);
+
+    // Convert a local projection co-ordinate to lat/lon
+    Point3D ConvertFromLocal(Point3D pt);
+
+private:
+    Point3D origin;    // lat/lon of local area origin
+    double correction_factor;  // Reduction in surface distance per degree of longitude due to latitude.  Saves having to do a cos() every call.
+
+};
+
+
+// FGATCAlignedProjection - a class to project an area local to a runway onto an orthogonal co-ordinate system
+// with the origin at the threshold and the runway aligned with the y axis.
+class FGATCAlignedProjection {
+
+public:
+    FGATCAlignedProjection();
+    ~FGATCAlignedProjection();
+
+    void Init(Point3D centre, double heading);
+
+    // Convert a lat/lon co-ordinate to the local projection
+    Point3D ConvertToLocal(Point3D pt);
+
+    // Convert a local projection co-ordinate to lat/lon
+    Point3D ConvertFromLocal(Point3D pt);
+
+private:
+    Point3D origin;    // lat/lon of local area origin (the threshold)
+    double theta;      // the rotation angle for alignment in radians
+    double correction_factor;  // Reduction in surface distance per degree of longitude due to latitude.  Saves having to do a cos() every call.
+
+};
+
+#endif // _FG_ATC_PROJECTION_HXX
index eb6441de854ef79a889a8a82a80af14d88c1be19..b663814e908bc0c9e832b6738cd1c35813f2424e 100644 (file)
@@ -146,6 +146,22 @@ void FGATCMgr::RemoveFromList(const char* id, atc_type tp) {
     }
 }
 
+//DCL - this routine untested so far.
+// Find in list - return a currently active ATC pointer given ICAO code and type
+FGATC* FGATCMgr::FindInList(const char* id, atc_type tp) {
+    atc_list_itr = atc_list.begin();
+    while(atc_list_itr != atc_list.end()) {
+       if( (!strcmp((*atc_list_itr)->GetIdent(), id))
+           && ((*atc_list_itr)->GetType() == tp) ) {
+           return(*atc_list_itr);
+       }       // Note that that can upset where we are in the list but that shouldn't really matter
+       ++atc_list_itr;
+    }
+    // We need a fallback position
+    cout << "*** Failed to find FGATC* in FGATCMgr::FindInList - this should not happen!" << endl;
+    return(NULL);
+}
+
 // 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()) {
@@ -168,6 +184,7 @@ FGATC* FGATCMgr::GetATCPointer(string icao, atc_type type) {
     case TOWER:
        if(a->tower_active) {
            // Get the pointer from the list
+           return(FindInList(icao.c_str(), type));     // DCL - this untested so far.
        } else {
            FGTower* t = new FGTower;
            if(current_towerlist->query(a->lon, a->lat, a->elev, a->tower_freq, &tower)) {
@@ -199,7 +216,7 @@ FGATC* FGATCMgr::GetATCPointer(string icao, atc_type type) {
 
     cout << "ERROR IN FGATCMgr - reached end of GetATCPointer\n";
 
-    return NULL;
+    return(NULL);
 }
 
 
index 30e06294ff6c00c0f83608c856c9f3ac57920d64..fed906e755f64726344b00174f579aca68908199 100644 (file)
@@ -167,6 +167,9 @@ private:
     // Remove a class from the atc_list and delete it from memory
     void RemoveFromList(const char* id, atc_type tp);
 
+    // Return a pointer to a class in the list (external interface to this is through GetATCPointer)
+    FGATC* FGATCMgr::FindInList(const char* id, atc_type tp);
+
     // Search a specified freq for matching stations
     void Search();
 
index 09ac9ecff68a57dfe6f77734a8c04ca0836d5b51..e4cfc3e39f10ac717bce4fbdb30cfecd1e4c69db 100644 (file)
@@ -1,9 +1,88 @@
-// Utility functions for the ATC / AI system
+// ATCutils.cxx - Utility functions for the ATC / AI system
+//
+// Written by David Luff, started March 2002.
+//
+// Copyright (C) 2002  David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 #include <math.h>
 #include <simgear/math/point3d.hxx>
 #include <simgear/constants.h>
 #include <plib/sg.h>
+#include <iomanip.h>
+
+#include "ATCutils.hxx"
+
+// Convert a 2 digit rwy number to a spoken-style string
+string convertNumToSpokenString(int n) {
+    string nums[10] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
+    // Basic error/sanity checking
+    while(n < 0) {
+       n += 36;
+    }
+    while(n > 36) {
+       n -= 36;
+    }
+    if(n == 0) {
+       n = 36; // Is this right?
+    }
+
+    string str = "";
+    int index = n/10;
+    str += nums[index];
+    n -= (index * 10);
+    str += "-";
+    str += nums[n];
+    return(str);
+}
+
+// Return the phonetic letter of a letter represented as an integer 1->26
+string GetPhoneticIdent(int i) {
+// TODO - Check i is between 1 and 26 and wrap if necessary
+    switch(i) {
+    case 1 : return("Alpha");
+    case 2 : return("Bravo");
+    case 3 : return("Charlie");
+    case 4 : return("Delta");
+    case 5 : return("Echo");
+    case 6 : return("Foxtrot");
+    case 7 : return("Golf");
+    case 8 : return("Hotel");
+    case 9 : return("Indigo");
+    case 10 : return("Juliet");
+    case 11 : return("Kilo");
+    case 12 : return("Lima");
+    case 13 : return("Mike");
+    case 14 : return("November");
+    case 15 : return("Oscar");
+    case 16 : return("Papa");
+    case 17 : return("Quebec");
+    case 18 : return("Romeo");
+    case 19 : return("Sierra");
+    case 20 : return("Tango");
+    case 21 : return("Uniform");
+    case 22 : return("Victor");
+    case 23 : return("Whiskey");
+    case 24 : return("X-ray");
+    case 25 : return("Yankee");
+    case 26 : return("Zulu");
+    }
+    // We shouldn't get here
+    return("Error");
+}
 
 // Given two positions, get the HORIZONTAL separation (in meters)
 double dclGetHorizontalSeparation(Point3D pos1, Point3D pos2) {
@@ -23,12 +102,34 @@ double dclGetHorizontalSeparation(Point3D pos1, Point3D pos2) {
     return(z);
 }
 
+// Given a point and a line, get the HORIZONTAL shortest distance from the point to a point on the line.
+// Expects to be fed orthogonal co-ordinates, NOT lat & lon !
+double dclGetLinePointSeparation(double px, double py, double x1, double y1, double x2, double y2) {
+    double vecx = x2-x1;
+    double vecy = y2-y1;
+    double magline = sqrt(vecx*vecx + vecy*vecy);
+    double u = ((px-x1)*(x2-x1) + (py-y1)*(y2-y1)) / (magline * magline);
+    double x0 = x1 + u*(x2-x1);
+    double y0 = y1 + u*(y2-y1);
+    vecx = px - x0;
+    vecy = py - y0;
+    double d = sqrt(vecx*vecx + vecy*vecy);
+    if(d < 0) {
+       d *= -1;
+    }
+    return(d);
+}
+
 // Given a position (lat/lon/elev), heading, vertical angle, and distance, calculate the new position.
 // Assumes that the ground is not hit!!!  Expects heading and angle in degrees, distance in meters.
 Point3D dclUpdatePosition(Point3D pos, double heading, double angle, double distance) {
-    double lat = pos.lat() * SG_DEGREES_TO_RADIANS;
-    double lon = pos.lon() * SG_DEGREES_TO_RADIANS;
+    //cout << setprecision(10) << pos.lon() << ' ' << pos.lat() << '\n';
+    heading *= DCL_DEGREES_TO_RADIANS;
+    angle *= DCL_DEGREES_TO_RADIANS;
+    double lat = pos.lat() * DCL_DEGREES_TO_RADIANS;
+    double lon = pos.lon() * DCL_DEGREES_TO_RADIANS;
     double elev = pos.elev();
+    //cout << setprecision(10) << lon*DCL_RADIANS_TO_DEGREES << ' ' << lat*DCL_RADIANS_TO_DEGREES << '\n';
 
     double horiz_dist = distance * cos(angle);
     double vert_dist = distance * sin(angle);
@@ -36,11 +137,19 @@ Point3D dclUpdatePosition(Point3D pos, double heading, double angle, double dist
     double north_dist = horiz_dist * cos(heading);
     double east_dist = horiz_dist * sin(heading);
 
-    lat += asin(north_dist / SG_EQUATORIAL_RADIUS_M);
-    lon += asin(east_dist / SG_EQUATORIAL_RADIUS_M) * (1.0 / cos(lat));  // I suppose really we should use the average of the original and new lat but we'll assume that this will be good enough.
+    //cout << distance << ' ' << horiz_dist << ' ' << vert_dist << ' ' << north_dist << ' ' << east_dist << '\n';
+
+    double delta_lat = asin(north_dist / (double)SG_EQUATORIAL_RADIUS_M);
+    double delta_lon = asin(east_dist / (double)SG_EQUATORIAL_RADIUS_M) * (1.0 / cos(lat));  // I suppose really we should use the average of the original and new lat but we'll assume that this will be good enough.
+    //cout << delta_lon*DCL_RADIANS_TO_DEGREES << ' ' << delta_lat*DCL_RADIANS_TO_DEGREES << '\n';
+    lat += delta_lat;
+    lon += delta_lon;
     elev += vert_dist;
+    //cout << setprecision(10) << lon*DCL_RADIANS_TO_DEGREES << ' ' << lat*DCL_RADIANS_TO_DEGREES << '\n';
+
+    //cout << setprecision(15) << DCL_DEGREES_TO_RADIANS * DCL_RADIANS_TO_DEGREES << '\n';
 
-    return(Point3D(lon*SG_RADIANS_TO_DEGREES, lat*SG_RADIANS_TO_DEGREES, elev));
+    return(Point3D(lon*DCL_RADIANS_TO_DEGREES, lat*DCL_RADIANS_TO_DEGREES, elev));
 }
     
 
index 13d3a09a3b9a1991cfc0ea90e34462f9134a51fd..6576bf1aca358c3bfb9ef24df980211cead572d1 100644 (file)
@@ -1,11 +1,59 @@
-// Utility functions for the ATC / AI subsytem declarations
+// ATCutils.hxx - Utility functions for the ATC / AI subsytem
+//
+// Written by David Luff, started March 2002.
+//
+// Copyright (C) 2002  David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 #include <math.h>
 #include <simgear/math/point3d.hxx>
+#include <string>
+SG_USING_STD(string);
+
+// These are defined here because I had a problem with SG_DEGREES_TO_RADIANS
+#define DCL_PI  3.1415926535f
+#define DCL_DEGREES_TO_RADIANS  (DCL_PI/180.0)
+#define DCL_RADIANS_TO_DEGREES  (180.0/DCL_PI)
+
+/*******************************
+*
+*  Communication functions
+*
+********************************/
+
+// Convert a 2 digit rwy number to a spoken-style string
+string convertNumToSpokenString(int n);
+
+// Return the phonetic letter of a letter represented as an integer 1->26
+string GetPhoneticIdent(int i);
+
+
+/*******************************
+*
+*  Positional functions
+*
+********************************/
 
 // Given two positions, get the HORIZONTAL separation
 double dclGetHorizontalSeparation(Point3D pos1, Point3D pos2);
 
+// Given a point and a line, get the HORIZONTAL shortest distance from the point to a point on the line.
+// Expects to be fed orthogonal co-ordinates, NOT lat & lon !
+double dclGetLinePointSeparation(double px, double py, double x1, double y1, double x2, double y2);
+
 // Given a position (lat/lon/elev), heading, vertical angle, and distance, calculate the new position.
 // Assumes that the ground is not hit!!!  Expects heading and angle in degrees, distance in meters.
 Point3D dclUpdatePosition(Point3D pos, double heading, double angle, double distance);
index 2128a6f89c9ed09daf419a03f085403e8cb5fb5f..b2e9991eb77280d027ce47e94619afb6ef766f4a 100644 (file)
@@ -8,8 +8,10 @@ libATC_a_SOURCES = \
        ATCdisplay.hxx ATCdisplay.cxx \
        ATCmgr.hxx ATCmgr.cxx \
        ATCutils.hxx ATCutils.cxx \
+       ATCProjection.hxx ATCProjection.cxx \
        AIMgr.hxx AIMgr.cxx \
        AIEntity.hxx AIEntity.cxx \
+       AIPlane.hxx AIPlane.cxx \
        AILocalTraffic.hxx AILocalTraffic.cxx
 
 INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
index 428847a37bef1f28c29a1d3927cdbd137c8f48a7..4da9521a8a69f28bf262aaabedbd7758adb00e46 100644 (file)
@@ -86,7 +86,7 @@ bool FGApproachList::init( SGPath path ) {
        //cout << " Name = " << a.get_name() << endl; 
 
        approachlist_freq[a.get_freq()].push_back(a);
-       approachlist_bck[a.get_bucket()].push_back(a);
+       approachlist_bck[int(a.get_bucket())].push_back(a);
         in >> skipcomment;
 
     }
index 758df527ec26d091385a4b95e646650de418f405..0b2007aa0c0900373a7c09b21edb99dcfe4217b2 100644 (file)
@@ -54,40 +54,7 @@ SG_USING_STD(cout);
 #include "atis.hxx"
 #include "atislist.hxx"
 #include "ATCdisplay.hxx"
-
-string GetPhoneticIdent(int i) {
-// TODO - Check i is between 1 and 26 and wrap if necessary
-    switch(i) {
-    case 1 : return("Alpha");
-    case 2 : return("Bravo");
-    case 3 : return("Charlie");
-    case 4 : return("Delta");
-    case 5 : return("Echo");
-    case 6 : return("Foxtrot");
-    case 7 : return("Golf");
-    case 8 : return("Hotel");
-    case 9 : return("Indigo");
-    case 10 : return("Juliet");
-    case 11 : return("Kilo");
-    case 12 : return("Lima");
-    case 13 : return("Mike");
-    case 14 : return("November");
-    case 15 : return("Oscar");
-    case 16 : return("Papa");
-    case 17 : return("Quebec");
-    case 18 : return("Romeo");
-    case 19 : return("Sierra");
-    case 20 : return("Tango");
-    case 21 : return("Uniform");
-    case 22 : return("Victor");
-    case 23 : return("Whiskey");
-    case 24 : return("X-ray");
-    case 25 : return("Yankee");
-    case 26 : return("Zulu");
-    }
-    // We shouldn't get here
-    return("Error");
-}
+#include "ATCutils.hxx"
 
 // Constructor
 FGATIS::FGATIS()
index f4941ce707fb55ae34a78ce3031e9312ce0145ce..c0ed749d861e41b5cb936942251bf0e2d5ce1824 100644 (file)
@@ -76,6 +76,7 @@ public:
     inline int get_range() const { return range; }
     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 TOWER; }
 
     // Make a request of tower control