1
0
Fork 0

Make traffic take-off roll look a little better.

Expand the performance DB logic to support aliases, and select based on aircraft type as well as class. This allows to introduce some variation into AI traffic performance. Change the initial climb-out waypoints to use pitch-hold until passing 3000', which looks much more convincing
This commit is contained in:
James Turner 2012-10-05 18:12:46 +01:00
parent 1e5bd162e7
commit 7d547d1287
8 changed files with 136 additions and 66 deletions

View file

@ -107,7 +107,7 @@ void FGAIAircraft::readFromScenario(SGPropertyNode* scFileNode) {
FGAIBase::readFromScenario(scFileNode);
setPerformance(scFileNode->getStringValue("class", "jet_transport"));
setPerformance("", scFileNode->getStringValue("class", "jet_transport"));
setFlightPlan(scFileNode->getStringValue("flightplan"),
scFileNode->getBoolValue("repeat", false));
setCallSign(scFileNode->getStringValue("callsign"));
@ -131,16 +131,17 @@ void FGAIAircraft::update(double dt) {
Transform();
}
void FGAIAircraft::setPerformance(const std::string& acclass) {
static PerformanceDB perfdb; //TODO make it a global service
setPerformance(perfdb.getDataFor(acclass));
}
void FGAIAircraft::setPerformance(const std::string& acType, const std::string& acclass)
{
static PerformanceDB perfdb; //TODO make it a global service
_performance = perfdb.getDataFor(acType, acclass);
}
#if 0
void FGAIAircraft::setPerformance(PerformanceData *ps) {
_performance = ps;
}
#endif
void FGAIAircraft::Run(double dt) {
FGAIAircraft::dt = dt;

View file

@ -44,8 +44,8 @@ public:
virtual void bind();
virtual void update(double dt);
void setPerformance(const std::string& perfString);
void setPerformance(PerformanceData *ps);
void setPerformance(const std::string& acType, const std::string& perfString);
// void setPerformance(PerformanceData *ps);
void setFlightPlan(const std::string& fp, bool repat = false);
void SetFlightPlan(FGAIFlightPlan *f);
@ -68,6 +68,8 @@ public:
double getBearing(double crse);
void setAcType(const std::string& ac) { acType = ac; };
std::string getAcType() const { return acType; }
void setCompany(const std::string& comp) { company = comp;};
void announcePositionToController(); //TODO have to be public?

View file

@ -145,6 +145,7 @@ FGAIWaypoint * FGAIFlightPlan::createInAir(FGAIAircraft * ac,
wpt->setGear_down (false );
wpt->setFlaps_down (false );
wpt->setOn_ground (false );
wpt->setCrossat (aElev );
return wpt;
}
@ -427,6 +428,22 @@ bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt,
return true;
}
static double accelDistance(double v0, double v1, double accel)
{
double t = fabs(v1 - v0) / accel; // time in seconds to change velocity
// area under the v/t graph: (t * v0) + (dV / 2t) where (dV = v1 - v0)
return t * 0.5 * (v1 + v0);
}
// find the horizontal distance to gain the specific altiude, holding
// a constant pitch angle. Used to compute distance based on standard FD/AP
// PITCH mode prior to VS or CLIMB engaging. Visually, we want to avoid
// a dip in the nose angle after rotation, during initial climb-out.
static double pitchDistance(double pitchAngleDeg, double altGainM)
{
return altGainM / tan(pitchAngleDeg * SG_DEGREES_TO_RADIANS);
}
/*******************************************************************
* CreateTakeOff
* A note on units:
@ -442,27 +459,22 @@ bool FGAIFlightPlan::createTakeOff(FGAIAircraft * ac, bool firstFlight,
FGAirport * apt, double speed,
const string & fltType)
{
const double ACCEL_POINT = 105.0;
const double KNOTS_HOUR_TO_MSEC = SG_NM_TO_METER / 3600.0;
// climb-out angle in degrees. could move this to the perf-db but this
// value is pretty sane
const double INITIAL_PITCH_ANGLE = 12.5;
double accel = ac->getPerformance()->acceleration();
double vTaxi = ac->getPerformance()->vTaxi();
double vRotate = ac->getPerformance()->vRotate();
double vTakeoff = ac->getPerformance()->vTakeoff();
//double vClimb = ac->getPerformance()->vClimb();
double accelMetric = (accel * SG_NM_TO_METER) / 3600;
double vTaxiMetric = (vTaxi * SG_NM_TO_METER) / 3600;
double vRotateMetric = (vRotate * SG_NM_TO_METER) / 3600;
double vTakeoffMetric = (vTakeoff * SG_NM_TO_METER) / 3600;
//double vClimbMetric = (vClimb * SG_NM_TO_METER) / 3600;
// Acceleration = dV / dT
// Acceleration X dT = dV
// dT = dT / Acceleration
//d = (Vf^2 - Vo^2) / (2*a)
//double accelTime = (vRotate - vTaxi) / accel;
//cerr << "Using " << accelTime << " as total acceleration time" << endl;
double accelDistance =
(vRotateMetric * vRotateMetric -
vTaxiMetric * vTaxiMetric) / (2 * accelMetric);
//cerr << "Using " << accelDistance << " " << accelMetric << " " << vRotateMetric << endl;
double accelMetric = accel * KNOTS_HOUR_TO_MSEC;
double vTaxiMetric = vTaxi * KNOTS_HOUR_TO_MSEC;
double vRotateMetric = vRotate * KNOTS_HOUR_TO_MSEC;
double vTakeoffMetric = vTakeoff * KNOTS_HOUR_TO_MSEC;
FGAIWaypoint *wpt;
// Get the current active runway, based on code from David Luff
// This should actually be unified and extended to include
@ -475,41 +487,36 @@ bool FGAIFlightPlan::createTakeOff(FGAIAircraft * ac, bool firstFlight,
apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
heading);
}
FGRunway * rwy = apt->getRunwayByIdent(activeRunway);
assert( rwy != NULL );
double airportElev = apt->getElevation();
accelDistance =
(vTakeoffMetric * vTakeoffMetric -
vTaxiMetric * vTaxiMetric) / (2 * accelMetric);
//cerr << "Using " << accelDistance << " " << accelMetric << " " << vTakeoffMetric << endl;
SGGeod accelPoint = rwy->pointOnCenterline(105.0 + accelDistance);
double d = accelDistance(vTaxiMetric, vRotateMetric, accelMetric) + ACCEL_POINT;
SGGeod accelPoint = rwy->pointOnCenterline(d);
wpt = createOnGround(ac, "rotate", accelPoint, airportElev, vTakeoff);
pushBackWaypoint(wpt);
accelDistance =
((vTakeoffMetric * 1.1) * (vTakeoffMetric * 1.1) -
vTaxiMetric * vTaxiMetric) / (2 * accelMetric);
//cerr << "Using " << accelDistance << " " << accelMetric << " " << vTakeoffMetric << endl;
accelPoint = rwy->pointOnCenterline(105.0 + accelDistance);
wpt =
createOnGround(ac, "rotate", accelPoint, airportElev + 1000,
vTakeoff * 1.1);
double vRef = vTakeoffMetric + 20; // climb-out at v2 + 20kts
double gearUpDist = d + pitchDistance(INITIAL_PITCH_ANGLE, 400 * SG_FEET_TO_METER);
accelPoint = rwy->pointOnCenterline(gearUpDist);
wpt = cloneWithPos(ac, wpt, "gear-up", accelPoint);
wpt->setSpeed(vRef);
wpt->setCrossat(airportElev + 400);
wpt->setOn_ground(false);
wpt->setGear_down(false);
pushBackWaypoint(wpt);
double climbOut = d + pitchDistance(INITIAL_PITCH_ANGLE, 2000 * SG_FEET_TO_METER);
accelPoint = rwy->pointOnCenterline(climbOut);
wpt = createInAir(ac, "2000'", accelPoint, airportElev + 2000, vRef);
pushBackWaypoint(wpt);
wpt = cloneWithPos(ac, wpt, "3000 ft", rwy->end());
wpt->setAltitude(airportElev + 3000);
pushBackWaypoint(wpt);
// Finally, add two more waypoints, so that aircraft will remain under
// Tower control until they have reached the 3000 ft climb point
SGGeod pt = rwy->pointOnCenterline(5000 + rwy->lengthM() * 0.5);
wpt = cloneWithPos(ac, wpt, "5000 ft", pt);
wpt->setAltitude(airportElev + 5000);
pushBackWaypoint(wpt);
// as soon as we pass 2000', hand off to departure so the next acft can line up
// ideally the next aircraft would be able to line-up + hold but that's tricky
// with the current design.
return true;
}

View file

@ -1,3 +1,11 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "performancedb.hxx"
#include <boost/foreach.hpp>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
@ -7,7 +15,7 @@
#include <iostream>
#include <fstream>
#include "performancedb.hxx"
#include "performancedata.hxx"
using std::string;
using std::cerr;
@ -36,14 +44,28 @@ void PerformanceDB::registerPerformanceData(const std::string& id, const std::st
registerPerformanceData(id, new PerformanceData(filename));
}
PerformanceData* PerformanceDB::getDataFor(const std::string& id) {
if (_db.find(id) == _db.end()) // id not found -> return jet_transport data
PerformanceData* PerformanceDB::getDataFor(const string& acType, const string& acClass)
{
// first, try with the specific aircraft type, such as 738 or A322
if (_db.find(acType) != _db.end()) {
return _db[acType];
}
string alias = findAlias(acType);
if (_db.find(alias) != _db.end()) {
return _db[alias];
}
SG_LOG(SG_AI, SG_INFO, "no performance data for " << acType);
if (_db.find(acClass) == _db.end()) {
return _db["jet_transport"];
return _db[id];
}
return _db[acClass];
}
void PerformanceDB::load(SGPath filename) {
void PerformanceDB::load(const SGPath& filename) {
string name;
double acceleration;
double deceleration;
@ -67,8 +89,9 @@ void PerformanceDB::load(SGPath filename) {
}
SGPropertyNode * node = root.getNode("performancedb");
for (int i = 0; i < node->nChildren(); i++) {
for (int i = 0; i < node->nChildren(); i++) {
SGPropertyNode * db_node = node->getChild(i);
if (!strcmp(db_node->getName(), "aircraft")) {
name = db_node->getStringValue("type", "heavy_jet");
acceleration = db_node->getDoubleValue("acceleration-kts-hour", 4.0);
deceleration = db_node->getDoubleValue("deceleration-kts-hour", 2.0);
@ -85,6 +108,32 @@ void PerformanceDB::load(SGPath filename) {
registerPerformanceData(name, new PerformanceData(
acceleration, deceleration, climbRate, descentRate, vRotate, vTakeOff, vClimb, vCruise, vDescent, vApproach, vTouchdown, vTaxi));
}
} else if (!strcmp(db_node->getName(), "alias")) {
string alias(db_node->getStringValue("alias"));
if (alias.empty()) {
SG_LOG(SG_AI, SG_ALERT, "performance DB alias entry with no <alias> definition");
continue;
}
BOOST_FOREACH(SGPropertyNode* matchNode, db_node->getChildren("match")) {
string match(matchNode->getStringValue());
_aliases.push_back(StringPair(match, alias));
}
} else {
SG_LOG(SG_AI, SG_ALERT, "unrecognized performance DB entry:" << db_node->getName());
}
} // of nodes iteration
}
string PerformanceDB::findAlias(const string& acType) const
{
BOOST_FOREACH(const StringPair& alias, _aliases) {
if (acType.find(alias.first) == 0) { // matched!
return alias.second;
}
} // of alias iteration
return string();
}

View file

@ -2,10 +2,11 @@
#define PERFORMANCEDB_HXX
#include <string>
#include <vector>
#include <map>
#include <vector>
#include "performancedata.hxx"
class PerformanceData;
class SGPath;
/**
* Registry for performance data.
@ -25,11 +26,23 @@ public:
void registerPerformanceData(const std::string& id, PerformanceData* data);
void registerPerformanceData(const std::string& id, const std::string& filename);
PerformanceData* getDataFor(const std::string& id);
void load(SGPath path);
/**
* get performance data for an aircraft type / class. Type is specific, eg
* '738' or 'A319'. Class is more generic, such as 'jet_transport'.
*/
PerformanceData* getDataFor(const std::string& acType, const std::string& acClass);
void load(const SGPath& path);
private:
std::map<std::string, PerformanceData*> _db;
std::string findAlias(const std::string& acType) const;
typedef std::pair<std::string, std::string> StringPair;
/// alias list, to allow type/class names to share data. This is used to merge
/// related types together. Note it's ordered, and not a map since we permit
/// partial matches when merging - the first matching alias is used.
std::vector<StringPair> _aliases;
};
#endif

View file

@ -74,7 +74,7 @@ void FGATCManager::init() {
ai_ac.setLongitude( longitude );
ai_ac.setLatitude ( latitude );
ai_ac.setAltitude ( altitude );
ai_ac.setPerformance("jet_transport");
ai_ac.setPerformance("", "jet_transport");
// NEXT UP: Create a traffic Schedule and fill that with appropriate information. This we can use to flight planning.
// Note that these are currently only defaults.

View file

@ -346,7 +346,7 @@ bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots
}
FGAIAircraft *aircraft = new FGAIAircraft(this);
aircraft->setPerformance(m_class); //"jet_transport";
aircraft->setPerformance(acType, m_class); //"jet_transport";
aircraft->setCompany(airline); //i->getAirline();
aircraft->setAcType(acType); //i->getAcType();
aircraft->setPath(modelPath.c_str());

View file

@ -782,8 +782,6 @@ void FGTrafficManager::endAircraft()
acCounter++;
requiredAircraft = "";
homePort = "";
SG_LOG(SG_GENERAL, SG_BULK, "Reading aircraft : "
<< registration << " with prioritization score " << score);
score = 0;
}