From c581cfa395bdd1bdfa94c34952ecf5c949343e15 Mon Sep 17 00:00:00 2001 From: david Date: Wed, 2 Oct 2002 15:27:49 +0000 Subject: [PATCH] Preliminary support for AI planes from Dave Luff. This works only at 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). --- src/ATC/AIEntity.cxx | 29 ++- src/ATC/AIEntity.hxx | 26 ++- src/ATC/AILocalTraffic.cxx | 362 +++++++++++++++++++++++++++++++++---- src/ATC/AILocalTraffic.hxx | 72 ++++++-- src/ATC/AIMgr.cxx | 11 +- src/ATC/AIMgr.hxx | 2 +- src/ATC/AIPlane.cxx | 77 ++++++++ src/ATC/AIPlane.hxx | 87 +++++++++ src/ATC/ATCProjection.cxx | 76 ++++++++ src/ATC/ATCProjection.hxx | 51 ++++++ src/ATC/ATCmgr.cxx | 19 +- src/ATC/ATCmgr.hxx | 3 + src/ATC/ATCutils.cxx | 123 ++++++++++++- src/ATC/ATCutils.hxx | 50 ++++- src/ATC/Makefile.am | 2 + src/ATC/approachlist.cxx | 2 +- src/ATC/atis.cxx | 35 +--- src/ATC/tower.hxx | 1 + 18 files changed, 908 insertions(+), 120 deletions(-) create mode 100644 src/ATC/AIPlane.cxx create mode 100644 src/ATC/AIPlane.hxx create mode 100644 src/ATC/ATCProjection.cxx create mode 100644 src/ATC/ATCProjection.hxx diff --git a/src/ATC/AIEntity.cxx b/src/ATC/AIEntity.cxx index f3b06ff9c..9ca86e220 100644 --- a/src/ATC/AIEntity.cxx +++ b/src/ATC/AIEntity.cxx @@ -39,10 +39,18 @@ FGAIEntity::~FGAIEntity() { } -// Run the internal calculations -void FGAIEntity::Update() { +void FGAIEntity::Update(double dt) { } +// Run the internal calculations +//void FGAIEntity::Update() { +void FGAIEntity::Transform() { + aip.setPosition(pos.lon(), pos.lat(), pos.elev() * SG_METER_TO_FEET); + aip.setOrientation(roll, pitch, hdg); + aip.update(); +} + +/* 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 ); } +*/ diff --git a/src/ATC/AIEntity.hxx b/src/ATC/AIEntity.hxx index c7262c8f1..f8ad8131a 100644 --- a/src/ATC/AIEntity.hxx +++ b/src/ATC/AIEntity.hxx @@ -29,10 +29,20 @@ #ifndef _FG_AIEntity_HXX #define _FG_AIEntity_HXX +#include #include #include #include + +/***************************************************************** +* +* FGAIEntity - this class implements the minimum requirement +* for any AI entity - a position, an orientation, an associated +* 3D model, and the ability to be moved. It does nothing useful +* and all AI entities are expected to be derived from it. +* +******************************************************************/ class FGAIEntity { public: @@ -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); }; diff --git a/src/ATC/AILocalTraffic.cxx b/src/ATC/AILocalTraffic.cxx index a705d753d..a4e1b8215 100644 --- a/src/ATC/AILocalTraffic.cxx +++ b/src/ATC/AILocalTraffic.cxx @@ -19,28 +19,37 @@ // 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
-#include -//#include +#include
+#include #include #include #include #include +#include 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"; - } else { - cout << "AILocal Traffic Model loaded successfully\n"; - } - position = new ssgTransform; - position->addKid(model); + 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()); - // 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; +#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 { + 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. + } +#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; - } - i++; +} + +// 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; + } + + 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); } diff --git a/src/ATC/AILocalTraffic.hxx b/src/ATC/AILocalTraffic.hxx index 8d2e6d537..79e4bded2 100644 --- a/src/ATC/AILocalTraffic.hxx +++ b/src/ATC/AILocalTraffic.hxx @@ -35,11 +35,12 @@ #include #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 diff --git a/src/ATC/AIMgr.cxx b/src/ATC/AIMgr.cxx index e99f09994..a7851b60b 100644 --- a/src/ATC/AIMgr.cxx +++ b/src/ATC/AIMgr.cxx @@ -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; } } diff --git a/src/ATC/AIMgr.hxx b/src/ATC/AIMgr.hxx index 3933348f8..35276aeae 100644 --- a/src/ATC/AIMgr.hxx +++ b/src/ATC/AIMgr.hxx @@ -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 index 000000000..c3a25ece0 --- /dev/null +++ b/src/ATC/AIPlane.cxx @@ -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
+#include
+//#include +//#include +#include +//#include +//#include +#include +#include +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 index 000000000..bb58f20cb --- /dev/null +++ b/src/ATC/AIPlane.hxx @@ -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 +#include +#include +#include + +#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 index 000000000..94293502a --- /dev/null +++ b/src/ATC/ATCProjection.cxx @@ -0,0 +1,76 @@ +#include "ATCProjection.hxx" +#include +#include + +#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 index 000000000..d5cf306a7 --- /dev/null +++ b/src/ATC/ATCProjection.hxx @@ -0,0 +1,51 @@ +#ifndef _FG_ATC_PROJECTION_HXX +#define _FG_ATC_PROJECTION_HXX + +#include + +// FGATCProjection - a class to project an area local to an airport onto an orthogonal co-ordinate system +class FGATCProjection { + +public: + FGATCProjection(); + ~FGATCProjection(); + + 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 diff --git a/src/ATC/ATCmgr.cxx b/src/ATC/ATCmgr.cxx index eb6441de8..b663814e9 100644 --- a/src/ATC/ATCmgr.cxx +++ b/src/ATC/ATCmgr.cxx @@ -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); } diff --git a/src/ATC/ATCmgr.hxx b/src/ATC/ATCmgr.hxx index 30e06294f..fed906e75 100644 --- a/src/ATC/ATCmgr.hxx +++ b/src/ATC/ATCmgr.hxx @@ -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(); diff --git a/src/ATC/ATCutils.cxx b/src/ATC/ATCutils.cxx index 09ac9ecff..e4cfc3e39 100644 --- a/src/ATC/ATCutils.cxx +++ b/src/ATC/ATCutils.cxx @@ -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 #include #include #include +#include + +#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. - elev += vert_dist; + //cout << distance << ' ' << horiz_dist << ' ' << vert_dist << ' ' << north_dist << ' ' << east_dist << '\n'; - return(Point3D(lon*SG_RADIANS_TO_DEGREES, lat*SG_RADIANS_TO_DEGREES, elev)); + double delta_lat = asin(north_dist / (double)SG_EQUATORIAL_RADIUS_M); + double delta_lon = asin(east_dist / (double)SG_EQUATORIAL_RADIUS_M) * (1.0 / cos(lat)); // I suppose really we should use the average of the original and new lat but we'll assume that this will be good enough. + //cout << delta_lon*DCL_RADIANS_TO_DEGREES << ' ' << delta_lat*DCL_RADIANS_TO_DEGREES << '\n'; + lat += delta_lat; + lon += delta_lon; + elev += vert_dist; + //cout << setprecision(10) << lon*DCL_RADIANS_TO_DEGREES << ' ' << lat*DCL_RADIANS_TO_DEGREES << '\n'; + + //cout << setprecision(15) << DCL_DEGREES_TO_RADIANS * DCL_RADIANS_TO_DEGREES << '\n'; + + return(Point3D(lon*DCL_RADIANS_TO_DEGREES, lat*DCL_RADIANS_TO_DEGREES, elev)); } diff --git a/src/ATC/ATCutils.hxx b/src/ATC/ATCutils.hxx index 13d3a09a3..6576bf1ac 100644 --- a/src/ATC/ATCutils.hxx +++ b/src/ATC/ATCutils.hxx @@ -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 #include +#include +SG_USING_STD(string); + +// These are defined here because I had a problem with SG_DEGREES_TO_RADIANS +#define DCL_PI 3.1415926535f +#define DCL_DEGREES_TO_RADIANS (DCL_PI/180.0) +#define DCL_RADIANS_TO_DEGREES (180.0/DCL_PI) + +/******************************* +* +* Communication functions +* +********************************/ + +// Convert 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); diff --git a/src/ATC/Makefile.am b/src/ATC/Makefile.am index 2128a6f89..b2e9991eb 100644 --- a/src/ATC/Makefile.am +++ b/src/ATC/Makefile.am @@ -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 diff --git a/src/ATC/approachlist.cxx b/src/ATC/approachlist.cxx index 428847a37..4da9521a8 100644 --- a/src/ATC/approachlist.cxx +++ b/src/ATC/approachlist.cxx @@ -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; } diff --git a/src/ATC/atis.cxx b/src/ATC/atis.cxx index 758df527e..0b2007aa0 100644 --- a/src/ATC/atis.cxx +++ b/src/ATC/atis.cxx @@ -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() diff --git a/src/ATC/tower.hxx b/src/ATC/tower.hxx index f4941ce70..c0ed749d8 100644 --- a/src/ATC/tower.hxx +++ b/src/ATC/tower.hxx @@ -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