1
0
Fork 0
flightgear/src/AIModel/AIAircraft.cxx

1088 lines
36 KiB
C++
Raw Normal View History

// FGAIAircraft - FGAIBase-derived class creates an AI airplane
//
// Written by David Culp, started October 2003.
//
// Copyright (C) 2003 David P. Culp - davidculp2@comcast.net
//
// 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
2006-02-21 01:16:04 +00:00
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/math/point3d.hxx>
#include <simgear/route/waypoint.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <Main/viewer.hxx>
#include <Scenery/scenery.hxx>
#include <Scenery/tilemgr.hxx>
#include <string>
#include <math.h>
2004-12-05 09:34:03 +00:00
#include <time.h>
2005-02-18 10:16:30 +00:00
#ifdef _MSC_VER
# include <float.h>
# define finite _finite
#elif defined(__sun) || defined(sgi)
2005-05-07 08:56:42 +00:00
# include <ieeefp.h>
2005-02-18 10:16:30 +00:00
#endif
SG_USING_STD(string);
#include "AIAircraft.hxx"
//#include <Airports/trafficcontroller.hxx>
static string tempReg;
//
// accel, decel, climb_rate, descent_rate, takeoff_speed, climb_speed,
// cruise_speed, descent_speed, land_speed
//
2003-12-21 13:42:01 +00:00
const FGAIAircraft::PERF_STRUCT FGAIAircraft::settings[] = {
// light aircraft
{2.0, 2.0, 450.0, 1000.0, 70.0, 80.0, 100.0, 80.0, 60.0},
// ww2_fighter
{4.0, 2.0, 3000.0, 1500.0, 110.0, 180.0, 250.0, 200.0, 100.0},
// jet_transport
{5.0, 2.0, 3000.0, 1500.0, 140.0, 300.0, 430.0, 300.0, 130.0},
// jet_fighter
David Culp: Here's some additions to AI that allow refueling from an AI tanker (the actual onload of fuel must be handled by the user's FDM of course, this just lets the FDM know that the user is in position to refuel). I've added a new class of AIAircraft called "tanker". It uses the same performance struct as a jet transport. An AI tanker is just like an AI jet transport, except it uses the already-existing radar data to control the boolean property systems/refuel/contact. The code change was minimal. An AI tanker can be created like this: <entry> <callsign>Esso 1</callsign> <type>aircraft</type> <class>tanker</class> <model>Aircraft/737/Models/boeing733.xml</model> <latitude>37.61633</latitude> <longitude>-122.38334</longitude> <altitude>3000</altitude> <heading>020</heading> <speed>280</speed> <roll>-15</roll> </entry> This puts a tanker over KSFO at 3000 feet, in a left-hand orbit. When the user gets within refueling range (contact position) then the property systems/refuel/contact will be true. Otherwise it is false. The dimensions of the refueling envelope are pretty rough right now, but still usable. The user must be behind the tanker (ie. radar y_offset > 0). The user must be at or below the tanker's altitude (ie. radar elevation > 0). The user's lat/lon must be within 250 feet of the tanker's lat/lon (ie. radar range_ft < 250). This last requirement is loose because the radar data is only updated every 100 ms, which is accurate enough for radar use, but which is sloppy for air refueling. This could be tightened up by increasing the radar update rate to once every sim cycle. I'm going to add a light to the T-38 instrument panel that will monitor the property systems/refuel/contact. This will make it easier to explore the boundaries of the refueling envelope.
2004-06-06 08:50:17 +00:00
{7.0, 3.0, 4000.0, 2000.0, 150.0, 350.0, 500.0, 350.0, 150.0},
// tanker
{5.0, 2.0, 3000.0, 1500.0, 140.0, 300.0, 430.0, 300.0, 130.0},
// ufo (extreme accel/decel)
{30.0, 30.0, 6000.0, 6000.0, 150.0, 300.0, 430.0, 300.0, 130.0}
2003-12-21 13:42:01 +00:00
};
FGAIAircraft::FGAIAircraft(FGAISchedule *ref) :
FGAIBase(otAircraft)
{
trafficRef = ref;
if (trafficRef)
groundOffset = trafficRef->getGroundOffset();
else
groundOffset = 0;
fp = 0;
controller = 0;
prevController = 0;
dt_count = 0;
dt_elev_count = 0;
use_perf_vs = true;
isTanker = false;
// set heading and altitude locks
hdg_lock = false;
alt_lock = false;
roll = 0;
headingChangeRate = 0.0;
holdPos = false;
}
FGAIAircraft::~FGAIAircraft() {
//delete fp;
if (controller)
controller->signOff(getID());
}
void FGAIAircraft::readFromScenario(SGPropertyNode* scFileNode) {
if (!scFileNode)
return;
FGAIBase::readFromScenario(scFileNode);
setPerformance(scFileNode->getStringValue("class", "jet_transport"));
setFlightPlan(scFileNode->getStringValue("flightplan"),
scFileNode->getBoolValue("repeat", false));
setCallSign(scFileNode->getStringValue("callsign"));
setTACANChannelID(scFileNode->getStringValue("TACAN-channel-ID"));
}
bool FGAIAircraft::init() {
//refuel_node = fgGetNode("systems/refuel/contact", true);
return FGAIBase::init();
}
void FGAIAircraft::bind() {
FGAIBase::bind();
props->tie("controls/gear/gear-down",
SGRawValueMethods<FGAIAircraft,bool>(*this,
&FGAIAircraft::_getGearDown));
props->tie("refuel/contact", SGRawValuePointer<bool>(&contact));
props->setStringValue("callsign", callsign.c_str());
props->setStringValue("navaids/tacan/channel-ID", TACAN_channel_id.c_str());
props->setBoolValue("tanker",isTanker);
}
void FGAIAircraft::unbind() {
FGAIBase::unbind();
props->untie("controls/gear/gear-down");
props->untie("refuel/contact");
}
void FGAIAircraft::update(double dt) {
FGAIBase::update(dt);
Run(dt);
Transform();
}
void FGAIAircraft::setPerformance(const std::string& acclass) {
if (acclass == "light") {
SetPerformance(&FGAIAircraft::settings[FGAIAircraft::LIGHT]);
} else if (acclass == "ww2_fighter") {
SetPerformance(&FGAIAircraft::settings[FGAIAircraft::WW2_FIGHTER]);
} else if (acclass == "jet_transport") {
SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]);
} else if (acclass == "jet_fighter") {
SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_FIGHTER]);
} else if (acclass == "tanker") {
SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]);
SetTanker(true);
} else if (acclass == "ufo") {
SetPerformance(&FGAIAircraft::settings[FGAIAircraft::UFO]);
} else {
SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]);
}
}
2003-12-21 13:42:01 +00:00
void FGAIAircraft::SetPerformance(const PERF_STRUCT *ps) {
performance = ps;
}
void FGAIAircraft::Run(double dt) {
FGAIAircraft::dt = dt;
if (fp) {
time_t now = time(NULL) + fgGetLong("/sim/time/warp");
ProcessFlightPlan(dt, now);
if (now < fp->getStartTime()) {
// Do execute Ground elev for inactive aircraft, so they
// Are repositioned to the correct ground altitude when the user flies within visibility range.
// In addition, check whether we are out of user range, so this aircraft
// can be deleted.
if (no_roll) {
Transform(); // make sure aip is initialized.
if (trafficRef) {
double userLatitude = fgGetDouble("/position/latitude-deg");
double userLongitude = fgGetDouble("/position/longitude-deg");
double course, distance;
SGWayPoint current(pos.getLongitudeDeg(), pos.getLatitudeDeg(), 0);
SGWayPoint user (userLongitude, userLatitude, 0);
user.CourseAndDistance(current, &course, &distance);
if ((distance * SG_METER_TO_NM) > TRAFFICTOAIDIST) {
setDie(true);
return;
}
getGroundElev(dt); // make sure it's exectuted first time around, so force a large dt value
//getGroundElev(dt); // Need to do this twice.
//cerr << trafficRef->getRegistration() << " Setting altitude to " << tgt_altitude;
doGroundAltitude();
//cerr << " Actual altitude " << altitude << endl;
// Transform();
pos.setElevationFt(altitude_ft);
}
}
return;
}
} else {
// no flight plan, update target heading, speed, and altitude
// from control properties. These default to the initial
// settings in the config file, but can be changed "on the
// fly".
string lat_mode = props->getStringValue("controls/flight/lateral-mode");
if ( lat_mode == "roll" ) {
double angle
= props->getDoubleValue("controls/flight/target-roll" );
RollTo( angle );
} else {
double angle
= props->getDoubleValue("controls/flight/target-hdg" );
TurnTo( angle );
}
string lon_mode
= props->getStringValue("controls/flight/longitude-mode");
if ( lon_mode == "alt" ) {
double alt = props->getDoubleValue("controls/flight/target-alt" );
ClimbTo( alt );
} else {
double angle
= props->getDoubleValue("controls/flight/target-pitch" );
PitchTo( angle );
}
AccelTo( props->getDoubleValue("controls/flight/target-spd" ) );
}
if (controller)
{
controller->update(getID(),
pos.getLatitudeDeg(),
pos.getLongitudeDeg(),
hdg,
speed,
altitude_ft, dt);
processATC(controller->getInstruction(getID()));
}
double turn_radius_ft;
double turn_circum_ft;
double speed_north_deg_sec;
double speed_east_deg_sec;
double dist_covered_ft;
double alpha;
// adjust speed
double speed_diff; //= tgt_speed - speed;
if (!no_roll) {
speed_diff = tgt_speed - speed;
} else {
speed_diff = groundTargetSpeed - speed;
}
if (speed_diff > 0.0) {
speed += performance->accel * dt;
if (no_roll) { // apply overshoot correction
if ( speed > groundTargetSpeed ) speed = groundTargetSpeed;
}else {
if ( speed > tgt_speed ) speed = tgt_speed;
}
} else if (speed_diff < 0.0) {
if (no_roll) {
// on ground (aircraft can't roll)
// deceleration performance is better due to wheel brakes.
speed -= performance->decel * dt * 3;
} else {
speed -= performance->decel * dt;
}
if (no_roll) { // apply overshoot correction
if (speed < groundTargetSpeed ) speed = groundTargetSpeed;
} else {
if ( speed < tgt_speed ) speed = tgt_speed;
}
}
// convert speed to degrees per second
speed_north_deg_sec = cos( hdg * SGD_DEGREES_TO_RADIANS )
* speed * 1.686 / ft_per_deg_lat;
speed_east_deg_sec = sin( hdg * SGD_DEGREES_TO_RADIANS )
* speed * 1.686 / ft_per_deg_lon;
// set new position
2006-06-15 08:29:43 +00:00
pos.setLatitudeDeg( pos.getLatitudeDeg() + speed_north_deg_sec * dt);
pos.setLongitudeDeg( pos.getLongitudeDeg() + speed_east_deg_sec * dt);
/* printf("%.7f %0.4f %.7f %.5f %.7f %.7f %.8f %.8f %.9f %.9f\n",
dt, hdg, speed, ft_per_deg_lat,
cos( hdg * SGD_DEGREES_TO_RADIANS ),
speed * 1.686 / ft_per_deg_lat,
speed_north_deg_sec, speed_east_deg_sec,
pos.getLatitudeDeg(), pos.getLongitudeDeg()); */
//if (!(finite(pos.lat()) && finite(pos.lon())))
// {
// cerr << "Position is not finite" << endl;
// cerr << "speed = " << speed << endl;
// cerr << "dt" << dt << endl;
// cerr << "heading " << hdg << endl;
// cerr << "speed east " << speed_east_deg_sec << endl;
// cerr << "speed nrth " << speed_north_deg_sec << endl;
// cerr << "deg_lat " << ft_per_deg_lat << endl;
// cerr << "deg_lon " << ft_per_deg_lon << endl;
// }
// adjust heading based on current bank angle
if (roll == 0.0)
roll = 0.01;
if (roll != 0.0) {
// double turnConstant;
//if (no_roll)
// turnConstant = 0.0088362;
//else
// turnConstant = 0.088362;
// If on ground, calculate heading change directly
if (no_roll) {
double headingDiff = fabs(hdg-tgt_heading);
if (headingDiff > 180)
headingDiff = fabs(headingDiff - 360);
groundTargetSpeed = tgt_speed - (tgt_speed * (headingDiff/45));
if (sign(groundTargetSpeed) != sign(tgt_speed))
groundTargetSpeed = 0.21 * sign(tgt_speed); // to prevent speed getting stuck in 'negative' mode
if (headingDiff > 30.0) {
headingChangeRate += dt * sign(roll); // invert if pushed backward
// Print some debug statements to find out why aircraft may get stuck
// forever turning
//if (trafficRef->getDepartureAirport()->getId() == string("EHAM")) {
// cerr << "Turning : " << trafficRef->getRegistration()
// cerr << " Speed = " << speed << " Heading " << hdg
// << " Target Heading " << tgt_heading
// << " Lead Distance " << fp->getLeadDistance()
// << " Distance to go "
// << fp->getDistanceToGo(pos.lat(), pos.lon(), fp->getCurrentWaypoint())
// << "waypoint name " << fp->getCurrentWaypoint()->name
// << endl;
//}
if (headingChangeRate > 30)
headingChangeRate = 30;
else if (headingChangeRate < -30)
headingChangeRate = -30;
} else {
if (fabs(headingChangeRate) > headingDiff)
headingChangeRate = headingDiff*sign(roll);
else
headingChangeRate += dt * sign(roll);
}
hdg += headingChangeRate * dt;
//cerr << "On ground. Heading: " << hdg << ". Target Heading: " << tgt_heading
// << ". Target speed: " << groundTargetSpeed << ". heading change rate"
// << headingChangeRate << endl;
} else {
if (fabs(speed) > 1.0) {
turn_radius_ft = 0.088362 * speed * speed
/ tan( fabs(roll) / SG_RADIANS_TO_DEGREES );
} else {
turn_radius_ft = 1.0; // Check if turn_radius_ft == 0; this might lead to a division by 0.
}
turn_circum_ft = SGD_2PI * turn_radius_ft;
dist_covered_ft = speed * 1.686 * dt;
alpha = dist_covered_ft / turn_circum_ft * 360.0;
hdg += alpha * sign(roll);
}
while ( hdg > 360.0 ) {
hdg -= 360.0;
spinCounter++;
}
while ( hdg < 0.0) {
hdg += 360.0;
spinCounter--;
}
}
// adjust target bank angle if heading lock engaged
if (hdg_lock) {
double bank_sense = 0.0;
double diff = fabs(hdg - tgt_heading);
if (diff > 180)
diff = fabs(diff - 360);
double sum = hdg + diff;
if (sum > 360.0)
sum -= 360.0;
if (fabs(sum - tgt_heading) < 1.0) {
bank_sense = 1.0; // right turn
} else {
bank_sense = -1.0; // left turn
}
if (diff < 30) {
tgt_roll = diff * bank_sense;
} else {
tgt_roll = 30.0 * bank_sense;
}
if ((fabs((double) spinCounter) > 1) && (diff > 30)) {
tgt_speed *= 0.999; // Ugly hack: If aircraft get stuck, they will continually spin around.
// The only way to resolve this is to make them slow down.
//if (tempReg.empty())
// tempReg = trafficRef->getRegistration();
//if (trafficRef->getRegistration() == tempReg) {
// cerr << trafficRef->getRegistration()
// << " appears to be spinning: " << spinCounter << endl
// << " speed " << speed << endl
// << " heading " << hdg << endl
// << " lead distance " << fp->getLeadDistance() << endl
// << " waypoint " << fp->getCurrentWaypoint()->name
// << " target heading " << tgt_heading << endl
// << " lead in angle " << fp->getLeadInAngle()<< endl
// << " roll " << roll << endl
// << " target_roll " << tgt_roll << endl;
//}
}
}
// adjust bank angle, use 9 degrees per second
double bank_diff = tgt_roll - roll;
if (fabs(bank_diff) > 0.2) {
if (bank_diff > 0.0)
roll += 9.0 * dt;
if (bank_diff < 0.0)
roll -= 9.0 * dt;
//while (roll > 180) roll -= 360;
//while (roll < 180) roll += 360;
}
// adjust altitude (meters) based on current vertical speed (fpm)
altitude_ft += vs / 60.0 * dt;
pos.setElevationFt(altitude_ft);
// adjust target Altitude, based on ground elevation when on ground
if (no_roll) {
getGroundElev(dt);
doGroundAltitude();
} else {
// find target vertical speed if altitude lock engaged
if (alt_lock && use_perf_vs) {
if (altitude_ft < tgt_altitude_ft) {
tgt_vs = tgt_altitude_ft - altitude_ft;
if (tgt_vs > performance->climb_rate)
tgt_vs = performance->climb_rate;
} else {
tgt_vs = tgt_altitude_ft - altitude_ft;
if (tgt_vs < (-performance->descent_rate))
tgt_vs = -performance->descent_rate;
}
}
if (alt_lock && !use_perf_vs) {
double max_vs = 4*(tgt_altitude_ft - altitude_ft);
double min_vs = 100;
if (tgt_altitude_ft < altitude_ft)
min_vs = -100.0;
if ((fabs(tgt_altitude_ft - altitude_ft) < 1500.0)
&& (fabs(max_vs) < fabs(tgt_vs)))
tgt_vs = max_vs;
if (fabs(tgt_vs) < fabs(min_vs))
tgt_vs = min_vs;
}
}
// adjust vertical speed
double vs_diff = tgt_vs - vs;
if (fabs(vs_diff) > 10.0) {
if (vs_diff > 0.0) {
vs += (performance->climb_rate / 3.0) * dt;
if (vs > tgt_vs)
vs = tgt_vs;
} else {
vs -= (performance->descent_rate / 3.0) * dt;
if (vs < tgt_vs)
vs = tgt_vs;
}
}
// match pitch angle to vertical speed
if (vs > 0) {
pitch = vs * 0.005;
} else {
pitch = vs * 0.002;
}
//###########################//
// do calculations for radar //
//###########################//
double range_ft2 = UpdateRadar(manager);
//************************************//
// Tanker code //
//************************************//
if ( isTanker) {
if ( (range_ft2 < 250.0 * 250.0) && (y_shift > 0.0)
&& (elevation > 0.0) ) {
//refuel_node->setBoolValue(true);
contact = true;
} else {
//refuel_node->setBoolValue(false);
contact = false;
}
} else {
contact = false;
}
}
void FGAIAircraft::AccelTo(double speed) {
tgt_speed = speed;
}
void FGAIAircraft::PitchTo(double angle) {
tgt_pitch = angle;
alt_lock = false;
}
void FGAIAircraft::RollTo(double angle) {
tgt_roll = angle;
hdg_lock = false;
}
void FGAIAircraft::YawTo(double angle) {
tgt_yaw = angle;
}
void FGAIAircraft::ClimbTo(double alt_ft ) {
tgt_altitude_ft = alt_ft;
alt_lock = true;
}
void FGAIAircraft::TurnTo(double heading) {
tgt_heading = heading;
hdg_lock = true;
}
double FGAIAircraft::sign(double x) {
if ( x < 0.0 )
return -1.0;
else
return 1.0;
}
void FGAIAircraft::setFlightPlan(const std::string& flightplan, bool repeat) {
if (!flightplan.empty()) {
FGAIFlightPlan* fp = new FGAIFlightPlan(flightplan);
fp->setRepeat(repeat);
SetFlightPlan(fp);
}
}
void FGAIAircraft::SetFlightPlan(FGAIFlightPlan *f) {
delete fp;
fp = f;
}
void FGAIAircraft::ProcessFlightPlan( double dt, time_t now ) {
bool eraseWaypoints;
if (trafficRef) {
eraseWaypoints = true;
} else
eraseWaypoints = false;
FGAIFlightPlan::waypoint* prev = 0; // the one behind you
FGAIFlightPlan::waypoint* curr = 0; // the one ahead
FGAIFlightPlan::waypoint* next = 0; // the next plus 1
prev = fp->getPreviousWaypoint();
curr = fp->getCurrentWaypoint();
next = fp->getNextWaypoint();
dt_count += dt;
///////////////////////////////////////////////////////////////////////////
// Initialize the flightplan
//////////////////////////////////////////////////////////////////////////
if (!prev) {
spinCounter = 0;
tempReg = "";
fp->IncrementWaypoint(eraseWaypoints);
if (!(fp->getNextWaypoint()) && trafficRef)
loadNextLeg();
prev = fp->getPreviousWaypoint(); //first waypoint
curr = fp->getCurrentWaypoint(); //second waypoint
next = fp->getNextWaypoint(); //third waypoint (might not exist!)
setLatitude(prev->latitude);
setLongitude(prev->longitude);
setSpeed(prev->speed);
setAltitude(prev->altitude);
if (prev->speed > 0.0)
setHeading(fp->getBearing(prev->latitude, prev->longitude, curr));
else
setHeading(fp->getBearing(curr->latitude, curr->longitude, prev));
// If next doesn't exist, as in incrementally created flightplans for
// AI/Trafficmanager created plans,
// Make sure lead distance is initialized otherwise
if (next)
fp->setLeadDistance(speed, hdg, curr, next);
if (curr->crossat > -1000.0) { //use a calculated descent/climb rate
use_perf_vs = false;
tgt_vs = (curr->crossat - prev->altitude)
/ (fp->getDistanceToGo(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr)
/ 6076.0 / prev->speed*60.0);
tgt_altitude_ft = curr->crossat;
} else {
use_perf_vs = true;
tgt_altitude_ft = prev->altitude;
}
alt_lock = hdg_lock = true;
no_roll = prev->on_ground;
if (no_roll) {
Transform(); // make sure aip is initialized.
getGroundElev(60.1); // make sure it's exectuted first time around, so force a large dt value
doGroundAltitude();
}
// Make sure to announce the aircraft's position
announcePositionToController();
prevSpeed = 0;
return;
} // end of initialization
///////////////////////////////////////////////////////////////////////////
// Check Execution time (currently once every 100 ms
///////////////////////////////////////////////////////////////////////////
if ((dt_count < 0.1) || (now < fp->getStartTime())) {
//cerr << "done fp dt" << endl;
return;
} else {
dt_count = 0;
}
// check to see if we've reached the lead point for our next turn
2006-06-15 08:29:43 +00:00
double dist_to_go = fp->getDistanceToGo(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr);
//cerr << "2" << endl;
double lead_dist = fp->getLeadDistance();
//cerr << " Distance : " << dist_to_go << ": Lead distance " << lead_dist << endl;
// experimental: Use fabs, because speed can be negative (I hope) during push_back.
if (lead_dist < fabs(2*speed)) {
lead_dist = fabs(2*speed); //don't skip over the waypoint
//cerr << "Extending lead distance to " << lead_dist << endl;
}
//prev_dist_to_go = dist_to_go;
if ( dist_to_go < lead_dist ) {
if (curr->finished) { //end of the flight plan
if (fp->getRepeat())
fp->restart();
else
setDie(true);
return;
}
if (next) {
tgt_heading = fp->getBearing(curr, next);
spinCounter = 0;
}
fp->IncrementWaypoint(eraseWaypoints);
if (!(fp->getNextWaypoint()) && trafficRef)
loadNextLeg();
prev = fp->getPreviousWaypoint();
curr = fp->getCurrentWaypoint();
next = fp->getNextWaypoint();
// Now that we have incremented the waypoints, excute some traffic manager specific code
if (trafficRef) {
double userLatitude = fgGetDouble("/position/latitude-deg");
double userLongitude = fgGetDouble("/position/longitude-deg");
double course, distance;
2006-06-15 08:29:43 +00:00
SGWayPoint current(pos.getLongitudeDeg(), pos.getLatitudeDeg(), 0);
SGWayPoint user (userLongitude, userLatitude, 0);
user.CourseAndDistance(current, &course, &distance);
if ((distance * SG_METER_TO_NM) > TRAFFICTOAIDIST) {
setDie(true);
return;
}
FGAirport * dep = trafficRef->getDepartureAirport();
FGAirport * arr = trafficRef->getArrivalAirport();
if (!( dep && arr)) {
setDie(true);
return;
}
// This waypoint marks the fact that the aircraft has passed the initial taxi
// departure waypoint, so it can release the parking.
if (prev->name == "park2")
dep->getDynamics()->releaseParking(fp->getGate());
// This is the last taxi waypoint, and marks the the end of the flight plan
// so, the schedule should update and wait for the next departure time.
if (prev->name == "END") {
// make sure to wait at least 20 minutes at parking to prevent "nervous" taxi behavior
// delayed aircraft.
time_t nextDeparture = trafficRef->getDepartureTime();
if (nextDeparture < (now+1200)) {
nextDeparture = now + 1200;
}
fp->setTime(trafficRef->getDepartureTime());
}
announcePositionToController();
}
if (next) {
//cerr << "Current waypoint" << curr->name << endl;
//cerr << "Next waypoint" << next->name << endl;
fp->setLeadDistance(speed, tgt_heading, curr, next);
}
//cerr << "5.1" << endl;
if (!(prev->on_ground)) { // only update the tgt altitude from flightplan if not on the ground
tgt_altitude_ft = prev->altitude;
if (curr->crossat > -1000.0) {
//cerr << "5.1a" << endl;
use_perf_vs = false;
tgt_vs = (curr->crossat - altitude_ft) / (fp->getDistanceToGo(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr)
/ 6076.0 / speed*60.0);
//cerr << "5.1b" << endl;
tgt_altitude_ft = curr->crossat;
} else {
//cerr << "5.1c" << endl;
use_perf_vs = true;
//cerr << "5.1d" << endl;
//cerr << "Setting target altitude : " <<tgt_altitude_ft << endl;
}
}
//cerr << "6" << endl;
tgt_speed = prev->speed;
hdg_lock = alt_lock = true;
no_roll = prev->on_ground;
//cout << "Crossing waypoint: " << prev->name << endl;
//cout << " Target speed: " << tgt_speed << endl;
//cout << " Target altitude: " << tgt_altitude_ft << endl;
//cout << " Target heading: " << tgt_heading << endl << endl;
} else {
2006-06-15 08:29:43 +00:00
double calc_bearing = fp->getBearing(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr);
//cerr << "Bearing = " << calc_bearing << endl;
if (speed < 0) {
calc_bearing +=180;
if (calc_bearing > 360)
calc_bearing -= 360;
}
if (finite(calc_bearing)) {
double hdg_error = calc_bearing - tgt_heading;
if (fabs(hdg_error) > 1.0) {
TurnTo( calc_bearing );
}
} else {
cerr << "calc_bearing is not a finite number : "
<< "Speed " << speed
2006-06-15 08:29:43 +00:00
<< "pos : " << pos.getLatitudeDeg() << ", " << pos.getLongitudeDeg()
<< "waypoint " << curr->latitude << ", " << curr->longitude << endl;
cerr << "waypoint name " << curr->name;
exit(1); // FIXME
}
double speed_diff = speed - prevSpeed;
// Update the lead distance calculation if speed has changed sufficiently
// to prevent spinning (hopefully);
if (fabs(speed_diff) > 10) {
prevSpeed = speed;
fp->setLeadDistance(speed, tgt_heading, curr, next);
}
//cerr << "Done Processing FlightPlan"<< endl;
}
}
void FGAIAircraft::initializeFlightPlan()
{
}
bool FGAIAircraft::_getGearDown() const {
return ((props->getFloatValue("position/altitude-agl-ft") < 900.0)
&& (props->getFloatValue("velocities/airspeed-kt")
< performance->land_speed*1.25));
}
void FGAIAircraft::loadNextLeg() {
//delete fp;
//time_t now = time(NULL) + fgGetLong("/sim/time/warp");
//FGAIModelEntity entity;
//entity.m_class = "jet_transport";
//entity.path = modelPath.c_str();
//entity.flightplan = "none";
//entity.latitude = _getLatitude();
//entity.longitude = _getLongitude();
//entity.altitude = trafficRef->getCruiseAlt() * 100; // convert from FL to feet
//entity.speed = 450; // HACK ALERT
//entity.fp = new FGAIFlightPlan(&entity, courseToDest, i->getDepartureTime(), dep, arr);
int leg;
if ((leg = fp->getLeg()) == 10) {
trafficRef->next();
leg = 1;
fp->setLeg(leg);
//cerr << "Resetting leg : " << leg << endl;
}
//leg++;
//fp->setLeg(leg);
//cerr << "Creating leg number : " << leg << endl;
FGAirport *dep = trafficRef->getDepartureAirport();
FGAirport *arr = trafficRef->getArrivalAirport();
if (!(dep && arr)) {
setDie(true);
//cerr << "Failed to get airport in AIAircraft::ProcessFlightplan()" << endl;
//if (dep)
// cerr << "Departure " << dep->getId() << endl;
//if (arr)
// cerr << "Arrival " << arr->getId() << endl;
} else {
double cruiseAlt = trafficRef->getCruiseAlt() * 100;
//cerr << "Creating new leg using " << cruiseAlt << " as cruise altitude."<< endl;
fp->create (dep,
arr,
leg,
cruiseAlt, //(trafficRef->getCruiseAlt() * 100), // convert from FL to feet
trafficRef->getSpeed(),
_getLatitude(),
_getLongitude(),
false,
trafficRef->getRadius(),
trafficRef->getFlightType(),
acType,
company);
//prev = fp->getPreviousWaypoint();
//curr = fp->getCurrentWaypoint();
//next = fp->getNextWaypoint();
//cerr << "25" << endl;
//if (next)
// {
// //cerr << "Next waypoint" << next->name << endl;
// fp->setLeadDistance(speed, tgt_heading, curr, next);
// }
//cerr << "25.1" << endl;
//if (curr->crossat > -1000.0) {
// //cerr << "25.1a" << endl;
// use_perf_vs = false;
//
// tgt_vs = (curr->crossat - altitude_ft)/
// (fp->getDistanceToGo(pos.lat(), pos.lon(), curr)/6076.0/speed*60.0);
// //cerr << "25.1b" << endl;
// tgt_altitude_ft = curr->crossat;
//} else {
// //cerr << "25.1c" << endl;
// use_perf_vs = true;
// //cerr << "25.1d" << endl;
// tgt_altitude_ft = prev->altitude;
// //cerr << "Setting target altitude : " <<tgt_altitude_ft << endl;
// }
//cerr << "26" << endl;
//tgt_speed = prev->speed;
//hdg_lock = alt_lock = true;
//no_roll = prev->on_ground;
}
//else
//{
//delete entity.fp;
//entity.fp = new FGAIFlightPlan(&entity,
// 999, // A hack
// trafficRef->getDepartureTime(),
// trafficRef->getDepartureAirport(),
// trafficRef->getArrivalAirport(),
// false,
// acType,
// company);
//SetFlightPlan(entity.fp);
}
// Note: This code is copied from David Luff's AILocalTraffic
// Warning - ground elev determination is CPU intensive
// Either this function or the logic of how often it is called
// will almost certainly change.
void FGAIAircraft::getGroundElev(double dt) {
dt_elev_count += dt;
// Update minimally every three secs, but add some randomness
// to prevent all IA objects doing this in synchrony
if (dt_elev_count < (3.0) + (rand() % 10))
return;
else
dt_elev_count = 0;
// Only do the proper hitlist stuff if we are within visible range of the viewer.
if (!invisible) {
double visibility_meters = fgGetDouble("/environment/visibility-m");
FGViewer* vw = globals->get_current_view();
double course, distance;
2006-06-15 08:29:43 +00:00
SGWayPoint current(pos.getLongitudeDeg(), pos.getLatitudeDeg(), 0);
SGWayPoint view (vw->getLongitude_deg(), vw->getLatitude_deg(), 0);
view.CourseAndDistance(current, &course, &distance);
if(distance > visibility_meters) {
//aip.getSGLocation()->set_cur_elev_m(aptElev);
return;
}
// FIXME: make sure the pos.lat/pos.lon values are in degrees ...
double range = 500.0;
2006-06-15 08:29:43 +00:00
if (!globals->get_tile_mgr()->scenery_available(pos.getLatitudeDeg(), pos.getLongitudeDeg(), range)) {
// Try to shedule tiles for that position.
globals->get_tile_mgr()->update( aip.getSGLocation(), range );
}
// FIXME: make sure the pos.lat/pos.lon values are in degrees ...
double alt;
2006-06-15 08:29:43 +00:00
if (globals->get_scenery()->get_elevation_m(pos.getLatitudeDeg(), pos.getLongitudeDeg(), 20000.0, alt, 0))
tgt_altitude_ft = alt * SG_METER_TO_FEET;
//cerr << "Target altitude : " << tgt_altitude_ft << endl;
// if (globals->get_scenery()->get_elevation_m(pos.lat(), pos.lon(),
// 20000.0, alt))
// tgt_altitude_ft = alt * SG_METER_TO_FEET;
//cerr << "Target altitude : " << tgt_altitude_ft << endl;
}
}
void FGAIAircraft::setCallSign(const string& s) {
callsign = s;
}
void FGAIAircraft::setTACANChannelID(const string& id) {
TACAN_channel_id = id;
}
void FGAIAircraft::doGroundAltitude() {
if (fabs(altitude_ft - (tgt_altitude_ft+groundOffset)) > 1000.0)
altitude_ft = (tgt_altitude_ft + groundOffset);
else
altitude_ft += 0.1 * ((tgt_altitude_ft+groundOffset) - altitude_ft);
}
void FGAIAircraft::announcePositionToController()
{
if (trafficRef) {
//FGTaxiRoute *taxiRoute = fp->getTaxiRoute();
int leg = fp->getLeg();
//if (fp->getCurrentWaypoint()->routeIndex != 0) {
//char buffer[10];
//snprintf (buffer, 10, "%d", node);
// Note that leg was been incremented after creating the current leg, so we should use
// leg numbers here that are one higher than the number that is used to create the leg
//
switch (leg) {
case 3: // Taxiing to runway
if (trafficRef->getDepartureAirport()->getDynamics()->getGroundNetwork()->exists())
controller = trafficRef->getDepartureAirport()->getDynamics()->getGroundNetwork();
break;
case 4: //Take off tower controller
if (trafficRef->getDepartureAirport()->getDynamics())
{
controller = trafficRef->getDepartureAirport()->getDynamics()->getTowerController();
//if (trafficRef->getDepartureAirport()->getId() == "EHAM") {
//cerr << trafficRef->getCallSign() << " at runway " << fp->getRunway() << "Ready for departure "
// << trafficRef->getFlightType() << " to " << trafficRef->getArrivalAirport()->getId() << endl;
// if (controller == 0) {
//cerr << "Error in assigning controller at " << trafficRef->getDepartureAirport()->getId() << endl;
//}
}
else {
cerr << "Error: Could not find Dynamics at airport : " << trafficRef->getDepartureAirport()->getId() << endl;
}
break;
case 9: // Taxiing for parking
if (trafficRef->getArrivalAirport()->getDynamics()->getGroundNetwork()->exists())
controller = trafficRef->getArrivalAirport()->getDynamics()->getGroundNetwork();
break;
default:
controller = 0;
break;
}
if ((controller != prevController) && (prevController != 0)) {
prevController->signOff(getID());
string callsign = trafficRef->getCallSign();
if ( trafficRef->getHeavy())
callsign += "Heavy";
switch (leg) {
case 3:
cerr << callsign << " ready to taxi to runway " << fp->getRunway() << endl;
break;
case 4:
cerr << callsign << " at runway " << fp->getRunway() << "Ready for take-off. "
<< trafficRef->getFlightRules() << " to " << trafficRef->getArrivalAirport()->getId()
<< "(" << trafficRef->getArrivalAirport()->getName() << ")."<< endl;
}
}
prevController = controller;
if (controller) {
controller->announcePosition(getID(), fp, fp->getCurrentWaypoint()->routeIndex,
_getLatitude(), _getLongitude(), hdg, speed, altitude_ft,
trafficRef->getRadius(), leg);
}
}
}
void FGAIAircraft::processATC(FGATCInstruction instruction)
{
//cerr << "Processing ATC instruction (not Implimented yet)" << endl;
if (instruction.getHoldPattern ()) {
}
// Hold Position
if (instruction.getHoldPosition ()) {
if (!holdPos) {
if (trafficRef)
cerr << trafficRef->getCallSign() << "Holding Position " << endl;
holdPos = true;
}
AccelTo(0.25);
} else {
if (holdPos) {
if (trafficRef)
cerr << trafficRef->getCallSign() << " Resuming Taxi " << endl;
holdPos = false;
}
// Change speed Instruction. This can only be excecuted when there is no
// Hold position instruction.
if (instruction.getChangeSpeed ()) {
AccelTo(instruction.getSpeed());
}else {
if (fp) AccelTo(fp->getPreviousWaypoint()->speed);
}
}
if (instruction.getChangeHeading ()) {
hdg_lock = false;
TurnTo(instruction.getHeading());
} else {
if (fp) {hdg_lock = true;}
}
if (instruction.getChangeAltitude()) {
}
}