# include <config.h>
#endif
+#include <cstdlib>
#include "AIFlightPlan.hxx"
#include <simgear/math/sg_geodesy.hxx>
int currWpt = wpt_iterator - waypoints.begin();
switch (legNr) {
case 1:
- retVal = createPushBack(ac, firstFlight, dep, latitude, longitude,
+ retVal = createPushBack(ac, firstFlight, dep,
radius, fltType, aircraftType, airline);
- // Pregenerate the
- if (retVal) {
- waypoints.back()->setName( waypoints.back()->getName() + string("legend"));
- retVal = createTakeoffTaxi(ac, false, dep, radius, fltType, aircraftType, airline);
- }
+ // Pregenerate the taxi leg.
+ //if (retVal) {
+ // waypoints.back()->setName( waypoints.back()->getName() + string("legend"));
+ // retVal = createTakeoffTaxi(ac, false, dep, radius, fltType, aircraftType, airline);
+ //}
break;
case 2:
retVal = createTakeoffTaxi(ac, firstFlight, dep, radius, fltType,
break;
default:
//exit(1);
- SG_LOG(SG_INPUT, SG_ALERT,
+ SG_LOG(SG_AI, SG_ALERT,
"AIFlightPlan::create() attempting to create unknown leg"
" this is probably an internal program error");
}
wpt->setGear_down (false );
wpt->setFlaps_down (false );
wpt->setOn_ground (false );
+ wpt->setCrossat (aElev );
return wpt;
}
const string & acType,
const string & airline)
{
- double heading, lat, lon;
// If this function is called during initialization,
// make sure we obtain a valid gate ID first
// and place the model at the location of the gate.
- if (firstFlight) {
- if (!(apt->getDynamics()->getAvailableParking(&lat, &lon,
- &heading, &gateId,
- radius, fltType,
- acType, airline))) {
- SG_LOG(SG_INPUT, SG_WARN, "Could not find parking for a " <<
- acType <<
- " of flight type " << fltType <<
- " of airline " << airline <<
- " at airport " << apt->getId());
- }
+ if (firstFlight)
+ {
+ gateId = apt->getDynamics()->getAvailableParking(radius, fltType,
+ acType, airline);
+ if (gateId < 0) {
+ SG_LOG(SG_AI, SG_WARN, "Could not find parking for a " <<
+ acType <<
+ " of flight type " << fltType <<
+ " of airline " << airline <<
+ " at airport " << apt->getId());
+ }
}
string rwyClass = getRunwayClassFromTrafficType(fltType);
FGTaxiNode *tn =
apt->getDynamics()->getGroundNetwork()->findNode(node);
FGAIWaypoint *wpt =
- createOnGround(ac, buffer, tn->getGeod(), apt->getElevation(),
+ createOnGround(ac, buffer, tn->geod(), apt->getElevation(),
ac->getPerformance()->vTaxi());
wpt->setRouteIndex(route);
//cerr << "Nodes left " << taxiRoute->nodesLeft() << " ";
ac->getPerformance()->vTaxi());
pushBackWaypoint(wpt);
- double heading, lat, lon;
- aAirport->getDynamics()->getParking(gateId, &lat, &lon, &heading);
- wpt =
- createOnGround(ac, "END", SGGeod::fromDeg(lon, lat), airportElev,
- ac->getPerformance()->vTaxi());
- pushBackWaypoint(wpt);
+ FGParking* parkPos = aAirport->getDynamics()->getParking(gateId);
+ if (parkPos) {
+ wpt = createOnGround(ac, "ENDtaxi", parkPos->geod(), airportElev,
+ ac->getPerformance()->vTaxi());
+ pushBackWaypoint(wpt);
+ }
}
bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt,
const string & acType,
const string & airline)
{
- double heading, lat, lon;
- apt->getDynamics()->getAvailableParking(&lat, &lon, &heading,
- &gateId, radius, fltType,
+ gateId = apt->getDynamics()->getAvailableParking(radius, fltType,
acType, airline);
SGGeod lastWptPos =
snprintf(buffer, 10, "%d", node);
FGTaxiNode *tn = gn->findNode(node);
FGAIWaypoint *wpt =
- createOnGround(ac, buffer, tn->getGeod(), apt->getElevation(),
+ createOnGround(ac, buffer, tn->geod(), apt->getElevation(),
ac->getPerformance()->vTaxi());
wpt->setRouteIndex(route);
pushBackWaypoint(wpt);
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:
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
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);
-
- wpt = cloneWithPos(ac, wpt, "3000 ft", rwy->end());
- wpt->setAltitude(airportElev + 3000);
+
+ 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);
- // 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;
}
//cerr << "Distance : " << distance << endl;
//cerr << "Azimuth : " << azimuth << endl;
//cerr << "Initial Lateral point: " << lateralOffset << endl;
- double lat = refPoint.getLatitudeDeg();
- double lon = refPoint.getLongitudeDeg();
+// double lat = refPoint.getLatitudeDeg();
+// double lon = refPoint.getLongitudeDeg();
//cerr << "Reference point (" << lat << ", " << lon << ")." << endl;
- lat = initialTarget.getLatitudeDeg();
- lon = initialTarget.getLongitudeDeg();
+// lat = initialTarget.getLatitudeDeg();
+// lon = initialTarget.getLongitudeDeg();
//cerr << "Initial Target point (" << lat << ", " << lon << ")." << endl;
double ratio = initialTurnRadius / distance;
azimuth = SGGeodesy::courseDeg(origin, secondaryTarget);
- lat = secondaryTarget.getLatitudeDeg();
- lon = secondaryTarget.getLongitudeDeg();
+// lat = secondaryTarget.getLatitudeDeg();
+// lon = secondaryTarget.getLongitudeDeg();
//cerr << "Secondary Target point (" << lat << ", " << lon << ")." << endl;
//cerr << "Distance : " << distance << endl;
//cerr << "Azimuth : " << azimuth << endl;
SGGeodesy::direct(origin, azimuth,
newDistance, tertiaryTarget, dummyAz2);
- lat = tertiaryTarget.getLatitudeDeg();
- lon = tertiaryTarget.getLongitudeDeg();
+// lat = tertiaryTarget.getLatitudeDeg();
+// lon = tertiaryTarget.getLongitudeDeg();
//cerr << "tertiary Target point (" << lat << ", " << lon << ")." << endl;
FGAIWaypoint *wpt;
- double aptElev = apt->getElevation();
+ //double aptElev = apt->getElevation();
double currElev = 0;
char buffer[12];
FGRunway * rwy = apt->getRunwayByIdent(activeRunway);
}
if (tn)
tn = gn->findNode(nodeId);
- else {
+ if (!tn)
break;
- }
- double dist = SGGeodesy::distanceM(coord, tn->getGeod());
+ double dist = SGGeodesy::distanceM(coord, tn->geod());
if (dist < (min + 0.75)) {
break;
}
min = dist;
}
if (tn) {
- wpt = createOnGround(ac, buffer, tn->getGeod(), currElev, vTaxi);
+ wpt = createOnGround(ac, buffer, tn->geod(), currElev, vTaxi);
pushBackWaypoint(wpt);
}
}
{
FGAIWaypoint *wpt;
double aptElev = apt->getElevation();
- double lat = 0.0, lat2 = 0.0;
- double lon = 0.0, lon2 = 0.0;
- double az2 = 0.0;
- double heading = 0.0;
-
double vTaxi = ac->getPerformance()->vTaxi();
double vTaxiReduced = vTaxi * (2.0 / 3.0);
- apt->getDynamics()->getParking(gateId, &lat, &lon, &heading);
- heading += 180.0;
- if (heading > 360)
- heading -= 360;
- geo_direct_wgs_84(0, lat, lon, heading,
- 2.2 * radius, &lat2, &lon2, &az2);
- wpt =
- createOnGround(ac, "taxiStart", SGGeod::fromDeg(lon2, lat2),
- aptElev, vTaxiReduced);
- pushBackWaypoint(wpt);
+ FGParking* parking = apt->getDynamics()->getParking(gateId);
+ if (!parking) {
+ wpt = createOnGround(ac, "END-Parking", apt->geod(), aptElev,
+ vTaxiReduced);
+ pushBackWaypoint(wpt);
- geo_direct_wgs_84(0, lat, lon, heading,
- 0.1 * radius, &lat2, &lon2, &az2);
+ return true;
+ }
+
+ double heading = SGMiscd::normalizePeriodic(0, 360, parking->getHeading() + 180.0);
+ double az; // unused
+ SGGeod pos;
+
+ SGGeodesy::direct(parking->geod(), heading, 2.2 * parking->getRadius(),
+ pos, az);
+
+ wpt = createOnGround(ac, "taxiStart", pos, aptElev, vTaxiReduced);
+ pushBackWaypoint(wpt);
- wpt =
- createOnGround(ac, "taxiStart2", SGGeod::fromDeg(lon2, lat2),
- aptElev, vTaxiReduced);
+ SGGeodesy::direct(parking->geod(), heading, 0.1 * parking->getRadius(),
+ pos, az);
+ wpt = createOnGround(ac, "taxiStart2", pos, aptElev, vTaxiReduced);
pushBackWaypoint(wpt);
- wpt =
- createOnGround(ac, "END", SGGeod::fromDeg(lon, lat), aptElev,
+ wpt = createOnGround(ac, "END-Parking", parking->geod(), aptElev,
vTaxiReduced);
pushBackWaypoint(wpt);
return true;