16#include <simgear/math/sg_geodesy.hxx>
17#include <simgear/props/props.hxx>
18#include <simgear/props/props_io.hxx>
19#include <simgear/timing/sg_time.hxx>
54 double radius,
const string& fltType,
55 const string& aircraftType,
56 const string& airline,
double distance)
59 SG_LOG(SG_AI, SG_DEBUG,
"Create Leg " << legNr <<
" " << (firstFlight ?
"First" :
"") <<
" Old Leg " <<
getLeg() <<
" At Airport : " << dep->
getId());
61 SG_LOG(SG_AI, SG_DEBUG,
"Create Leg " << legNr <<
" " << (firstFlight ?
"First" :
"") <<
" Old Leg " <<
getLeg() <<
" Departure Airport : " << dep->
getId() <<
" Arrival Airport : " << arr->
getId());
63 SG_LOG(SG_AI, SG_DEBUG,
"Create Leg " << legNr <<
" " << (firstFlight ?
"First" :
"") <<
" Old Leg " <<
getLeg() <<
" At Airport : " << arr->
getId());
66 int currWpt = wpt_iterator - waypoints.begin();
70 radius, fltType, aircraftType, airline);
73 retVal = createTakeoffTaxi(ac, firstFlight, dep, radius, fltType,
74 aircraftType, airline);
80 retVal = createClimb(ac, firstFlight, dep, arr, speed, alt, fltType);
83 retVal = createCruise(ac, firstFlight, dep, arr, SGGeod::fromDeg(
longitude,
latitude), speed,
87 retVal = createDescent(ac, arr, SGGeod::fromDeg(
longitude,
latitude), speed, alt, fltType,
91 retVal = createHold(ac, arr, SGGeod::fromDeg(
longitude,
latitude), speed, alt, fltType,
95 retVal = createLanding(ac, arr, fltType);
98 retVal = createLandingTaxi(ac, arr, radius, fltType, aircraftType, airline);
101 retVal = createParking(ac, arr, radius);
105 SG_LOG(SG_AI, SG_ALERT,
106 "AIFlightPlan::create() attempting to create unknown leg"
107 " this is probably an internal program error");
113 if (waypoints.empty()) {
114 SG_LOG(SG_AI, SG_WARN, ac->
getCallSign() <<
"|AIFlightPlan::create() Leg " << legNr <<
" created empty waypoints.");
117 wpt_iterator = waypoints.begin() + currWpt;
118 if (waypoints.back()->getName().size() == 0) {
119 SG_LOG(SG_AI, SG_WARN, ac->
getCallSign() <<
" Empty wpt name");
121 waypoints.back()->setName(waypoints.back()->getName() +
string(
"legend"));
128 const std::string& aName,
129 const SGGeod& aPos,
double aElev,
157 const std::string& aName,
158 const SGGeod& aPos,
double aElev,
161 FGAIWaypoint* wpt = createOnGround(ac, aName, aPos, aElev, aSpeed);
166void FGAIFlightPlan::createArc(
FGAIAircraft* ac,
const SGGeod& center,
int startAngle,
167 int endAngle,
int increment,
int radius,
double aElev,
double altDiff,
double aSpeed,
const char* pattern)
169 double trackSegmentLength = (2 *
M_PI * radius) / 360.0;
173 if (endAngle > startAngle && increment < 0) {
176 if (endAngle < startAngle && increment > 0) {
180 int nPoints = fabs(fabs(endAngle - startAngle) / increment);
181 double currentAltitude = aElev;
182 double altDecrement = altDiff / nPoints;
184 for (
int i = startAngle; !(endAngle <=
i &&
i < endAngle + fabs(increment));
i += increment) {
186 SG_LOG(SG_AI, SG_WARN,
"FGAIFlightPlan::createArc runaway " << startAngle <<
" " << endAngle <<
" " << increment);
190 SGGeodesy::direct(center,
i,
191 radius, result, dummyAz2);
192 snprintf(buffer,
sizeof(buffer), pattern,
i);
193 currentAltitude -= altDecrement;
194 FGAIWaypoint* wpt = createInAir(ac, buffer, result, currentAltitude, aSpeed);
197 pushBackWaypoint(wpt);
201void FGAIFlightPlan::createLine(
FGAIAircraft* ac,
const SGGeod& startPoint,
double azimuth,
double dist,
double aElev,
double dAlt,
double vDescent,
const char* pattern)
203 double nPoints = dist / (vDescent * 4);
205 double distIncrement = (dist / nPoints);
207 for (
int i = 1;
i < nPoints;
i++) {
208 double currentDist =
i * distIncrement;
209 double currentAltitude = aElev - (
i * (dAlt / nPoints));
210 SGGeod result = SGGeodesy::direct(startPoint, azimuth, currentDist);
211 snprintf(buffer,
sizeof(buffer), pattern,
i);
212 FGAIWaypoint* wpt = createInAir(ac, buffer, result, currentAltitude, vDescent);
215 pushBackWaypoint(wpt);
220 const std::string& aName,
221 const SGGeod& aPos,
double aElev,
224 FGAIWaypoint* wpt = createOnGround(ac, aName, aPos, aElev, aSpeed);
232 if (aPos.getElevationFt() < 10000.0f) {
242 FGAIWaypoint* wpt =
new FGAIWaypoint;
264 const std::string& aName,
267 FGAIWaypoint* wpt = clone(aWpt);
276void FGAIFlightPlan::createDefaultTakeoffTaxi(
FGAIAircraft* ac,
285 createOnGround(ac,
"Airport Center", aAirport->
geod(), airportElev,
287 pushBackWaypoint(wpt);
289 createOnRunway(ac,
"Runway Takeoff", runwayTakeoff, airportElev,
292 pushBackWaypoint(wpt);
297 wpt = createOnRunway(ac,
"Accel", accelPoint, airportElev,
299 pushBackWaypoint(wpt);
306bool FGAIFlightPlan::createTakeoffTaxi(
FGAIAircraft* ac,
bool firstFlight,
309 const string& fltType,
310 const string& acType,
311 const string& airline)
317 if (firstFlight && apt->
getDynamics()->hasParkings()) {
318 gate = apt->
getDynamics()->getAvailableParking(radius, fltType,
320 if (!gate.isValid()) {
321 SG_LOG(SG_AI, SG_WARN,
"Could not find parking for a " << acType <<
" of flight type " << fltType <<
" of airline " << airline <<
" at airport " << apt->
getId());
328 if (activeRunway.empty()) {
335 SG_LOG(SG_AI, SG_DEBUG,
"Taxi to " << apt->
getId() <<
"/" << activeRunway);
341 SG_LOG(SG_AI, SG_DEBUG,
"No groundnet " << apt->
getId() <<
" creating default taxi.");
342 createDefaultTakeoffTaxi(ac, apt, rwy);
363 FGParking* park = gate.parking();
370 }
else if (lastNodeVisited) {
371 node = lastNodeVisited;
373 SG_LOG(SG_AI, SG_WARN,
"Taxiroute could not be constructed no lastNodeVisited at " << (apt ? apt->
getId() :
"????") << gate.isValid());
377 SG_LOG(SG_AI, SG_WARN,
"Taxiroute could not be constructed no parking." << (apt ? apt->
getId() :
"????"));
380 FGTaxiRoute taxiRoute;
381 if (runwayNode && node) {
387 if (taxiRoute.
size() <= 1) {
388 SG_LOG(SG_AI, SG_DEBUG,
"Taxiroute too short " << apt->
getId() <<
"creating default taxi.");
389 createDefaultTakeoffTaxi(ac, apt, rwy);
401 int nrWaypointsToSkip = rand() % taxiRoute.
size();
404 for (
int i = 0;
i < nrWaypointsToSkip - 3;
i++) {
405 taxiRoute.
next(skipNode, &route);
410 if (taxiRoute.
size() > 1) {
411 taxiRoute.
next(skipNode, &route);
422 while (taxiRoute.
next(node, &route)) {
424 snprintf(buffer,
sizeof(buffer),
"%d", node->getIndex());
426 createOnGround(ac, buffer, node->geod(), apt->
getElevation(),
442 pushBackWaypoint(wpt);
444 double accell_point = 105.0;
446 SGGeod entryPoint = waypoints.back()->getPos();
448 double distM = SGGeodesy::distanceM(entryPoint, runwayEnd);
449 if (distM > accell_point) {
450 SG_LOG(SG_AI, SG_BULK,
"Distance down runway " << distM <<
" " << accell_point);
451 accell_point += distM;
456 pushBackWaypoint(wpt);
460 arrivalTime = now + calcArrivalTimes();
465void FGAIFlightPlan::createDefaultLandingTaxi(
FGAIAircraft* ac,
469 SGGeod::fromDeg(waypoints.back()->getLongitude(),
470 waypoints.back()->getLatitude());
475 createOnGround(ac,
"Runway Exit", lastWptPos, airportElev,
477 pushBackWaypoint(wpt);
479 createOnGround(ac,
"Airport Center", aAirport->
geod(), airportElev,
481 pushBackWaypoint(wpt);
483 if (gate.isValid()) {
484 wpt = createOnGround(ac,
"END-taxi", gate.parking()->geod(), airportElev,
486 pushBackWaypoint(wpt);
490 arrivalTime = now + calcArrivalTimes();
496 const string& fltType,
497 const string& acType,
498 const string& airline)
501 gate = apt->
getDynamics()->getAvailableParking(radius, fltType,
503 SGGeod lastWptPos = waypoints.back()->getPos();
508 createDefaultLandingTaxi(ac, apt);
523 FGTaxiRoute taxiRoute;
524 if (runwayNode && gate.isValid()) {
528 if (taxiRoute.
empty()) {
529 createDefaultLandingTaxi(ac, apt);
535 int size = taxiRoute.
size();
536 for (
int i = 0;
i < size;
i++) {
537 taxiRoute.
next(node, &route);
539 snprintf(buffer,
sizeof(buffer),
"landingtaxi-%d-%d", node->getIndex(),
i);
541 createOnGround(ac, buffer, node->geod(), apt->
getElevation(),
546 if ((!waypoints.back() ||
547 SGGeodesy::distanceM(waypoints.back()->getPos(), wpt->
getPos()) > 1) &&
548 SGGeodesy::distanceM(gate.parking()->geod(), wpt->
getPos()) > 20) {
549 if (waypoints.back()) {
550 int dist = SGGeodesy::distanceM(waypoints.back()->getPos(), wpt->
getPos());
552 if (dist < 10 || (size -
i) < 2) {
556 pushBackWaypoint(wpt);
559 SG_LOG(SG_AI, SG_BULK,
"Created taxi from " << runwayNode->getIndex() <<
" to " << gate.parking()->ident() <<
" at " << apt->
getId());
563 arrivalTime = now + calcArrivalTimes();
569 double t = fabs(v1 - v0) / accel;
571 SG_LOG(SG_AI, SG_BULK,
"Brakingtime " << t);
572 return t * 0.5 * (v1 + v0);
581 return altGainM / tan(pitchAngleDeg * SG_DEGREES_TO_RADIANS);
600 const string& fltType)
602 SG_LOG(SG_AI, SG_BULK,
"createTakeOff " << apt->
getId() <<
"/" << activeRunway);
603 double accell_point = 105.0;
606 const double INITIAL_PITCH_ANGLE = 10.0;
613 double accelMetric = accel * SG_KT_TO_MPS;
614 double vTaxiMetric = vTaxi * SG_KT_TO_MPS;
615 double vRotateMetric = vRotate * SG_KT_TO_MPS;
616 double vTakeoffMetric = vTakeoff * SG_KT_TO_MPS;
633 SG_LOG(SG_AI, SG_WARN,
"FGAIFlightPlan::createTakeOff: invalid active runway:" << activeRunway);
636 SG_LOG(SG_AI, SG_BULK,
"Takeoff from airport " << apt->
getId() <<
"/" << activeRunway);
646 double distM = SGGeodesy::distanceM(pos, runwayEnd);
647 if (distM > accell_point) {
648 SG_LOG(SG_AI, SG_BULK,
"Distance down runway " << distM <<
" " << accell_point);
649 accell_point += distM;
654 double d =
accelDistance(vTaxiMetric, vRotateMetric, accelMetric) + accell_point;
656 wpt = createOnRunway(ac,
"rotate", rotatePoint, airportElev, vRotate);
658 pushBackWaypoint(wpt);
661 double t = d +
accelDistance(vRotateMetric, vTakeoffMetric, accelMetric);
663 wpt = createOnRunway(ac,
"takeoff", takeoffPoint, airportElev, vTakeoff);
666 pushBackWaypoint(wpt);
668 double vRef = vTakeoff + 20;
674 double gearUpDist = t + 2 * vRef * SG_FEET_TO_METER +
pitchDistance(INITIAL_PITCH_ANGLE, 400 * SG_FEET_TO_METER);
676 wpt = createInAir(ac,
"gear-up", gearUpPoint, airportElev + 400, vRef);
678 pushBackWaypoint(wpt);
686 double climbOut = t + 2 * vClimbBelow10000 * SG_FEET_TO_METER +
pitchDistance(INITIAL_PITCH_ANGLE, 2000 * SG_FEET_TO_METER);
688 wpt = createInAir(ac,
"2000'", climbOutPoint, airportElev + 2000, vClimbBelow10000);
689 pushBackWaypoint(wpt);
691 climbOut = t + 2 * vClimbBelow10000 * SG_FEET_TO_METER +
pitchDistance(INITIAL_PITCH_ANGLE, 2500 * SG_FEET_TO_METER);
693 wpt = createInAir(ac,
"2500'", climbOutPoint2, airportElev + 2500, vClimbBelow10000);
694 pushBackWaypoint(wpt);
696 time_t now =
globals->get_time_params()->get_cur_time();
698 arrivalTime = now + calcArrivalTimes();
706bool FGAIFlightPlan::createClimb(
FGAIAircraft* ac,
bool firstFlight,
708 double speed,
double alt,
709 const string& fltType)
720 for (wpt_vector_iterator
i = sid->getFirstWayPoint();
i != sid->getLastWayPoint(); ++
i) {
721 pushBackWaypoint(clone(*(
i)));
728 SGGeod cur = runway->end();
729 if (!waypoints.empty()) {
730 cur = waypoints.back()->getPos();
734 double course = SGGeodesy::courseDeg(cur, arrival->geod());
737 const double headingDiffRunway = SGMiscd::normalizePeriodic(-180, 180, course - runway->headingDeg());
739 if (fabs(headingDiffRunway) < 10) {
740 SGGeod climb1 = SGGeodesy::direct(cur, course, 10 * SG_NM_TO_METER);
741 FGAIWaypoint* wpt = createInAir(ac,
"10000ft climb", climb1, 10000, vClimb);
742 pushBackWaypoint(wpt);
744 SGGeod climb2 = SGGeodesy::direct(cur, course, 20 * SG_NM_TO_METER);
745 wpt = createInAir(ac,
"18000ft climb", climb2, 18000, vClimb);
746 pushBackWaypoint(wpt);
748 double initialTurnRadius = getTurnRadius(vClimb,
true);
749 SGGeod climb1 = SGGeodesy::direct(cur, runway->headingDeg(), 5 * SG_NM_TO_METER);
750 FGAIWaypoint* wpt = createInAir(ac,
"5000ft climb", climb1, 5000, vClimb);
751 pushBackWaypoint(wpt);
752 int rightAngle = headingDiffRunway > 0 ? 90 : -90;
753 int firstTurnIncrement = headingDiffRunway > 0 ? 4 : -4;
755 SGGeod firstTurnCenter = SGGeodesy::direct(climb1, ac->
getTrueHeadingDeg() + rightAngle, initialTurnRadius);
756 createArc(ac, firstTurnCenter, ac->
_getHeading() - rightAngle, course - rightAngle, firstTurnIncrement, initialTurnRadius, 5000, 100, vClimb,
"climb-out-%03d");
757 SGGeod climb2 = SGGeodesy::direct(cur, course, 20 * SG_NM_TO_METER);
758 wpt = createInAir(ac,
"18000ft climb", climb2, 18000, vClimb);
759 pushBackWaypoint(wpt);
773 const SGGeod& current,
776 const string& fltType,
777 double requiredDistance)
792 SG_LOG(SG_AI, SG_WARN, ac->
getCallSign() <<
"| FGAIFlightPlan::createDescent: No such runway " << activeRunway <<
" at " << apt->
ident());
799 double courseTowardsThreshold = SGGeodesy::courseDeg(current, rwy->pointOnCenterlineDisplaced(0));
803 SGGeod threshold = rwy->threshold();
804 double currElev = threshold.getElevationFt();
805 double altDiff = alt - currElev - 2000;
809 double initialTurnRadius = getTurnRadius(vDescent,
true);
813 double distanceOut = apt->
getDynamics()->getRunwayQueue(rwy->name())->getApproachDistance();
816 const double headingDiffRunway = SGMiscd::normalizePeriodic(-180, 180, ac->
getTrueHeadingDeg() - rwy->headingDeg());
817 double lateralOffset = initialTurnRadius;
818 if (headingDiffRunway > 0.0) {
819 lateralOffset *= -1.0;
822 SGGeod initialTarget = rwy->pointOnCenterline(-distanceOut);
824 SGGeod secondaryTarget =
825 rwy->pointOffCenterline(-2 * distanceOut, lateralOffset);
826 SGGeod secondHoldCenter =
827 rwy->pointOffCenterline(-3 * distanceOut, lateralOffset);
829 double distance = SGGeodesy::distanceM(current, initialTarget);
830 double azimuth = SGGeodesy::courseDeg(current, initialTarget);
831 double secondaryAzimuth = SGGeodesy::courseDeg(current, secondaryTarget);
832 double initialHeadingDiff = SGMiscd::normalizePeriodic(-180, 180, azimuth - secondaryAzimuth);
833 double courseTowardsThreshold = SGGeodesy::courseDeg(current, rwy->pointOnCenterlineDisplaced(0));
835 double headingDiffToRunwayThreshold = SGMiscd::normalizePeriodic(-180, 180, ac->
getTrueHeadingDeg() - courseTowardsThreshold);
839 <<
" WPs : " << waypoints.size() <<
" Heading Diff (rwy) : " << headingDiffRunway <<
" Distance : " << distance <<
" Azimuth : " << azimuth <<
" Heading : " << ac->
getTrueHeadingDeg() <<
" Initial Headingdiff " << initialHeadingDiff <<
" Lateral : " << lateralOffset);
844 if (fabs(headingDiffRunway) >= 30 && fabs(headingDiffRunway) <= 150) {
845 if (distance < (2 * initialTurnRadius)) {
847 SG_LOG(SG_AI, SG_BULK, ac->
getCallSign() <<
"| Enter near S curve");
849 rwy->pointOffCenterline(-2 * distanceOut, -lateralOffset);
851 rwy->pointOffCenterline(-3 * distanceOut, -lateralOffset);
854 int rightAngle = headingDiffRunway > 0 ? 90 : -90;
855 int firstTurnIncrement = headingDiffRunway > 0 ? 2 : -2;
857 SGGeod firstTurnCenter = SGGeodesy::direct(current, ac->
getTrueHeadingDeg() + rightAngle, initialTurnRadius);
858 SGGeod newCurrent = current;
860 if (
abs(headingDiffToRunwayThreshold) < 90) {
861 newCurrent = SGGeodesy::direct(current, ac->
getTrueHeadingDeg(), distance + 1000);
862 firstTurnCenter = SGGeodesy::direct(newCurrent, ac->
getTrueHeadingDeg() + rightAngle, initialTurnRadius);
863 createLine(ac, current, ac->
getTrueHeadingDeg(), distance + 1000, waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, 0, vDescent,
"move%03d");
866 while (SGGeodesy::distanceM(firstTurnCenter, secondaryTarget) < 2 * initialTurnRadius) {
867 newCurrent = SGGeodesy::direct(newCurrent, ac->
getTrueHeadingDeg(), offset += 100);
868 firstTurnCenter = SGGeodesy::direct(newCurrent, ac->
getTrueHeadingDeg() + rightAngle, initialTurnRadius);
871 createArc(ac, firstTurnCenter, ac->
_getHeading() - rightAngle, dHeading - rightAngle, firstTurnIncrement, initialTurnRadius, waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff / 4, vDescent,
"near-initialturn%03d");
873 createLine(ac, waypoints.back()->getPos(), dHeading, length, waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff / 2, vDescent,
"descent%03d");
874 int startVal = SGMiscd::normalizePeriodic(0, 360, dHeading - rightAngle);
875 int endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg() - rightAngle);
877 createArc(ac, secondaryTarget, startVal, endVal, firstTurnIncrement, initialTurnRadius,
878 waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff / 4, vDescent,
"turn%03d");
880 SG_LOG(SG_AI, SG_BULK, ac->
getCallSign() <<
"| Enter far S curve");
882 int rightAngle = headingDiffRunway > 0 ? 90 : -90;
883 int firstTurnIncrement = headingDiffRunway > 0 ? 2 : -2;
884 SGGeod firstTurnCenter = SGGeodesy::direct(current, ac->
getTrueHeadingDeg() + rightAngle, initialTurnRadius);
885 int innerTangent = headingDiffRunway < 0 ? 0 : 1;
887 while (SGGeodesy::distanceM(firstTurnCenter, secondaryTarget) < 2 * initialTurnRadius) {
888 secondaryTarget = rwy->pointOffCenterline(-2 * distanceOut + (offset += 1000), lateralOffset);
891 createArc(ac, firstTurnCenter, ac->
_getHeading() - rightAngle, dHeading - rightAngle, firstTurnIncrement, initialTurnRadius, waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff / 8, vDescent,
"far-initialturn%03d");
893 createLine(ac, waypoints.back()->getPos(), dHeading, length, waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff * 0.75, vDescent,
"descent%03d");
894 int startVal = SGMiscd::normalizePeriodic(0, 360, dHeading + rightAngle);
895 int endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg() + rightAngle);
897 createArc(ac, secondaryTarget, startVal, endVal, firstTurnIncrement * -1, initialTurnRadius,
898 waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff / 8, vDescent,
"s-turn%03d");
900 }
else if (fabs(headingDiffRunway) >= 150) {
902 if (distance < (2 * initialTurnRadius)) {
904 SG_LOG(SG_AI, SG_BULK, ac->
getCallSign() <<
"| Enter near downrunway");
906 rwy->pointOffCenterline(-2 * distanceOut, -lateralOffset);
908 rwy->pointOffCenterline(-3 * distanceOut, -lateralOffset);
911 int rightAngle = azimuth > 0 ? 90 : -90;
912 int firstTurnIncrement = azimuth > 0 ? 2 : -2;
914 SGGeod firstTurnCenter = SGGeodesy::direct(current, ac->
getTrueHeadingDeg() + rightAngle, initialTurnRadius);
916 int endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg() - 180);
917 SGGeod secondTurnCenter = SGGeodesy::direct(firstTurnCenter, endVal, 2 * initialTurnRadius);
918 createArc(ac, firstTurnCenter, ac->
_getHeading() - rightAngle, endVal, firstTurnIncrement, initialTurnRadius, waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff / 4, vDescent,
"d-near-initialturn%03d");
919 endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg());
922 createArc(ac, secondTurnCenter, endVal, endVal2 + rightAngle, -firstTurnIncrement, initialTurnRadius, waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff / 4, vDescent,
"secondturn%03d");
925 createLine(ac, waypoints.back()->getPos(), endVal2, length, waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff / 4, vDescent,
"descent%03d");
926 endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg() + rightAngle);
928 createArc(ac, secondaryTarget, endVal2 + rightAngle, endVal, -firstTurnIncrement, initialTurnRadius,
929 waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff / 4, vDescent,
"turn%03d");
931 SG_LOG(SG_AI, SG_BULK, ac->
getCallSign() <<
"| Enter far S downrunway");
933 int rightAngle = headingDiffRunway > 0 ? 90 : -90;
934 int firstTurnIncrement = headingDiffRunway > 0 ? 2 : -2;
935 int innerTangent = headingDiffRunway < 0 ? 0 : 1;
936 SGGeod firstTurnCenter = SGGeodesy::direct(current, ac->
getTrueHeadingDeg() + rightAngle, initialTurnRadius);
938 createArc(ac, firstTurnCenter, ac->
_getHeading() - rightAngle, dHeading - rightAngle, firstTurnIncrement, initialTurnRadius, waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff / 3, vDescent,
"d-far-initialturn%03d");
940 createLine(ac, waypoints.back()->getPos(), dHeading, length, waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff / 3, vDescent,
"descent%03d");
941 int startVal = SGMiscd::normalizePeriodic(0, 360, dHeading + rightAngle);
942 int endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg() + rightAngle);
944 createArc(ac, secondaryTarget, startVal, endVal, -firstTurnIncrement, initialTurnRadius,
945 waypoints.size() > 0 ? waypoints.back()->getAltitude() : alt, altDiff / 3, vDescent,
"d-s-turn%03d");
948 SG_LOG(SG_AI, SG_BULK, ac->
getCallSign() <<
"| Enter far straight");
950 int rightAngle = headingDiffRunway > 0 ? 90 : -90;
951 SGGeod firstTurnCenter = SGGeodesy::direct(current, ac->
getTrueHeadingDeg() - rightAngle, initialTurnRadius);
952 int firstTurnIncrement = headingDiffRunway > 0 ? -2 : 2;
953 const double dHeading = rwy->headingDeg();
954 createArc(ac, firstTurnCenter, ac->
_getHeading() + rightAngle, dHeading + rightAngle, firstTurnIncrement, initialTurnRadius, ac->
getAltitude(), altDiff / 3, vDescent,
"straight_turn_%03d");
959 arrivalTime = now + calcArrivalTimes();
974 const SGGeod& current,
977 const string& fltType,
978 double requiredDistance)
984 double initialTurnRadius = getTurnRadius(vDescent,
true);
989 SG_LOG(SG_AI, SG_WARN, ac->
getCallSign() <<
"| FGAIFlightPlan::createHold: No such runway " << activeRunway <<
" at " << apt->
ident());
993 double currentAltitude = waypoints.back()->getAltitude();
994 double distanceOut = apt->
getDynamics()->getRunwayQueue(rwy->name())->getApproachDistance();
995 double lateralOffset = initialTurnRadius;
997 SGGeod secondaryTarget = rwy->pointOffCenterline(-2 * distanceOut, lateralOffset);
998 SGGeod secondHoldCenter = rwy->pointOffCenterline(-4 * distanceOut, lateralOffset);
1000 createArc(ac, secondaryTarget, rwy->headingDeg() - 90, rwy->headingDeg() + 90, 5, initialTurnRadius,
1001 currentAltitude, 0, vDescent,
"hold_1_%03d");
1002 createArc(ac, secondHoldCenter, rwy->headingDeg() + 90, rwy->headingDeg() - 90, 5, initialTurnRadius,
1003 currentAltitude, 0, vDescent,
"hold_2_%03d");
1006 arrivalTime = now + calcArrivalTimes();
1023 SGVec3d runwayDirectionVec = normalize(SGVec3d::fromGeod(rwy->
end()) - runwayPosCart);
1024 SGVec3d gsTransmitterVec = gs->
cart() - runwayPosCart;
1028 double dist = dot(runwayDirectionVec, gsTransmitterVec);
1040 const string& fltType)
1047 double vTouchdownMetric = vTouchdown * SG_KT_TO_MPS;
1048 double vTaxiMetric = vTaxi * SG_KT_TO_MPS;
1049 double decelMetric = decel * SG_KT_TO_MPS;
1053 SG_LOG(SG_AI, SG_WARN,
"FGAIFlightPlan::createLanding: No such runway " << activeRunway <<
" at " << apt->
ident());
1065 SG_LOG(SG_AI, SG_BULK,
"Heading Diff " << headingDiff);
1068 double currElev = threshold.getElevationFt();
1071 if (touchdownDistance < 0.0) {
1077 const double tanGlideslope = tan(3.0);
1081 double glideslopeEntry = -((2000 * SG_FEET_TO_METER) / tanGlideslope) + touchdownDistance;
1083 snprintf(buffer,
sizeof(buffer),
"Glideslope begin Rwy %s", activeRunway.c_str());
1085 FGAIWaypoint* wpt = createInAir(ac, buffer, rwy->
pointOnCenterline(-glideslopeEntry),
1086 currElev + 2000, vApproach);
1090 pushBackWaypoint(wpt);
1094 double decelPoint = -((500 * SG_FEET_TO_METER) / tanGlideslope) + touchdownDistance;
1096 currElev + 500, vTouchdown);
1100 pushBackWaypoint(wpt);
1103 double heightAboveRunwayStart = touchdownDistance *
1104 tan(3.0 * SG_DEGREES_TO_RADIANS) * SG_METER_TO_FEET;
1105 wpt = createInAir(ac,
"CrossThreshold", rwy->
begin(),
1106 heightAboveRunwayStart + currElev, vTouchdown);
1110 pushBackWaypoint(wpt);
1112 double rolloutDistance =
accelDistance(vTouchdownMetric, vTaxiMetric, decelMetric);
1114 SG_LOG(SG_AI, SG_BULK,
"Landing " << glideslopeEntry <<
"\t" << decelPoint <<
" Rollout " << rolloutDistance);
1116 int nPoints = (int)(rolloutDistance / 60);
1117 for (
int i = 1;
i <= nPoints;
i++) {
1118 snprintf(buffer,
sizeof(buffer),
"rollout%03d",
i);
1119 double t = 1 - pow((
double)(nPoints -
i), 2) / pow(nPoints, 2);
1121 double vel = (vTouchdownMetric * (1.0 - t)) + (vTaxiMetric * t);
1122 wpt = createOnRunway(ac, buffer, coord, currElev, vel);
1127 pushBackWaypoint(wpt);
1131 double mindist = (1.1 * rolloutDistance) + touchdownDistance;
1135 SG_LOG(SG_AI, SG_DEBUG,
"No groundnet " << apt->
getId() <<
" no landing created.");
1149 wpt = createOnRunway(ac,
"runwayexit", tn->geod(), currElev, vTaxi);
1153 pushBackWaypoint(wpt);
1157 arrivalTime = now + calcArrivalTimes();
1172 double vTaxiReduced = vTaxi * (2.0 / 3.0);
1173 if (!gate.isValid()) {
1174 wpt = createOnGround(ac,
"END-ParkingInvalidGate", apt->
geod(), aptElev,
1176 pushBackWaypoint(wpt);
1180 FGParking* parking = gate.parking();
1181 double reverseHeading = SGMiscd::normalizePeriodic(0, 360, parking->
getHeading() + 180.0);
1185 SGGeodesy::direct(parking->
geod(), reverseHeading, 18,
1188 wpt = createOnGround(ac,
"parking1", pos, aptElev, 3);
1189 pushBackWaypoint(wpt);
1191 SGGeodesy::direct(parking->
geod(), reverseHeading, 14,
1193 wpt = createOnGround(ac,
"parking2", pos, aptElev, 3);
1194 pushBackWaypoint(wpt);
1196 SGGeodesy::direct(parking->
geod(), reverseHeading, 10,
1198 wpt = createOnGround(ac,
"parking3", pos, aptElev, 2);
1199 pushBackWaypoint(wpt);
1201 SGGeodesy::direct(parking->
geod(), reverseHeading, 6,
1203 wpt = createOnGround(ac,
"parking4", pos, aptElev, 2);
1204 pushBackWaypoint(wpt);
1206 SGGeodesy::direct(parking->
geod(), reverseHeading, 3,
1208 wpt = createOnGround(ac,
"parking5", pos, aptElev, 2);
1209 pushBackWaypoint(wpt);
1212 snprintf(buffer,
sizeof(buffer),
"Parking-%s", parking->
getName().c_str());
1214 wpt = createOnGround(ac, buffer, parking->
geod(), aptElev, vTaxiReduced / 3);
1215 pushBackWaypoint(wpt);
1218 wpt = createOnGround(ac,
"Beyond-Parking", pos, aptElev, vTaxiReduced / 3);
1219 pushBackWaypoint(wpt);
1222 wpt = createOnGround(ac,
"END-Parking", pos, aptElev, vTaxiReduced / 3);
1223 pushBackWaypoint(wpt);
1252 if ((fltType ==
"gate") || (fltType ==
"cargo")) {
1255 if (fltType ==
"ga") {
1258 if (fltType ==
"ul") {
1261 if ((fltType ==
"mil-fighter") || (fltType ==
"mil-transport")) {
1268double FGAIFlightPlan::getTurnRadius(
double speed,
bool inAir)
1272 turn_radius = ((360 / 30) * fabs(speed)) / (2 *
M_PI);
1274 turn_radius = 0.1911 * speed * speed;
static double accelDistance(double v0, double v1, double accel)
static double pitchDistance(double pitchAngleDeg, double altGainM)
static double runwayGlideslopeTouchdownDistance(FGRunway *rwy)
compute the distance along the centerline, to the ILS glideslope transmitter.
SGSharedPtr< FGTaxiNode > FGTaxiNodeRef
SGSharedPtr< FGRunway > FGRunwayRef
double getBearing(double crse)
Returns a normalised bearing.
PerformanceData * getPerformance()
double getAltitude() const
FGAISchedule * getTrafficRef()
double getTrueHeadingDeg() const
const std::string & getCallSign() const
double _getHeading() const
void setHeading(double heading)
void IncrementWaypoint(bool erase)
const char * getRunwayClassFromTrafficType(const std::string &fltType)
bool createTakeOff(FGAIAircraft *, bool, FGAirport *, const SGGeod &pos, double speed, const std::string &flightType)
bool create(FGAIAircraft *, FGAirport *dep, FGAirport *arr, int leg, double alt, double speed, double lat, double lon, bool firstLeg, double radius, const std::string &fltType, const std::string &aircraftType, const std::string &airline, double distance)
bool createPushBack(FGAIAircraft *, bool, FGAirport *, double radius, const std::string &, const std::string &, const std::string &)
void setGear_down(bool grd)
void setAltitude(double alt)
void setCrossat(double val)
void setName(const std::string &nam)
double getLatitude() const
void setStrobeLight(bool strobe)
void setOn_ground(bool grn)
void setTrackLength(double tl)
const std::string & getName()
void setSpoilers(double val)
void setFinished(bool fin)
void setLongitude(double lon)
void setLatitude(double lat)
void setSpeed(double spd)
void setLandingLight(bool ldg)
void setPowerDownLights()
double getLongitude() const
void setFlaps(double val)
void setTaxiLight(bool taxi)
double getAltitude() const
void setSpeedBrakes(double val)
void setNavLight(bool nav)
void setRouteIndex(int rte)
FGAirportDynamicsRef getDynamics() const
double getElevation() const
FGRunwayRef getRunwayByIdent(const std::string &aIdent) const
bool hasRunwayWithIdent(const std::string &aIdent) const
const std::string & getId() const
FGGroundNetwork * groundNetwork() const
SGTime * get_time_params() const
FGTaxiNodeRef findNearestNodeOnRunwayExit(const SGGeod &aGeod, FGRunway *aRunway=NULL) const
Returns the nearest node in that is in direction of runway heading.
FGTaxiNodeRef findNearestNodeOnRunwayEntry(const SGGeod &aGeod) const
FGTaxiNodeRef findNearestNode(const SGGeod &aGeod) const
FGTaxiRoute findShortestRoute(FGTaxiNode *start, FGTaxiNode *end, bool fullSearch=true)
double getHeading() const
std::string getName() const
FGTaxiNodeRef getPushBackPoint()
virtual const SGGeod & geod() const
virtual const SGVec3d & cart() const
The cartesian position associated with this object.
const std::string & ident() const
double headingDeg() const
Runway heading in degrees.
SGGeod pointOnCenterline(double aOffset) const
Retrieve a position on the extended centerline.
FGNavRecord * glideslope() const
retrieve the associated glideslope transmitter, if one is defined.
SGGeod pointOnCenterlineDisplaced(double aOffset) const
Retrieve a position on the extended centerline.
SGGeod end() const
Get the 'far' end - this is equivalent to calling pointOnCenterline(lengthFt());.
SGGeod threshold() const
Get the (possibly displaced) threshold point.
SGGeod begin() const
Get the runway beginning point - this is syntatic sugar, equivalent to calling pointOnCenterline(0....
double displacedThresholdM() const
bool next(FGTaxiNodeRef &nde, int *rte)
static double outerTangentsLength(SGGeod m1, SGGeod m2, double r1, double r2)
Length of outer tangent between two circles.
static std::array< double, 2 > innerTangentsAngle(SGGeod m1, SGGeod m2, double r1, double r2)
Angles of inner tangent between two circles.
static double innerTangentsLength(SGGeod m1, SGGeod m2, double r1, double r2)
Length of inner tangent between two circles.
static std::array< double, 2 > outerTangentsAngle(SGGeod m1, SGGeod m2, double r1, double r2)
Angles of outer tangent between two circles normalized to 0-360.