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
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;
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;
sgSetCoord( obj_pos, mat );
}
+*/
#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:
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);
};
// 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() {
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
}
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);
}
#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,
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:
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;
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
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;
}
}
void unbind();
- void update(int dt);
+ void update(double dt);
private:
--- /dev/null
+// FGAIPlane - abstract base class for an AI plane
+//
+// Written by David Luff, started 2002.
+//
+// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 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);
+ }
+ }
+}
--- /dev/null
+// FGAIPlane - abstract base class for an AI plane
+//
+// Written by David Luff, started 2002.
+//
+// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 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
+
--- /dev/null
+#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));
+}
--- /dev/null
+#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
}
}
+//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()) {
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)) {
cout << "ERROR IN FGATCMgr - reached end of GetATCPointer\n";
- return NULL;
+ return(NULL);
}
// 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();
-// 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) {
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);
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));
}
-// 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);
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
//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;
}
#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()
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