Make landings and takeoffs look more correct; tweak climb-out and touchdown phases in particular, so the turn to destination heading occurs earlier on climb out, and touchdown occurs close the GS transmitter / some distance down the runway from the beginning.
setAltitude(prev->getAltitude());
if (prev->getSpeed() > 0.0)
- setHeading(fp->getBearing(prev->getLatitude(), prev->getLongitude(), curr));
+ setHeading(fp->getBearing(prev, curr));
else
- setHeading(fp->getBearing(curr->getLatitude(), curr->getLongitude(), prev));
+ setHeading(fp->getBearing(curr, prev));
// If next doesn't exist, as in incrementally created flightplans for
// AI/Trafficmanager created plans,
// << " Ground target speed " << groundTargetSpeed << endl;
double bearing = 0;
// don't do bearing calculations for ground traffic
- bearing = getBearing(fp->getBearing(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr));
+ bearing = getBearing(fp->getBearing(pos, curr));
if (bearing < minBearing) {
minBearing = bearing;
if (minBearing < 10) {
* @param curr
*/
void FGAIAircraft::controlHeading(FGAIWaypoint* curr) {
- double calc_bearing = fp->getBearing(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr);
+ double calc_bearing = fp->getBearing(pos, curr);
//cerr << "Bearing = " << calc_bearing << endl;
if (speed < 0) {
calc_bearing +=180;
- if (calc_bearing > 360)
- calc_bearing -= 360;
+ SG_NORMALIZE_RANGE(calc_bearing, 0.0, 360.0);
}
if (finite(calc_bearing)) {
}
}
-void FGAIAircraft::updatePosition() {
- // convert speed to degrees per second
- double speed_north_deg_sec = cos( hdg * SGD_DEGREES_TO_RADIANS )
- * speed * 1.686 / ft_per_deg_lat;
- double speed_east_deg_sec = sin( hdg * SGD_DEGREES_TO_RADIANS )
- * speed * 1.686 / ft_per_deg_lon;
-
- // set new position
- pos.setLatitudeDeg( pos.getLatitudeDeg() + speed_north_deg_sec * dt);
- pos.setLongitudeDeg( pos.getLongitudeDeg() + speed_east_deg_sec * dt);
-}
-
-
void FGAIAircraft::updateHeading() {
// adjust heading based on current bank angle
if (roll == 0.0)
// find target vertical speed
if (use_perf_vs) {
if (altitude_ft < tgt_altitude_ft) {
- tgt_vs = tgt_altitude_ft - altitude_ft;
- if (tgt_vs > _performance->climbRate())
- tgt_vs = _performance->climbRate();
+ tgt_vs = std::min(tgt_altitude_ft - altitude_ft, _performance->climbRate());
} else {
- tgt_vs = tgt_altitude_ft - altitude_ft;
- if (tgt_vs < (-_performance->descentRate()))
- tgt_vs = -_performance->descentRate();
+ tgt_vs = std::max(tgt_altitude_ft - altitude_ft, -_performance->descentRate());
}
} else {
double vert_dist_ft = fp->getCurrentWaypoint()->getCrossat() - altitude_ft;
void FGAIAircraft::updateActualState() {
//update current state
//TODO have a single tgt_speed and check speed limit on ground on setting tgt_speed
- updatePosition();
+ double distance = speed * SG_KT_TO_MPS * dt;
+ pos = SGGeodesy::direct(pos, hdg, distance);
+
if (onGround())
speed = _performance->actualSpeed(this, groundTargetSpeed, dt, holdPos);
void updatePrimaryTargetValues(bool& flightplanActive, bool& aiOutOfSight);
void updateSecondaryTargetValues();
- void updatePosition();
void updateHeading();
void updateBankAngleTarget();
void updateVerticalSpeedTarget();
#include <Main/fg_props.hxx>
#include <Main/fg_init.hxx>
#include <Airports/simple.hxx>
+#include <Airports/dynamics.hxx>
#include <Airports/runways.hxx>
#include <Airports/groundnetwork.hxx>
lastNodeVisited = 0;
taxiRoute = 0;
- SGPath path( globals->get_fg_root() );
- path.append( ("/AI/FlightPlans/" + filename).c_str() );
- SGPropertyNode root;
-
- try {
- readProperties(path.str(), &root);
- } catch (const sg_exception &) {
- SG_LOG(SG_AI, SG_ALERT,
- "Error reading AI flight plan: " << path.str());
- // cout << path.str() << endl;
- return;
- }
-
- SGPropertyNode * node = root.getNode("flightplan");
- for (int i = 0; i < node->nChildren(); i++) {
- //cout << "Reading waypoint " << i << endl;
- FGAIWaypoint* wpt = new FGAIWaypoint;
- SGPropertyNode * wpt_node = node->getChild(i);
- wpt->setName (wpt_node->getStringValue("name", "END" ));
- wpt->setLatitude (wpt_node->getDoubleValue("lat", 0 ));
- wpt->setLongitude (wpt_node->getDoubleValue("lon", 0 ));
- wpt->setAltitude (wpt_node->getDoubleValue("alt", 0 ));
- wpt->setSpeed (wpt_node->getDoubleValue("ktas", 0 ));
- wpt->setCrossat (wpt_node->getDoubleValue("crossat", -10000 ));
- wpt->setGear_down (wpt_node->getBoolValue("gear-down", false ));
- wpt->setFlaps_down (wpt_node->getBoolValue("flaps-down", false ));
- wpt->setOn_ground (wpt_node->getBoolValue("on-ground", false ));
- wpt->setTime_sec (wpt_node->getDoubleValue("time-sec", 0 ));
- wpt->setTime (wpt_node->getStringValue("time", "" ));
-
- if (wpt->getName() == "END") wpt->setFinished(true);
- else wpt->setFinished(false);
-
- pushBackWaypoint( wpt );
- }
- wpt_iterator = waypoints.begin();
- isValid = true;
- //cout << waypoints.size() << " waypoints read." << endl;
+ isValid = parseProperties(filename);
}
double speed,
const string& fltType,
const string& acType,
- const string& airline)
+ const string& airline) :
+ departure(dep),
+ arrival(arr)
{
sid = 0;
repeat = false;
lastNodeVisited = 0;
taxiRoute = 0;
- SGPath path( globals->get_fg_root() );
- path.append( "/AI/FlightPlans" );
- path.append( p );
-
- SGPropertyNode root;
- isValid = true;
-
- // This is a bit of a hack:
- // Normally the value of course will be used to evaluate whether
- // or not a waypoint will be used for midair initialization of
- // an AI aircraft. However, if a course value of 999 will be passed
- // when an update request is received, which will by definition always be
- // on the ground and should include all waypoints.
-// bool useInitialWayPoint = true;
-// bool useCurrentWayPoint = false;
-// if (course == 999)
-// {
-// useInitialWayPoint = false;
-// useCurrentWayPoint = true;
-// }
-
- if (path.exists())
- {
- try
- {
- readProperties(path.str(), &root);
-
- SGPropertyNode * node = root.getNode("flightplan");
-
- //pushBackWaypoint( init_waypoint );
- for (int i = 0; i < node->nChildren(); i++) {
- //cout << "Reading waypoint " << i << endl;
- FGAIWaypoint* wpt = new FGAIWaypoint;
- SGPropertyNode * wpt_node = node->getChild(i);
- wpt->setName (wpt_node->getStringValue("name", "END" ));
- wpt->setLatitude (wpt_node->getDoubleValue("lat", 0 ));
- wpt->setLongitude (wpt_node->getDoubleValue("lon", 0 ));
- wpt->setAltitude (wpt_node->getDoubleValue("alt", 0 ));
- wpt->setSpeed (wpt_node->getDoubleValue("ktas", 0 ));
- wpt->setCrossat (wpt_node->getDoubleValue("crossat", -10000 ));
- wpt->setGear_down (wpt_node->getBoolValue("gear-down", false ));
- wpt->setFlaps_down (wpt_node->getBoolValue("flaps-down", false ));
-
- if (wpt->getName() == "END") wpt->setFinished(true);
- else wpt->setFinished(false);
- pushBackWaypoint(wpt);
- } // of node loop
- wpt_iterator = waypoints.begin();
- } catch (const sg_exception &e) {
- SG_LOG(SG_AI, SG_WARN, "Error reading AI flight plan: " <<
- e.getMessage() << " from " << e.getOrigin());
- }
+ if (parseProperties(p)) {
+ isValid = true;
} else {
- // cout << path.str() << endl;
- // cout << "Trying to create this plan dynamically" << endl;
- // cout << "Route from " << dep->id << " to " << arr->id << endl;
- time_t now = time(NULL) + fgGetLong("/sim/time/warp");
- time_t timeDiff = now-start;
- leg = 1;
-
- if ((timeDiff > 60) && (timeDiff < 1500))
- leg = 2;
- //else if ((timeDiff >= 1200) && (timeDiff < 1500)) {
- //leg = 3;
- //ac->setTakeOffStatus(2);
- //}
- else if ((timeDiff >= 1500) && (timeDiff < 2000))
- leg = 4;
- else if (timeDiff >= 2000)
- leg = 5;
- /*
- if (timeDiff >= 2000)
- leg = 5;
- */
- SG_LOG(SG_AI, SG_INFO, "Route from " << dep->getId() << " to " << arr->getId() << ". Set leg to : " << leg << " " << ac->getTrafficRef()->getCallSign());
- wpt_iterator = waypoints.begin();
- bool dist = 0;
- isValid = create(ac, dep,arr, leg, alt, speed, lat, lon,
- firstLeg, radius, fltType, acType, airline, dist);
- wpt_iterator = waypoints.begin();
- }
-
+ createWaypoints(ac, course, start, dep, arr, firstLeg, radius,
+ alt, lat, lon, speed, fltType, acType, airline);
+ }
}
-
-
-
FGAIFlightPlan::~FGAIFlightPlan()
{
deleteWaypoints();
delete taxiRoute;
+
+// if we're parked at a gate, release it
+ if (gateId >= 0) {
+ FGAirport* apt = (leg >= 7) ? arrival : departure;
+ if (apt) {
+ SG_LOG(SG_AI, SG_INFO, "releasing parking gate " << gateId << " at " << apt->ident());
+ apt->getDynamics()->releaseParking(gateId);
+ }
+ }
+}
+
+void FGAIFlightPlan::createWaypoints(FGAIAircraft *ac,
+ double course,
+ time_t start,
+ FGAirport *dep,
+ FGAirport *arr,
+ bool firstLeg,
+ double radius,
+ double alt,
+ double lat,
+ double lon,
+ double speed,
+ const string& fltType,
+ const string& acType,
+ const string& airline)
+{
+ time_t now = time(NULL) + fgGetLong("/sim/time/warp");
+ time_t timeDiff = now-start;
+ leg = 1;
+
+ if ((timeDiff > 60) && (timeDiff < 1500))
+ leg = 2;
+ //else if ((timeDiff >= 1200) && (timeDiff < 1500)) {
+ //leg = 3;
+ //ac->setTakeOffStatus(2);
+ //}
+ else if ((timeDiff >= 1500) && (timeDiff < 2000))
+ leg = 4;
+ else if (timeDiff >= 2000)
+ leg = 5;
+ /*
+ if (timeDiff >= 2000)
+ leg = 5;
+ */
+ SG_LOG(SG_AI, SG_INFO, "Route from " << dep->getId() << " to " << arr->getId() << ". Set leg to : " << leg << " " << ac->getTrafficRef()->getCallSign());
+ wpt_iterator = waypoints.begin();
+ bool dist = 0;
+ isValid = create(ac, dep, arr, leg, alt, speed, lat, lon,
+ firstLeg, radius, fltType, acType, airline, dist);
+ wpt_iterator = waypoints.begin();
+
}
+bool FGAIFlightPlan::parseProperties(const std::string& filename)
+{
+ SGPath path( globals->get_fg_root() );
+ path.append( "/AI/FlightPlans/" + filename );
+ if (!path.exists()) {
+ return false;
+ }
+
+ SGPropertyNode root;
+ try {
+ readProperties(path.str(), &root);
+ } catch (const sg_exception &e) {
+ SG_LOG(SG_AI, SG_ALERT, "Error reading AI flight plan: " << path.str()
+ << "message:" << e.getFormattedMessage());
+ return false;
+ }
+
+ SGPropertyNode * node = root.getNode("flightplan");
+ for (int i = 0; i < node->nChildren(); i++) {
+ FGAIWaypoint* wpt = new FGAIWaypoint;
+ SGPropertyNode * wpt_node = node->getChild(i);
+ wpt->setName (wpt_node->getStringValue("name", "END" ));
+ wpt->setLatitude (wpt_node->getDoubleValue("lat", 0 ));
+ wpt->setLongitude (wpt_node->getDoubleValue("lon", 0 ));
+ wpt->setAltitude (wpt_node->getDoubleValue("alt", 0 ));
+ wpt->setSpeed (wpt_node->getDoubleValue("ktas", 0 ));
+ wpt->setCrossat (wpt_node->getDoubleValue("crossat", -10000 ));
+ wpt->setGear_down (wpt_node->getBoolValue("gear-down", false ));
+ wpt->setFlaps_down (wpt_node->getBoolValue("flaps-down", false ));
+ wpt->setOn_ground (wpt_node->getBoolValue("on-ground", false ));
+ wpt->setTime_sec (wpt_node->getDoubleValue("time-sec", 0 ));
+ wpt->setTime (wpt_node->getStringValue("time", "" ));
+ wpt->setFinished ((wpt->getName() == "END"));
+ pushBackWaypoint( wpt );
+ }
+
+ wpt_iterator = waypoints.begin();
+ return true;
+}
FGAIWaypoint* const FGAIFlightPlan::getPreviousWaypoint( void ) const
{
return SGGeodesy::courseDeg(first->getPos(), second->getPos());
}
-
-double FGAIFlightPlan::getBearing(double lat, double lon, FGAIWaypoint* wp) const
+double FGAIFlightPlan::getBearing(const SGGeod& aPos, FGAIWaypoint* wp) const
{
- return SGGeodesy::courseDeg(SGGeod::fromDeg(lon, lat), wp->getPos());
+ return SGGeodesy::courseDeg(aPos, wp->getPos());
}
void FGAIFlightPlan::deleteWaypoints()
#ifndef _FG_AIFLIGHTPLAN_HXX
#define _FG_AIFLIGHTPLAN_HXX
-#include <simgear/compiler.h>
#include <vector>
#include <string>
+
+#include <simgear/compiler.h>
#include <simgear/math/SGMath.hxx>
+#include <simgear/structure/SGSharedPtr.hxx>
+// forward decls
class FGTaxiRoute;
class FGRunway;
class FGAIAircraft;
class FGAirport;
+typedef SGSharedPtr<FGAirport> FGAirportRef;
+
class FGAIWaypoint {
private:
std::string name;
void setLeadDistance(double distance_ft);
double getLeadDistance( void ) const {return lead_distance;}
double getBearing(FGAIWaypoint* previous, FGAIWaypoint* next) const;
- double getBearing(double lat, double lon, FGAIWaypoint* next) const;
+ double getBearing(const SGGeod& aPos, FGAIWaypoint* next) const;
+
double checkTrackLength(std::string wptName);
time_t getStartTime() const { return start_time; }
time_t getArrivalTime() const { return arrivalTime; }
FGTaxiRoute *taxiRoute;
std::string name;
bool isValid;
-
+ FGAirportRef departure, arrival;
+
void createPushBackFallBack(FGAIAircraft *, bool, FGAirport*, double radius, const std::string&, const std::string&, const std::string&);
- bool createClimb(FGAIAircraft *, bool, FGAirport *, double, double, const std::string&);
+ bool createClimb(FGAIAircraft *, bool, FGAirport *, FGAirport* arrival, double, double, const std::string&);
bool createCruise(FGAIAircraft *, bool, FGAirport*, FGAirport*, double, double, double, double, const std::string&);
bool createDescent(FGAIAircraft *, FGAirport *, double latitude, double longitude, double speed, double alt,const std::string&, double distance);
bool createLanding(FGAIAircraft *, FGAirport *, const std::string&);
//void createCruiseFallback(bool, FGAirport*, FGAirport*, double, double, double, double);
void evaluateRoutePart(double deplat, double deplon, double arrlat, double arrlon);
+
+ /**
+ * look for and parse an PropertyList flight-plan file - essentially
+ * a flat list waypoint objects, encoded to properties
+ */
+ bool parseProperties(const std::string& filename);
+
+ void createWaypoints(FGAIAircraft *ac,
+ double course,
+ time_t start,
+ FGAirport *dep,
+ FGAirport *arr,
+ bool firstLeg,
+ double radius,
+ double alt,
+ double lat,
+ double lon,
+ double speed,
+ const std::string& fltType,
+ const std::string& acType,
+ const std::string& airline);
+
public:
wpt_vector_iterator getFirstWayPoint() { return waypoints.begin(); };
wpt_vector_iterator getLastWayPoint() { return waypoints.end(); };
#include <Environment/environment_mgr.hxx>
#include <Environment/environment.hxx>
#include <FDM/LaRCsim/basic_aero.h>
+#include <Navaids/navrecord.hxx>
/* FGAIFlightPlan::create()
retVal = createTakeOff(ac, firstFlight, dep, speed, fltType);
break;
case 4:
- retVal = createClimb(ac, firstFlight, dep, speed, alt, fltType);
+ retVal = createClimb(ac, firstFlight, dep, arr, speed, alt, fltType);
break;
case 5:
retVal = createCruise(ac, firstFlight, dep, arr, latitude, longitude, 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;
+ const double INITIAL_PITCH_ANGLE = 10.0;
double accel = ac->getPerformance()->acceleration();
double vTaxi = ac->getPerformance()->vTaxi();
double vRotate = ac->getPerformance()->vRotate();
double vTakeoff = ac->getPerformance()->vTakeoff();
- 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;
+ double accelMetric = accel * SG_KT_TO_MPS;
+ double vTaxiMetric = vTaxi * SG_KT_TO_MPS;
+ double vRotateMetric = vRotate * SG_KT_TO_MPS;
FGAIWaypoint *wpt;
// Get the current active runway, based on code from David Luff
wpt = createOnGround(ac, "rotate", accelPoint, airportElev, vTakeoff);
pushBackWaypoint(wpt);
- double vRef = vTakeoffMetric + 20; // climb-out at v2 + 20kts
+ double vRef = vTakeoff + 20; // climb-out at v2 + 20kts
+
double gearUpDist = d + pitchDistance(INITIAL_PITCH_ANGLE, 400 * SG_FEET_TO_METER);
accelPoint = rwy->pointOnCenterline(gearUpDist);
wpt->setGear_down(false);
pushBackWaypoint(wpt);
+ // limit climbout speed to 240kts below 10000'
+ double vClimbBelow10000 = std::min(240.0, ac->getPerformance()->vClimb());
+
+ // create two climb-out points. This is important becuase the first climb point will
+ // be a (sometimes large) turn towards the destination, and we don't want to
+ // commence that turn below 2000'
double climbOut = d + pitchDistance(INITIAL_PITCH_ANGLE, 2000 * SG_FEET_TO_METER);
accelPoint = rwy->pointOnCenterline(climbOut);
- wpt = createInAir(ac, "2000'", accelPoint, airportElev + 2000, vRef);
+ wpt = createInAir(ac, "2000'", accelPoint, airportElev + 2000, vClimbBelow10000);
+ pushBackWaypoint(wpt);
+
+ climbOut = d + pitchDistance(INITIAL_PITCH_ANGLE, 2500 * SG_FEET_TO_METER);
+ accelPoint = rwy->pointOnCenterline(climbOut);
+ wpt = createInAir(ac, "2500'", accelPoint, airportElev + 2500, vClimbBelow10000);
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;
}
* initialize the Aircraft at the parking location
******************************************************************/
bool FGAIFlightPlan::createClimb(FGAIAircraft * ac, bool firstFlight,
- FGAirport * apt, double speed, double alt,
+ FGAirport * apt, FGAirport* arrival,
+ double speed, double alt,
const string & fltType)
{
FGAIWaypoint *wpt;
-// bool planLoaded = false;
- string fPLName;
+ // string fPLName;
double vClimb = ac->getPerformance()->vClimb();
-
+
if (firstFlight) {
string rwyClass = getRunwayClassFromTrafficType(fltType);
double heading = ac->getTrafficRef()->getCourse();
//cerr << " Cloning waypoint " << endl;
}
} else {
- FGRunway * rwy = apt->getRunwayByIdent(activeRunway);
- assert( rwy != NULL );
-
- SGGeod climb1 = rwy->pointOnCenterline(10 * SG_NM_TO_METER);
+ FGRunway* runway = apt->getRunwayByIdent(activeRunway);
+ SGGeod cur = runway->end();
+ if (!waypoints.empty()) {
+ cur = waypoints.back()->getPos();
+ }
+
+ // compute course towards destination
+ double course = SGGeodesy::courseDeg(cur, arrival->geod());
+
+ SGGeod climb1 = SGGeodesy::direct(cur, course, 10 * SG_NM_TO_METER);
wpt = createInAir(ac, "10000ft climb", climb1, 10000, vClimb);
wpt->setGear_down(true);
wpt->setFlaps_down(true);
pushBackWaypoint(wpt);
- SGGeod climb2 = rwy->pointOnCenterline(20 * SG_NM_TO_METER);
- wpt = cloneWithPos(ac, wpt, "18000ft climb", climb2);
- wpt->setAltitude(18000);
+ SGGeod climb2 = SGGeodesy::direct(cur, course, 20 * SG_NM_TO_METER);
+ wpt = createInAir(ac, "18000ft climb", climb2, 18000, vClimb);
pushBackWaypoint(wpt);
}
return true;
FGAIWaypoint *wpt;
double vDescent = ac->getPerformance()->vDescent();
double vApproach = ac->getPerformance()->vApproach();
- double vTouchdown = ac->getPerformance()->vTouchdown();
-
//Beginning of Descent
string rwyClass = getRunwayClassFromTrafficType(fltType);
// The approach leg should bring the aircraft to approximately 4-6 nm out, after which the landing phase should take over.
//cerr << "Phase 3: Approach" << endl;
- double tgt_speed = vApproach;
- distanceOut -= distanceCovered;
- double touchDownPoint = 0; //(rwy->lengthM() * 0.1);
- for (int i = 1; i < nPoints; i++) {
- SGGeod result;
- double currentDist = i * (distanceOut / nPoints);
- //double currentAltitude =
- // apt->getElevation() + 2000 - (i * 2000 / (nPoints-1));
- double alt = currentAltitude - (i * 2000 / (nPoints - 1));
- snprintf(buffer, 16, "final%03d", i);
- result = rwy->pointOnCenterline((-distanceOut) + currentDist + touchDownPoint);
- if (i == nPoints - 30) {
- tgt_speed = vTouchdown;
- }
- wpt = createInAir(ac, buffer, result, alt, tgt_speed);
- wpt->setCrossat(alt);
- wpt->setTrackLength((distanceOut / nPoints));
- // account for the extra distance due to an extended downwind leg
- if (i == 1) {
- wpt->setTrackLength(wpt->getTrackLength() + distanceCovered);
- }
- //cerr << "Track Length : " << wpt->trackLength;
- pushBackWaypoint(wpt);
- //if (apt->ident() == fgGetString("/sim/presets/airport-id")) {
- // cerr << " Position : " << result.getLatitudeDeg() << " " << result.getLongitudeDeg() << " " << currentAltitude << " " << apt->getElevation() << " " << distanceOut << endl;
- //}
- }
-
+
//cerr << "Done" << endl;
// Erase the two bogus BOD points: Note check for conflicts with scripted AI flightPlans
ac->resetPositionFromFlightPlan();
}
waypoints[1]->setName( (waypoints[1]->getName() + string("legend")));
- waypoints.back()->setName(waypoints.back()->getName() + "LandingThreshold");
return true;
}
+/**
+ * compute the distance along the centerline, to the ILS glideslope
+ * transmitter. Return -1 if there's no GS for the runway
+ */
+static double runwayGlideslopeTouchdownDistance(FGRunway* rwy)
+{
+ FGNavRecord* gs = rwy->glideslope();
+ if (!gs) {
+ return -1;
+ }
+
+ SGVec3d runwayPosCart = SGVec3d::fromGeod(rwy->pointOnCenterline(0.0));
+ // compute a unit vector in ECF cartesian space, from the runway beginning to the end
+ SGVec3d runwayDirectionVec = normalize(SGVec3d::fromGeod(rwy->end()) - runwayPosCart);
+ SGVec3d gsTransmitterVec = gs->cart() - runwayPosCart;
+
+// project the gsTransmitterVec along the runwayDirctionVec to get out
+// final value (in metres)
+ double dist = dot(runwayDirectionVec, gsTransmitterVec);
+ return dist;
+}
+
/*******************************************************************
* CreateLanding
* Create a flight path from the "permision to land" point (currently
double vTouchdown = ac->getPerformance()->vTouchdown();
double vTaxi = ac->getPerformance()->vTaxi();
double decel = ac->getPerformance()->deceleration() * 1.4;
-
+ double vApproach = ac->getPerformance()->vApproach();
+
double vTouchdownMetric = (vTouchdown * SG_NM_TO_METER) / 3600;
double vTaxiMetric = (vTaxi * SG_NM_TO_METER) / 3600;
double decelMetric = (decel * SG_NM_TO_METER) / 3600;
- //string rwyClass = getRunwayClassFromTrafficType(fltType);
- //double heading = ac->getTrafficRef()->getCourse();
- //apt->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway, heading);
- //rwy = apt->getRunwayByIdent(activeRunway);
-
-
- FGAIWaypoint *wpt;
- //double aptElev = apt->getElevation();
- double currElev = 0;
char buffer[12];
FGRunway * rwy = apt->getRunwayByIdent(activeRunway);
assert( rwy != NULL );
- SGGeod refPoint = rwy->pointOnCenterline(0);
- FGTaxiNode *tn = 0;
- if (apt->getDynamics()->getGroundNetwork()) {
- int node = apt->getDynamics()->getGroundNetwork()->findNearestNode(refPoint);
- tn = apt->getDynamics()->getGroundNetwork()->findNode(node);
- }
- if (tn) {
- currElev = tn->getElevationFt(apt->getElevation());
- } else {
- currElev = apt->getElevation();
+ SGGeod threshold = rwy->threshold();
+ double currElev = threshold.getElevationFt();
+
+ double touchdownDistance = runwayGlideslopeTouchdownDistance(rwy);
+ if (touchdownDistance < 0.0) {
+ double landingLength = rwy->lengthM() - (rwy->displacedThresholdM());
+ // touchdown 25% of the way along the landing area
+ touchdownDistance = rwy->displacedThresholdM() + (landingLength * 0.25);
}
-
-
+
SGGeod coord;
-
-
- /*double distanceOut = rwy->lengthM() * .1;
- double nPoints = 20;
- for (int i = 1; i < nPoints; i++) {
- snprintf(buffer, 12, "flare%d", i);
- double currentDist = i * (distanceOut / nPoints);
- double currentAltitude = apt->getElevation() + 20 - (i * 20 / nPoints);
- coord = rwy->pointOnCenterline((currentDist * (i / nPoints)));
- wpt = createInAir(ac, buffer, coord, currentAltitude, (vTouchdown));
- }*/
- double rolloutDistance =
- (vTouchdownMetric * vTouchdownMetric - vTaxiMetric * vTaxiMetric) / (2 * decelMetric);
- //cerr << " touchdown speed = " << vTouchdown << ". Rollout distance " << rolloutDistance << endl;
+ // find glideslope entry point, 2000' above touchdown elevation
+ double glideslopeEntry = -((2000 * SG_FEET_TO_METER) / tan(3.0)) + touchdownDistance;
+ FGAIWaypoint *wpt = createInAir(ac, "Glideslope begin", rwy->pointOnCenterline(glideslopeEntry),
+ currElev + 2000, vApproach);
+ pushBackWaypoint(wpt);
+
+ // deceleration point, 500' above touchdown elevation - slow from approach speed
+ // to touchdown speed
+ double decelPoint = -((500 * SG_FEET_TO_METER) / tan(3.0)) + touchdownDistance;
+ wpt = createInAir(ac, "500' decel", rwy->pointOnCenterline(decelPoint),
+ currElev + 2000, vTouchdown);
+ pushBackWaypoint(wpt);
+
+ // compute elevation above the runway start, based on a 3-degree glideslope
+ double heightAboveRunwayStart = touchdownDistance *
+ tan(3.0 * SG_DEGREES_TO_RADIANS) * SG_METER_TO_FEET;
+ wpt = createInAir(ac, "CrossThreshold", rwy->begin(),
+ heightAboveRunwayStart + currElev, vTouchdown);
+ pushBackWaypoint(wpt);
+
+ double rolloutDistance = accelDistance(vTouchdownMetric, vTaxiMetric, decelMetric);
+
int nPoints = 50;
for (int i = 1; i < nPoints; i++) {
snprintf(buffer, 12, "landing03%d", i);
-
- coord = rwy->pointOnCenterline((rolloutDistance * ((double) i / (double) nPoints)));
- wpt = createOnGround(ac, buffer, coord, currElev, 2*vTaxi);
+ double t = ((double) i) / nPoints;
+ coord = rwy->pointOnCenterline(touchdownDistance + (rolloutDistance * t));
+ double vel = (vTouchdownMetric * (1.0 - t)) + (vTaxiMetric * t);
+ wpt = createOnGround(ac, buffer, coord, currElev, vel);
wpt->setCrossat(currElev);
pushBackWaypoint(wpt);
}
+
wpt->setSpeed(vTaxi);
- double mindist = 1.1 * rolloutDistance;
- double maxdist = rwy->lengthM();
- //cerr << "Finding nearest exit" << endl;
+ double mindist = (1.1 * rolloutDistance) + touchdownDistance;
+
FGGroundNetwork *gn = apt->getDynamics()->getGroundNetwork();
- if (gn) {
- double min = 0;
- for (int i = ceil(mindist); i < floor(maxdist); i++) {
- coord = rwy->pointOnCenterline(mindist);
- int nodeId = 0;
- if (gn->getVersion() > 0) {
- nodeId = gn->findNearestNodeOnRunway(coord);
- } else {
- nodeId = gn->findNearestNode(coord);
- }
- if (tn)
- tn = gn->findNode(nodeId);
- if (!tn)
- break;
-
- double dist = SGGeodesy::distanceM(coord, tn->geod());
- if (dist < (min + 0.75)) {
- break;
- }
- min = dist;
- }
- if (tn) {
- wpt = createOnGround(ac, buffer, tn->geod(), currElev, vTaxi);
- pushBackWaypoint(wpt);
- }
+ if (!gn) {
+ return true;
}
- //cerr << "Done. " << endl;
-
- /*
- //Runway Threshold
- wpt = createOnGround(ac, "Threshold", rwy->threshold(), aptElev, vTouchdown);
- wpt->crossat = apt->getElevation();
- pushBackWaypoint(wpt);
-
- // Roll-out
- wpt = createOnGround(ac, "Center", rwy->geod(), aptElev, vTaxi*2);
- pushBackWaypoint(wpt);
-
- SGGeod rollOut = rwy->pointOnCenterline(rwy->lengthM() * 0.9);
- wpt = createOnGround(ac, "Roll Out", rollOut, aptElev, vTaxi);
- wpt->crossat = apt->getElevation();
- pushBackWaypoint(wpt);
- */
+
+ coord = rwy->pointOnCenterline(mindist);
+ int nodeId = 0;
+ if (gn->getVersion() > 0) {
+ nodeId = gn->findNearestNodeOnRunway(coord, rwy);
+ } else {
+ nodeId = gn->findNearestNode(coord);
+ }
+
+ FGTaxiNode* tn = gn->findNode(nodeId);
+ if (tn) {
+ wpt = createOnGround(ac, buffer, tn->geod(), currElev, vTaxi);
+ pushBackWaypoint(wpt);
+ }
+
return true;
}
#include <Airports/simple.hxx>
#include <Airports/dynamics.hxx>
+#include <Airports/runways.hxx>
#include <AIModel/AIAircraft.hxx>
#include <AIModel/performancedata.hxx>
return index;
}
-int FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod)
+int FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod, FGRunway* aRunway)
{
double minDist = HUGE_VAL;
int index = -1;
if (!i->second->getIsOnRunway()) {
continue;
}
+ // check point lies on the runway - i.e that course from aGeod to the
+ // runway end, matches the runway heading
+ if (aRunway) {
+ double course = SGGeodesy::courseDeg(i->second->geod(), aRunway->end());
+ double headingDiff = course - aRunway->headingDeg();
+ SG_NORMALIZE_RANGE(headingDiff, -180.0, 180.0);
+ if (fabs(headingDiff) > 3.0) { // 3 degrees tolerance
+ continue;
+ }
+ }
double d = SGGeodesy::distanceM(aGeod, i->second->geod());
if (d < minDist) {
#include <list>
#include <map>
-class Block;
-
#include "gnnode.hxx"
#include "parking.hxx"
#include <ATC/trafficcontrol.hxx>
+class Block;
+class FGRunway;
class FGTaxiSegment; // forward reference
class FGAIFlightPlan; // forward reference
class FGAirport; // forward reference
};
int findNearestNode(const SGGeod& aGeod);
- int findNearestNodeOnRunway(const SGGeod& aGeod);
+ int findNearestNodeOnRunway(const SGGeod& aGeod, FGRunway* aRunway = NULL);
FGTaxiNode *findNode(unsigned idx);
FGTaxiSegment *findSegment(unsigned idx);
return (FGNavRecord*) flightgear::NavDataCache::instance()->loadById(_ils);
}
+FGNavRecord* FGRunway::glideslope() const
+{
+ flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
+ PositionedID gsId = cache->findNavaidForRunway(guid(), FGPositioned::GS);
+ if (gsId == 0) {
+ return NULL;
+ }
+
+ return (FGNavRecord*) cache->loadById(gsId);
+}
+
std::vector<flightgear::SID*> FGRunway::getSIDs() const
{
FGAirport* apt = airport();
FGNavRecord* ILS() const;
+ /**
+ * retrieve the associated glideslope transmitter, if one is defined.
+ */
+ FGNavRecord* glideslope() const;
+
void setILS(PositionedID nav) { _ils = nav; }
FGRunway* reciprocalRunway() const;
findNavsByFreqNoPos = prepare("SELECT positioned.rowid FROM positioned, navaid WHERE "
"positioned.rowid=navaid.rowid AND freq=?1 " AND_TYPED);
+ findNavaidForRunway = prepare("SELECT positioned.rowid FROM positioned, navaid WHERE "
+ "positioned.rowid=navaid.rowid AND runway=?1 AND type=?2");
+
// for an octree branch, return the child octree nodes which exist,
// described as a bit-mask
getOctreeChildren = prepare("SELECT children FROM octree WHERE rowid=?1");
sqlite3_stmt_ptr searchAirports;
sqlite3_stmt_ptr findCommByFreq, findNavsByFreq,
- findNavsByFreqNoPos;
+ findNavsByFreqNoPos, findNavaidForRunway;
sqlite3_stmt_ptr getAirportItems, getAirportItemByIdent;
sqlite3_stmt_ptr findAirportRunway,
findILS;
}
return result;
}
+
+PositionedID NavDataCache::findNavaidForRunway(PositionedID runway, FGPositioned::Type ty)
+{
+ d->reset(d->findNavaidForRunway);
+ sqlite3_bind_int64(d->findNavaidForRunway, 1, runway);
+ sqlite3_bind_int(d->findNavaidForRunway, 2, ty);
+ if (!d->execSelect(d->findNavaidForRunway)) {
+ return 0;
+ }
+
+ return sqlite3_column_int64(d->findNavaidForRunway, 0);
+}
} // of namespace flightgear
/// returning results. Only used by TACAN carrier search
PositionedIDVec findNavaidsByFreq(int freqKhz, FGPositioned::Filter* filt);
+ /**
+ * Given a runway and type, find the corresponding navaid (ILS / GS / OM)
+ */
+ PositionedID findNavaidForRunway(PositionedID runway, FGPositioned::Type ty);
+
/**
* given a navaid name (or similar) from apt.dat / nav.dat, find the
* corresponding airport and runway IDs.
cachefile << "[TrafficManagerCachedata:ref:2011:09:04]" << endl;
}
}
- for (ScheduleVectorIterator sched = scheduledAircraft.begin();
- sched != scheduledAircraft.end(); sched++) {
+
+ BOOST_FOREACH(FGAISchedule* acft, scheduledAircraft) {
if (saveData) {
- cachefile << (*sched)->getRegistration() << " "
- << (*sched)->getRunCount() << " "
- << (*sched)->getHits() << " "
- << (*sched)->getLastUsed() << endl;
+ cachefile << acft->getRegistration() << " "
+ << acft->getRunCount() << " "
+ << acft->getHits() << " "
+ << acft->getLastUsed() << endl;
}
- delete(*sched);
+ delete acft;
}
if (saveData) {
cachefile.close();
SG_LOG(SG_AI, SG_INFO, "finishing AI-Traffic init");
loadHeuristics();
- // Do sorting and scoring separately, to take advantage of the "homeport| variable
- for (currAircraft = scheduledAircraft.begin();
- currAircraft != scheduledAircraft.end(); currAircraft++) {
- (*currAircraft)->setScore();
+ // Do sorting and scoring separately, to take advantage of the "homeport" variable
+ BOOST_FOREACH(FGAISchedule* schedule, scheduledAircraft) {
+ schedule->setScore();
}
sort(scheduledAircraft.begin(), scheduledAircraft.end(),
}
time_t now = time(NULL) + fgGetLong("/sim/time/warp");
- if (scheduledAircraft.size() == 0) {
+ if (scheduledAircraft.empty()) {
return;
}