diff --git a/src/AIModel/AIAircraft.cxx b/src/AIModel/AIAircraft.cxx index 82f21a4e4..fe93d938b 100644 --- a/src/AIModel/AIAircraft.cxx +++ b/src/AIModel/AIAircraft.cxx @@ -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; diff --git a/src/AIModel/AIAircraft.hxx b/src/AIModel/AIAircraft.hxx index ea2d08e7c..73e98aeb3 100644 --- a/src/AIModel/AIAircraft.hxx +++ b/src/AIModel/AIAircraft.hxx @@ -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? diff --git a/src/AIModel/AIFlightPlanCreate.cxx b/src/AIModel/AIFlightPlanCreate.cxx index c49b4abe8..47026b60e 100644 --- a/src/AIModel/AIFlightPlanCreate.cxx +++ b/src/AIModel/AIFlightPlanCreate.cxx @@ -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; } diff --git a/src/AIModel/performancedb.cxx b/src/AIModel/performancedb.cxx index 37660e6a0..f218e851d 100644 --- a/src/AIModel/performancedb.cxx +++ b/src/AIModel/performancedb.cxx @@ -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(); +} + + diff --git a/src/AIModel/performancedb.hxx b/src/AIModel/performancedb.hxx index 61d040da0..ad3066cb8 100644 --- a/src/AIModel/performancedb.hxx +++ b/src/AIModel/performancedb.hxx @@ -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 diff --git a/src/ATC/atc_mgr.cxx b/src/ATC/atc_mgr.cxx index c43721739..bbfc73311 100644 --- a/src/ATC/atc_mgr.cxx +++ b/src/ATC/atc_mgr.cxx @@ -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. diff --git a/src/Traffic/Schedule.cxx b/src/Traffic/Schedule.cxx index 9af6a0c27..3d4db5cb7 100644 --- a/src/Traffic/Schedule.cxx +++ b/src/Traffic/Schedule.cxx @@ -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()); diff --git a/src/Traffic/TrafficMgr.cxx b/src/Traffic/TrafficMgr.cxx index 2510b7a62..464dca5e0 100644 --- a/src/Traffic/TrafficMgr.cxx +++ b/src/Traffic/TrafficMgr.cxx @@ -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; }