diff --git a/src/AIModel/AIAircraft.cxx b/src/AIModel/AIAircraft.cxx index 063a72713..e337dd767 100644 --- a/src/AIModel/AIAircraft.cxx +++ b/src/AIModel/AIAircraft.cxx @@ -333,7 +333,9 @@ void FGAIAircraft::ProcessFlightPlan( double dt, time_t now ) { dt_count = 0; double distanceToDescent; - if (reachedEndOfCruise(distanceToDescent)) { + // Not the best solution. Double adding of legs is possible + if (*fp->getLastWayPoint() == fp->getNextWaypoint() && + reachedEndOfCruise(distanceToDescent)) { if (!loadNextLeg(distanceToDescent)) { setDie(true); return; @@ -591,7 +593,6 @@ bool FGAIAircraft::loadNextLeg(double distance) { FGAirport *arr = trafficRef->getArrivalAirport(); if (!(dep && arr)) { setDie(true); - } else { double cruiseAlt = trafficRef->getCruiseAlt() * 100; @@ -1473,7 +1474,7 @@ bool FGAIAircraft::reachedEndOfCruise(double &distance) { distance = distanceCoveredByDescent; if (dist < distanceCoveredByDescent) { - SG_LOG(SG_AI, SG_BULK, "End Of Cruise"); + SG_LOG(SG_AI, SG_BULK, getCallSign() << "|End Of Cruise"); return true; } else { return false; diff --git a/src/AIModel/AIFlightPlan.cxx b/src/AIModel/AIFlightPlan.cxx index bc3b2dac6..81d8e7069 100644 --- a/src/AIModel/AIFlightPlan.cxx +++ b/src/AIModel/AIFlightPlan.cxx @@ -131,16 +131,19 @@ FGAIFlightPlan::FGAIFlightPlan(const string& filename) : } -// This is a modified version of the constructor, -// Which not only reads the waypoints from a -// Flight plan file, but also adds the current -// Position computed by the traffic manager, as well -// as setting speeds and altitude computed by the -// traffic manager. +/** + * This is a modified version of the constructor, + * Which not only reads the waypoints from a + * Flight plan file, but also adds the current + * Position computed by the traffic manager, as well + * as setting speeds and altitude computed by the + * traffic manager. + */ FGAIFlightPlan::FGAIFlightPlan(FGAIAircraft *ac, const std::string& p, double course, time_t start, + time_t remainingTime, FGAirport *dep, FGAirport *arr, bool firstLeg, @@ -168,7 +171,7 @@ FGAIFlightPlan::FGAIFlightPlan(FGAIAircraft *ac, if (parseProperties(p)) { isValid = true; } else { - createWaypoints(ac, course, start, dep, arr, firstLeg, radius, + createWaypoints(ac, course, start, remainingTime, dep, arr, firstLeg, radius, alt, lat, lon, speed, fltType, acType, airline); } } @@ -182,6 +185,7 @@ FGAIFlightPlan::~FGAIFlightPlan() void FGAIFlightPlan::createWaypoints(FGAIAircraft *ac, double course, time_t start, + time_t remainingTime, FGAirport *dep, FGAirport *arr, bool firstLeg, @@ -196,25 +200,22 @@ void FGAIFlightPlan::createWaypoints(FGAIAircraft *ac, { time_t now = globals->get_time_params()->get_cur_time(); time_t timeDiff = now-start; - leg = 1; + leg = AILeg::STARTUP_PUSHBACK; if ((timeDiff > 60) && (timeDiff < 1500)) - leg = 2; - //else if ((timeDiff >= 1200) && (timeDiff < 1500)) { - //leg = 3; - //ac->setTakeOffStatus(2); - //} + leg = AILeg::TAXI; else if ((timeDiff >= 1500) && (timeDiff < 2000)) - leg = 4; - else if (timeDiff >= 2000) - leg = 5; - /* - if (timeDiff >= 2000) - leg = 5; - */ + leg = AILeg::TAKEOFF; + else if (timeDiff >= 2000) { + if (remainingTime > 2000) { + leg = AILeg::CRUISE; + } else { + leg = AILeg::APPROACH; + } + } - SG_LOG(SG_AI, SG_DEBUG, "Route from " << dep->getId() << " to " << arr->getId() << - ". Set leg to : " << leg << " " << ac->getTrafficRef()->getCallSign()); + SG_LOG(SG_AI, SG_DEBUG, ac->getTrafficRef()->getCallSign() << "|Route from " << dep->getId() << " to " << arr->getId() << + ". Set leg to : " << leg << " " << remainingTime); wpt_iterator = waypoints.begin(); bool dist = 0; @@ -392,6 +393,8 @@ void FGAIFlightPlan::IncrementWaypoint(bool eraseWaypoints ) return; if (wpt_iterator+1 == waypoints.end()) return; + if (waypoints.size()<3) + return; FGAIWaypoint* previousWP = *(wpt_iterator -1); FGAIWaypoint* currentWP = *(wpt_iterator); FGAIWaypoint* nextWP = *(wpt_iterator + 1); diff --git a/src/AIModel/AIFlightPlan.hxx b/src/AIModel/AIFlightPlan.hxx index e5cb1f92f..d91184426 100644 --- a/src/AIModel/AIFlightPlan.hxx +++ b/src/AIModel/AIFlightPlan.hxx @@ -137,6 +137,7 @@ public: const std::string& p, double course, time_t start, + time_t remainingTime, FGAirport *dep, FGAirport *arr, bool firstLeg, @@ -276,7 +277,7 @@ private: /**Create an arc flightplan around a center from startAngle to endAngle.*/ void createArc(FGAIAircraft *ac, const SGGeod& center, int startAngle, int endAngle, int increment, int radius, double aElev, double aSpeed, const char* pattern); - void createLine(FGAIAircraft *ac, const SGGeod& startPoint, double azimuth, double dist, double dAlt, double vDescent); + void createLine(FGAIAircraft *ac, const SGGeod& startPoint, double azimuth, double dist, double dAlt, double vDescent, const char* pattern); bool createLandingTaxi(FGAIAircraft *, FGAirport *apt, double radius, const std::string& fltType, const std::string& acType, const std::string& airline); void createDefaultLandingTaxi(FGAIAircraft *, FGAirport* aAirport); void createDefaultTakeoffTaxi(FGAIAircraft *, FGAirport* aAirport, FGRunway* aRunway); @@ -300,9 +301,14 @@ private: */ bool parseProperties(const std::string& filename); + /** + * Creates the waypoints for this Flightplan. + */ + void createWaypoints(FGAIAircraft *ac, double course, time_t start, + time_t remainingTime, FGAirport *dep, FGAirport *arr, bool firstLeg, diff --git a/src/AIModel/AIFlightPlanCreate.cxx b/src/AIModel/AIFlightPlanCreate.cxx index 9ff145f75..5ddc741e3 100644 --- a/src/AIModel/AIFlightPlanCreate.cxx +++ b/src/AIModel/AIFlightPlanCreate.cxx @@ -69,9 +69,9 @@ bool FGAIFlightPlan::create(FGAIAircraft * ac, FGAirport * dep, const string & aircraftType, const string & airline, double distance) { - if( legNr <= 3 ) + if( legNr <= AILeg::TAKEOFF ) SG_LOG(SG_AI, SG_BULK, "Create Leg " << legNr << " " << (firstFlight?"First":"") << " Old Leg " << getLeg() << " At Airport : " << dep->getId()); - else if( legNr<= 6 ) + else if( legNr<= AILeg::APPROACH ) SG_LOG(SG_AI, SG_BULK, "Create Leg " << legNr << " " << (firstFlight?"First":"") << " Old Leg " << getLeg() << " Departure Airport : " << dep->getId() << " Arrival Airport : " << arr->getId()); else SG_LOG(SG_AI, SG_BULK, "Create Leg " << legNr << " " << (firstFlight?"First":"") << " Old Leg " << getLeg() << " At Airport : " << arr->getId()); @@ -79,35 +79,35 @@ bool FGAIFlightPlan::create(FGAIAircraft * ac, FGAirport * dep, bool retVal = true; int currWpt = wpt_iterator - waypoints.begin(); switch (legNr) { - case 1: + case AILeg::STARTUP_PUSHBACK: retVal = createPushBack(ac, firstFlight, dep, radius, fltType, aircraftType, airline); break; - case 2: + case AILeg::TAXI: retVal = createTakeoffTaxi(ac, firstFlight, dep, radius, fltType, aircraftType, airline); break; - case 3: + case AILeg::TAKEOFF: retVal = createTakeOff(ac, firstFlight, dep, SGGeod::fromDeg(longitude, latitude), speed, fltType); break; - case 4: + case AILeg::CLIMB: retVal = createClimb(ac, firstFlight, dep, arr, speed, alt, fltType); break; - case 5: + case AILeg::CRUISE: retVal = createCruise(ac, firstFlight, dep, arr, SGGeod::fromDeg(longitude, latitude), speed, alt, fltType); break; - case 6: + case AILeg::APPROACH: retVal = createDescent(ac, arr, SGGeod::fromDeg(longitude, latitude), speed, alt, fltType, distance); break; - case 7: + case AILeg::LANDING: retVal = createLanding(ac, arr, fltType); break; - case 8: + case AILeg::PARKING_TAXI: retVal = createLandingTaxi(ac, arr, radius, fltType, aircraftType, airline); break; - case 9: + case AILeg::PARKING: retVal = createParking(ac, arr, radius); break; default: @@ -202,7 +202,7 @@ int endAngle, int increment, int radius, double aElev, double aSpeed, const char } } -void FGAIFlightPlan::createLine( FGAIAircraft *ac, const SGGeod& startPoint, double azimuth, double dist, double dAlt, double vDescent) { +void FGAIFlightPlan::createLine( FGAIAircraft *ac, const SGGeod& startPoint, double azimuth, double dist, double dAlt, double vDescent, const char* pattern) { SGGeod dummyAz2; double nPoints = dist/(vDescent/2); char buffer[20]; @@ -211,7 +211,7 @@ void FGAIFlightPlan::createLine( FGAIAircraft *ac, const SGGeod& startPoint, dou double currentDist = i * (dist / nPoints); double currentAltitude = ac->getAltitude() - (i * (dAlt / nPoints)); SGGeod result = SGGeodesy::direct(startPoint, azimuth, currentDist); - snprintf(buffer, sizeof(buffer), "descent%03d", i); + snprintf(buffer, sizeof(buffer), pattern, i); FGAIWaypoint* wpt = createInAir(ac, buffer, result, currentAltitude, vDescent); wpt->setCrossat(currentAltitude); wpt->setTrackLength((dist / nPoints)); @@ -744,9 +744,11 @@ bool FGAIFlightPlan::createClimb(FGAIAircraft * ac, bool firstFlight, * Generate a flight path from the last waypoint of the cruise to * the permission to land point ******************************************************************/ -bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, +bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, + FGAirport * apt, const SGGeod& current, - double speed, double alt, + double speed, + double alt, const string & fltType, double requiredDistance) { @@ -762,16 +764,21 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, //Beginning of Descent const string& rwyClass = getRunwayClassFromTrafficType(fltType); - double heading = ac->getTrafficRef()->getCourse(); + double heading = ac->getTrueHeadingDeg(); apt->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway, heading); if (!apt->hasRunwayWithIdent(activeRunway)) return false; FGRunwayRef rwy = apt->getRunwayByIdent(activeRunway); + if (waypoints.size()==0 && ac->getTrueHeadingDeg()==0) { + // we obviously have no previous state and are free to position the aircraft + double courseTowardsThreshold = SGGeodesy::courseDeg(current, rwy->pointOnCenterlineDisplaced(0)); + ac->setHeading(courseTowardsThreshold); + } + // depending on entry we differ approach (teardrop/direct/parallel) - // Create a slow descent path that ends 250 lateral to the runway. double initialTurnRadius = getTurnRadius(vDescent, true); //double finalTurnRadius = getTurnRadius(vApproach, true); @@ -787,6 +794,7 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, } SGGeod initialTarget = rwy->pointOnCenterline(-distanceOut); + SGGeod otherRwyEnd = rwy->pointOnCenterline(rwy->lengthM()); SGGeod secondaryTarget = rwy->pointOffCenterline(-2 * distanceOut, lateralOffset); SGGeod secondHoldCenter = @@ -797,50 +805,204 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, double secondaryAzimuth = SGGeodesy::courseDeg(current, secondaryTarget); double initialHeadingDiff = SGMiscd::normalizePeriodic(-180, 180, azimuth-secondaryAzimuth); double dummyAz2; + double courseTowardsThreshold = SGGeodesy::courseDeg(current, rwy->pointOnCenterlineDisplaced(0)); + double courseTowardsRwyEnd = SGGeodesy::courseDeg(current, otherRwyEnd); + double headingDiffToRunwayThreshold = SGMiscd::normalizePeriodic(-180, 180, ac->getTrueHeadingDeg() - courseTowardsThreshold); + double headingDiffToRunwayEnd = SGMiscd::normalizePeriodic(-180, 180, ac->getTrueHeadingDeg() - courseTowardsRwyEnd); - SG_LOG(SG_AI, SG_BULK, " Heading Diff " << headingDiffRunway << - " Distance " << distance << + SG_LOG(SG_AI, SG_BULK, ac->getCallSign() << + "| " << + " WPs : " << waypoints.size() << + " Heading Diff (rwy) : " << headingDiffRunway << + " Distance : " << distance << " Azimuth : " << azimuth << - " Heading : " << ac->getTrueHeadingDeg() << " Lateral : " << lateralOffset); + " Heading : " << ac->getTrueHeadingDeg() << + " Initial Headingdiff " << initialHeadingDiff << + " Lateral : " << lateralOffset); + // Erase the two bogus BOD points: Note check for conflicts with scripted AI flightPlans + IncrementWaypoint(false); + IncrementWaypoint(false); - if (fabs(headingDiffRunway)>=30) { - // Entering not "straight" into runway so we do a s-curve - int rightAngle = initialHeadingDiff<0?90:-90; - SGGeod firstTurnCenter = SGGeodesy::direct(current, ac->getTrueHeadingDeg() + rightAngle, initialTurnRadius); - int firstTurnIncrement = initialHeadingDiff<0?2:-2; - const double heading = VectorMath::innerTangentsAngle(firstTurnCenter, secondaryTarget, initialTurnRadius, initialTurnRadius )[0]; - createArc(ac, firstTurnCenter, ac->_getHeading()-rightAngle, heading-rightAngle, firstTurnIncrement, initialTurnRadius, ac->getAltitude(), vDescent, "initialturn%03d"); - double length = VectorMath::innerTangentsLength(firstTurnCenter, secondaryTarget, initialTurnRadius, initialTurnRadius ); - createLine(ac, waypoints.back()->getPos(), heading, length, 50, vDescent); - int holdsPatterns = 0; - // Length of - if (holdsPatterns>0) { - // Turn into hold - createArc(ac, secondaryTarget, heading+rightAngle, rwy->headingDeg()+rightAngle, firstTurnIncrement*-1, initialTurnRadius, - waypoints.back()->getAltitude(), vDescent, "turnintohold%03d"); - for (int t = 0; t < holdsPatterns; t++) - { - /* - createArc(ac, secondaryTarget, endval, endval+180, increment, initialTurnRadius, - currentAltitude, vDescent, "hold_1_%03d"); - createArc(ac, secondHoldCenter, endval+180, endval, increment, initialTurnRadius, - currentAltitude, vDescent, "hold_2_%03d"); - */ + if (fabs(headingDiffRunway)>=30 && fabs(headingDiffRunway)<=150 ) { + if (distance < (2*initialTurnRadius)) { + // Too near so we pass over and enter over other side + SG_LOG(SG_AI, SG_BULK, ac->getCallSign() << "| Enter near S curve"); + secondaryTarget = + rwy->pointOffCenterline(-2 * distanceOut, -lateralOffset); + secondHoldCenter = + rwy->pointOffCenterline(-3 * distanceOut, -lateralOffset); + + // Entering not "straight" into runway so we do a s-curve + int rightAngle = headingDiffRunway>0?90:-90; + int firstTurnIncrement = headingDiffRunway>0?2:-2; + + SGGeod firstTurnCenter = SGGeodesy::direct(current, ac->getTrueHeadingDeg() + rightAngle, initialTurnRadius); + SGGeod newCurrent = current; + // If we are not far enough cross over + if (abs(headingDiffToRunwayThreshold)<90) { + newCurrent = SGGeodesy::direct(current, ac->getTrueHeadingDeg(), distance + 1000); + firstTurnCenter = SGGeodesy::direct(newCurrent, ac->getTrueHeadingDeg() + rightAngle, initialTurnRadius); + createLine(ac, current, ac->getTrueHeadingDeg(), distance + 1000, 50, vDescent, "move%03d"); + } + int offset = 1000; + while (SGGeodesy::distanceM(firstTurnCenter, secondaryTarget) < 2*initialTurnRadius) { + newCurrent = SGGeodesy::direct(newCurrent, ac->getTrueHeadingDeg(), offset+=100); + firstTurnCenter = SGGeodesy::direct(newCurrent, ac->getTrueHeadingDeg() + rightAngle, initialTurnRadius); + } + const double heading = VectorMath::outerTangentsAngle(firstTurnCenter, secondaryTarget, initialTurnRadius, initialTurnRadius )[0]; + createArc(ac, firstTurnCenter, ac->_getHeading()-rightAngle, heading-rightAngle, firstTurnIncrement, initialTurnRadius, ac->getAltitude(), vDescent, "near-initialturn%03d"); + double length = VectorMath::innerTangentsLength(firstTurnCenter, secondaryTarget, initialTurnRadius, initialTurnRadius ); + createLine(ac, waypoints.back()->getPos(), heading, length, 50, vDescent, "descent%03d"); + int holdsPatterns = 0; + // Length of + if (holdsPatterns>0) { + // Turn into hold + createArc(ac, secondaryTarget, heading+rightAngle, rwy->headingDeg()+rightAngle, firstTurnIncrement*-1, initialTurnRadius, + waypoints.back()->getAltitude(), vDescent, "turnintohold%03d"); + for (int t = 0; t < holdsPatterns; t++) + { + /* + createArc(ac, secondaryTarget, endval, endval+180, increment, initialTurnRadius, + currentAltitude, vDescent, "hold_1_%03d"); + createArc(ac, secondHoldCenter, endval+180, endval, increment, initialTurnRadius, + currentAltitude, vDescent, "hold_2_%03d"); + */ + } + } else { + int startVal = SGMiscd::normalizePeriodic(0, 360, heading-rightAngle); + int endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg()-rightAngle); + // Turn into runway + createArc(ac, secondaryTarget, startVal ,endVal , firstTurnIncrement, initialTurnRadius, + waypoints.back()->getAltitude(), vDescent, "turn%03d"); } } else { - int startVal = SGMiscd::normalizePeriodic(0, 360, heading+rightAngle); - int endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg()+rightAngle); - // Turn into runway - createArc(ac, secondaryTarget, startVal, endVal, firstTurnIncrement*-1, initialTurnRadius, - waypoints.back()->getAltitude(), vDescent, "turn%03d"); + SG_LOG(SG_AI, SG_BULK, ac->getCallSign() << "| Enter far S curve"); + // Entering not "straight" into runway so we do a s-curve + int rightAngle = headingDiffRunway>0?90:-90; + int firstTurnIncrement = headingDiffRunway>0?2:-2; + SGGeod firstTurnCenter = SGGeodesy::direct(current, ac->getTrueHeadingDeg() + rightAngle, initialTurnRadius); + int innerTangent = headingDiffRunway<0?0:1; + int offset = 1000; + while (SGGeodesy::distanceM(firstTurnCenter, secondaryTarget)<2*initialTurnRadius) { + secondaryTarget = rwy->pointOffCenterline(-2 * distanceOut + (offset+=1000), lateralOffset); + } + const double heading = VectorMath::innerTangentsAngle(firstTurnCenter, secondaryTarget, initialTurnRadius, initialTurnRadius )[innerTangent]; + createArc(ac, firstTurnCenter, ac->_getHeading()-rightAngle, heading-rightAngle, firstTurnIncrement, initialTurnRadius, ac->getAltitude(), vDescent, "far-initialturn%03d"); + double length = VectorMath::innerTangentsLength(firstTurnCenter, secondaryTarget, initialTurnRadius, initialTurnRadius ); + createLine(ac, waypoints.back()->getPos(), heading, length, 50, vDescent, "descent%03d"); + int holdsPatterns = 0; + // Length of + if (holdsPatterns>0) { + // Turn into hold + createArc(ac, secondaryTarget, heading+rightAngle, rwy->headingDeg()+rightAngle, firstTurnIncrement*-1, initialTurnRadius, + waypoints.back()->getAltitude(), vDescent, "turnintohold%03d"); + for (int t = 0; t < holdsPatterns; t++) + { + /* + createArc(ac, secondaryTarget, endval, endval+180, increment, initialTurnRadius, + currentAltitude, vDescent, "hold_1_%03d"); + createArc(ac, secondHoldCenter, endval+180, endval, increment, initialTurnRadius, + currentAltitude, vDescent, "hold_2_%03d"); + */ + } + } else { + int startVal = SGMiscd::normalizePeriodic(0, 360, heading+rightAngle); + int endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg()+rightAngle); + // Turn into runway + createArc(ac, secondaryTarget, startVal, endVal, firstTurnIncrement*-1, initialTurnRadius, + waypoints.back()->getAltitude(), vDescent, "s-turn%03d"); + } + } + } else if (fabs(headingDiffRunway)>=150 ) { + // We are entering downwind + if (distance < (2*initialTurnRadius)) { + // Too near so we pass over and enter over other side + SG_LOG(SG_AI, SG_BULK, ac->getCallSign() << "| Enter near downrunway"); + secondaryTarget = + rwy->pointOffCenterline(-2 * distanceOut, -lateralOffset); + secondHoldCenter = + rwy->pointOffCenterline(-3 * distanceOut, -lateralOffset); + + // Entering not "straight" into runway so we do a s-curve + int rightAngle = azimuth>0?90:-90; + int firstTurnIncrement = azimuth>0?2:-2; + + SGGeod firstTurnCenter = SGGeodesy::direct(current, ac->getTrueHeadingDeg() + rightAngle, initialTurnRadius); + // rwy->headingDeg()-rightAngle + int endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg()-180); + SGGeod secondTurnCenter = SGGeodesy::direct(firstTurnCenter, endVal, 2*initialTurnRadius); + createArc(ac, firstTurnCenter, ac->_getHeading()-rightAngle, endVal, firstTurnIncrement, initialTurnRadius, ac->getAltitude(), vDescent, "d-near-initialturn%03d"); + endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg()); + // int endVal2 = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg()-rightAngle); + const double endVal2 = VectorMath::outerTangentsAngle(secondTurnCenter, secondaryTarget, initialTurnRadius, initialTurnRadius )[0]; + createArc(ac, secondTurnCenter, endVal, endVal2+rightAngle, -firstTurnIncrement, initialTurnRadius, ac->getAltitude(), vDescent, "secondturn%03d"); + //outer + double length = VectorMath::outerTangentsLength(secondTurnCenter, secondaryTarget, initialTurnRadius, initialTurnRadius ); + int reverseRwyHeading = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg() - 180); + createLine(ac, waypoints.back()->getPos(), endVal2, length, 50, vDescent, "descent%03d"); + int holdsPatterns = 0; + // Length of + if (holdsPatterns>0) { + // Turn into hold + createArc(ac, secondaryTarget, heading+rightAngle, rwy->headingDeg()+rightAngle, firstTurnIncrement*-1, initialTurnRadius, + waypoints.back()->getAltitude(), vDescent, "turnintohold%03d"); + for (int t = 0; t < holdsPatterns; t++) + { + /* + createArc(ac, secondaryTarget, endval, endval+180, increment, initialTurnRadius, + currentAltitude, vDescent, "hold_1_%03d"); + createArc(ac, secondHoldCenter, endval+180, endval, increment, initialTurnRadius, + currentAltitude, vDescent, "hold_2_%03d"); + */ + } + } else { + int endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg()+rightAngle); + // Turn into runway + createArc(ac, secondaryTarget, endVal2+rightAngle, endVal, -firstTurnIncrement, initialTurnRadius, + waypoints.back()->getAltitude(), vDescent, "turn%03d"); + } + } else { + SG_LOG(SG_AI, SG_BULK, ac->getCallSign() << "| Enter far S downrunway"); + // Entering not "straight" into runway so we do a s-curve + int rightAngle = azimuth<0?90:-90; + int firstTurnIncrement = azimuth<0?2:-2; + int innerTangent = headingDiffRunway<0?0:1; + SGGeod firstTurnCenter = SGGeodesy::direct(current, ac->getTrueHeadingDeg() + rightAngle, initialTurnRadius); + const double heading = VectorMath::innerTangentsAngle(firstTurnCenter, secondaryTarget, initialTurnRadius, initialTurnRadius )[innerTangent]; + createArc(ac, firstTurnCenter, ac->_getHeading()-rightAngle, heading-rightAngle, firstTurnIncrement, initialTurnRadius, ac->getAltitude(), vDescent, "d-far-initialturn%03d"); + double length = VectorMath::innerTangentsLength(firstTurnCenter, secondaryTarget, initialTurnRadius, initialTurnRadius ); + createLine(ac, waypoints.back()->getPos(), heading, length, 50, vDescent, "descent%03d"); + int holdsPatterns = 0; + // Length of + if (holdsPatterns>0) { + // Turn into hold + createArc(ac, secondaryTarget, heading+rightAngle, rwy->headingDeg()+rightAngle, firstTurnIncrement*-1, initialTurnRadius, + waypoints.back()->getAltitude(), vDescent, "turnintohold%03d"); + for (int t = 0; t < holdsPatterns; t++) + { + /* + createArc(ac, secondaryTarget, endval, endval+180, increment, initialTurnRadius, + currentAltitude, vDescent, "hold_1_%03d"); + createArc(ac, secondHoldCenter, endval+180, endval, increment, initialTurnRadius, + currentAltitude, vDescent, "hold_2_%03d"); + */ + } + } else { + int startVal = SGMiscd::normalizePeriodic(0, 360, heading+rightAngle); + int endVal = SGMiscd::normalizePeriodic(0, 360, rwy->headingDeg()+rightAngle); + // Turn into runway + createArc(ac, secondaryTarget, startVal, endVal, -firstTurnIncrement, initialTurnRadius, + waypoints.back()->getAltitude(), vDescent, "d-s-turn%03d"); + } } } else { + SG_LOG(SG_AI, SG_BULK, ac->getCallSign() << "| Enter far straight"); // Entering "straight" into runway so only one turn int rightAngle = headingDiffRunway>0?90:-90; SGGeod firstTurnCenter = SGGeodesy::direct(current, ac->getTrueHeadingDeg() - rightAngle, initialTurnRadius); int firstTurnIncrement = headingDiffRunway>0?-2:2; const double heading = rwy->headingDeg(); - createArc(ac, firstTurnCenter, ac->_getHeading()+rightAngle, heading+rightAngle, firstTurnIncrement, initialTurnRadius, ac->getAltitude(), vDescent, "initialturn_%03d"); + createArc(ac, secondaryTarget, ac->_getHeading()+rightAngle, heading+rightAngle, firstTurnIncrement, initialTurnRadius, ac->getAltitude(), vDescent, "straight_turn_%03d"); } // To prevent absurdly steep approaches, compute the origin from where the approach should have started @@ -861,55 +1023,12 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, azimuth = SGGeodesy::courseDeg(origin, initialTarget); } - double dAlt = 0; // = alt - (apt->getElevation() + 2000); - FGTaxiNodeRef tn; - if (apt->groundNetwork()) { - tn = apt->groundNetwork()->findNearestNode(refPoint); - } - - if (tn) { - dAlt = alt - ((tn->getElevationFt()) + 2000); - } else { - dAlt = alt - (apt->getElevation() + 2000); - } - - //cerr << "Phase 1: Linear Descent path to runway" << rwy->name() << endl; - // Create an initial destination point on a semicircle - //cerr << "lateral offset : " << lateralOffset << endl; - //cerr << "Distance : " << distance << endl; - //cerr << "Azimuth : " << azimuth << endl; - //cerr << "Initial Lateral point: " << lateralOffset << endl; -// double lat = refPoint.getLatitudeDeg(); -// double lon = refPoint.getLongitudeDeg(); - //cerr << "Reference point (" << lat << ", " << lon << ")." << endl; -// lat = initialTarget.getLatitudeDeg(); -// lon = initialTarget.getLongitudeDeg(); - //cerr << "Initial Target point (" << lat << ", " << lon << ")." << endl; - -#if 0 - double ratio = initialTurnRadius / distance; - if (ratio > 1.0) - ratio = 1.0; - if (ratio < -1.0) - ratio = -1.0; - - double newHeading = asin(ratio) * SG_RADIANS_TO_DEGREES; - double newDistance = - cos(newHeading * SG_DEGREES_TO_RADIANS) * distance; - //cerr << "new distance " << newDistance << ". additional Heading " << newHeading << endl; -#endif - - double side = azimuth - rwy->headingDeg(); - if (side < 0.0) - side += 360.0; - // Calculate the ETA at final, based on remaining distance, and approach speed. // distance should really consist of flying time to terniary target, plus circle // but the distance to secondary target should work as a reasonable approximation // aditionally add the amount of distance covered by making a turn of "side" - double turnDistance = (2 * M_PI * initialTurnRadius) * (side / 360.0); time_t remaining = - (turnDistance + distance) / ((vDescent * SG_NM_TO_METER) / 3600.0); + (initialTurnRadius + distance) / ((vDescent * SG_NM_TO_METER) / 3600.0); time_t now = globals->get_time_params()->get_cur_time(); //if (ac->getTrafficRef()->getCallSign() == fgGetString("/ai/track-callsign")) { @@ -940,110 +1059,6 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, additionalTimeNeeded -= holdsPatterns*240; - double distanceCovered = - ((vApproach * SG_NM_TO_METER) / 3600.0) * additionalTimeNeeded; - distanceOut += distanceCovered; - - azimuth = SGGeodesy::courseDeg(origin, secondaryTarget); - distance = SGGeodesy::distanceM(origin, secondaryTarget); - double ratio = initialTurnRadius / distance; - if (ratio > 1.0) - ratio = 1.0; - if (ratio < -1.0) - ratio = -1.0; - - double newHeading = asin(ratio) * SG_RADIANS_TO_DEGREES; - double newDistance = cos(newHeading * SG_DEGREES_TO_RADIANS) * distance; - //cerr << "new distance realative to secondary target: " << newDistance << ". additional Heading " << newHeading << endl; - - if (side < 180) { - azimuth += newHeading; - } else { - azimuth -= newHeading; - } - - SGGeod tertiaryTarget; - SGGeodesy::direct(origin, azimuth, newDistance, tertiaryTarget, dummyAz2); - -// lat = tertiaryTarget.getLatitudeDeg(); -// lon = tertiaryTarget.getLongitudeDeg(); - //cerr << "tertiary Target point (" << lat << ", " << lon << ")." << endl; - - - //cerr << "Phase 2: Circle " << endl; - double initialAzimuth = - SGGeodesy::courseDeg(secondaryTarget, tertiaryTarget); - double finalAzimuth = - SGGeodesy::courseDeg(secondaryTarget, initialTarget); - - //cerr << "Angles from secondary target: " << initialAzimuth << " " << finalAzimuth << endl; - int increment, startval, endval; - // circle right around secondary target if orig of position is to the right of the runway - // i.e. use negative angles; else circle leftward and use postivi - if (side < 180) { - increment = -2; - startval = floor(initialAzimuth); - endval = ceil(finalAzimuth); - if (endval > startval) { - endval -= 360; - } - } else { - increment = 2; - startval = ceil(initialAzimuth); - endval = floor(finalAzimuth); - if (endval < startval) { - endval += 360; - } - } - - //cerr << "creating circle between " << startval << " and " << endval << " using " << increment << endl; - //FGTaxiNode * tn = apt->getDynamics()->getGroundNetwork()->findNearestNode(initialTarget); - double currentAltitude = 0; - if (tn) { - currentAltitude = (tn->getElevationFt()) + 2000; - } else { - currentAltitude = apt->getElevation() + 2000; - } - - - - - // 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; - - //cerr << "Done" << endl; - - // Erase the two bogus BOD points: Note check for conflicts with scripted AI flightPlans - IncrementWaypoint(true); - IncrementWaypoint(true); - - if (reposition) { - double tempDistance = SGGeodesy::distanceM(current, initialTarget); - time_t eta2 = - tempDistance / ((vDescent * SG_NM_TO_METER) / 3600.0) + now; - SG_LOG(SG_ATC, SG_BULK, "Final 1 " << eta2); - time_t newEta2 = - apt->getDynamics()->getApproachController()->getRunway(rwy->name())->requestTimeSlot(eta2); - SG_LOG(SG_ATC, SG_BULK, "Final 1 " << eta2 << " " << newEta2); - arrivalTime = newEta2; - double newDistance2 = - ((vDescent * SG_NM_TO_METER) / 3600.0) * (newEta2 - now); - - IncrementWaypoint(true); // remove waypoint BOD2 - - while (checkTrackLength("final001") > newDistance2) { - SG_LOG(SG_ATC, SG_BULK, "Final 2 " << checkTrackLength("final001") ); - IncrementWaypoint(true); - } - - ac->resetPositionFromFlightPlan(); - } - - if (!waypoints.empty()) { - SG_LOG(SG_AI, SG_BULK, "Setting Node " << waypoints.back()->getName() << " to a leg end"); - waypoints.back()->setName( (waypoints.back()->getName() + string("legend"))); - } - return true; } diff --git a/src/AIModel/AIFlightPlanCreateCruise.cxx b/src/AIModel/AIFlightPlanCreateCruise.cxx index 2ffeb4583..fb0100d39 100644 --- a/src/AIModel/AIFlightPlanCreateCruise.cxx +++ b/src/AIModel/AIFlightPlanCreateCruise.cxx @@ -314,12 +314,22 @@ bool FGAIFlightPlan::createCruise(FGAIAircraft *ac, bool firstFlight, FGAirport assert( rwy != NULL ); // begin descent 110km out double distanceOut = arr->getDynamics()->getApproachController()->getRunway(rwy->name())->getApproachDistance(); //12 * SG_NM_TO_METER; - SGGeod beginDescentPoint = rwy->pointOnCenterline(-2*distanceOut); + + SGGeod beginDescentPoint = rwy->pointOnCenterline(-3*distanceOut); SGGeod secondaryDescentPoint = rwy->pointOnCenterline(0); - wpt = createInAir(ac, "BOD", beginDescentPoint, alt, vCruise); - pushBackWaypoint(wpt); - wpt = createInAir(ac, "BOD2", secondaryDescentPoint, alt, vCruise); - pushBackWaypoint(wpt); + double distanceToRwy = SGGeodesy::distanceM(current, secondaryDescentPoint); + if (distanceToRwy>4*distanceOut) { + FGAIWaypoint *bodWpt = createInAir(ac, "BOD", beginDescentPoint, alt, vCruise); + pushBackWaypoint(bodWpt); + FGAIWaypoint *bod2Wpt = createInAir(ac, "BOD2", secondaryDescentPoint, alt, vCruise); + pushBackWaypoint(bod2Wpt); + } else { + // We are too near. The descent leg takes care of this (teardrop etc) + FGAIWaypoint *bodWpt = createInAir(ac, "BOD", SGGeodesy::direct(current, ac->getTrueHeadingDeg(), 10000), alt, vCruise); + pushBackWaypoint(bodWpt); + FGAIWaypoint *bod2Wpt = createInAir(ac, "BOD2", SGGeodesy::direct(current, ac->getTrueHeadingDeg(), 15000), alt, vCruise); + pushBackWaypoint(bod2Wpt); + } return true; } diff --git a/src/AIModel/VectorMath.cxx b/src/AIModel/VectorMath.cxx index a60214df9..7bf11dacb 100644 --- a/src/AIModel/VectorMath.cxx +++ b/src/AIModel/VectorMath.cxx @@ -59,4 +59,22 @@ double VectorMath::innerTangentsLength( SGGeod m1, SGGeod m2, double r1, double SGGeod p2 = SGGeodesy::direct(m2, angle2, r2); return SGGeodesy::distanceM(p1, p2); -} \ No newline at end of file +} + +std::array VectorMath::outerTangentsAngle( SGGeod m1, SGGeod m2, double r1, double r2) { + std::array ret; + double hypothenuse = SGGeodesy::distanceM(m1, m2); + double radiusDiff = abs(r1 - r2); + double beta = atan2( radiusDiff, hypothenuse ) * SG_RADIANS_TO_DEGREES; + double gamma = SGGeodesy::courseDeg(m1, m2); + ret[0] = SGMiscd::normalizePeriodic(0, 360, gamma - beta); + ret[1] = SGMiscd::normalizePeriodic(0, 360, gamma + beta); + return ret; +} + +double VectorMath::outerTangentsLength( SGGeod m1, SGGeod m2, double r1, double r2) { + double hypothenuse = SGGeodesy::distanceM(m1, m2); + double radiusDiff = abs(r1 - r2); + double dist = sqrt(pow(hypothenuse,2)-pow(radiusDiff,2)); + return dist; +} diff --git a/src/AIModel/VectorMath.hxx b/src/AIModel/VectorMath.hxx index 6705a9261..23c450342 100644 --- a/src/AIModel/VectorMath.hxx +++ b/src/AIModel/VectorMath.hxx @@ -27,4 +27,8 @@ class VectorMath { static std::array innerTangentsAngle( SGGeod m1, SGGeod m2, double r1, double r2); /**Length of inner tangent between two circles.*/ static double innerTangentsLength( SGGeod m1, SGGeod m2, double r1, double r2); + /**Angles of outer tangent between two circles normalized to 0-360*/ + static std::array outerTangentsAngle( SGGeod m1, SGGeod m2, double r1, double r2); + /**Length of outer tangent between two circles.*/ + static double outerTangentsLength( SGGeod m1, SGGeod m2, double r1, double r2); }; diff --git a/src/Traffic/Schedule.cxx b/src/Traffic/Schedule.cxx index e12371c52..800fab62e 100644 --- a/src/Traffic/Schedule.cxx +++ b/src/Traffic/Schedule.cxx @@ -202,10 +202,10 @@ bool FGAISchedule::init() bool FGAISchedule::update(time_t now, const SGVec3d& userCart) { - time_t totalTimeEnroute, - elapsedTimeEnroute, - //remainingTimeEnroute, - deptime = 0; + time_t totalTimeEnroute; + time_t elapsedTimeEnroute; + time_t remainingTimeEnroute; + time_t deptime = 0; if (!valid) { return true; // processing complete @@ -255,7 +255,7 @@ bool FGAISchedule::update(time_t now, const SGVec3d& userCart) // This flight entry is entirely in the past, do we need to // push it forward in time to the next scheduled departure. if (flight->getArrivalTime() < now) { - SG_LOG (SG_AI, SG_BULK, "Traffic Manager: Flight is in the Past"); + SG_LOG (SG_AI, SG_BULK, "Traffic Manager: " << flight->getCallSign() << " is in the Past"); // Don't just update: check whether we need to load a new leg. etc. // This update occurs for distant aircraft, so we can update the current leg // and detach it from the current list of aircraft. @@ -275,7 +275,7 @@ bool FGAISchedule::update(time_t now, const SGVec3d& userCart) totalTimeEnroute = flight->getArrivalTime() - flight->getDepartureTime(); if (flight->getDepartureTime() < now) { elapsedTimeEnroute = now - flight->getDepartureTime(); - //remainingTimeEnroute = totalTimeEnroute - elapsedTimeEnroute; + remainingTimeEnroute = totalTimeEnroute - elapsedTimeEnroute; double x = elapsedTimeEnroute / (double) totalTimeEnroute; // current pos is based on great-circle course between departure/arrival, @@ -285,21 +285,22 @@ bool FGAISchedule::update(time_t now, const SGVec3d& userCart) SGGeodesy::inverse(dep->geod(), arr->geod(), course, az2, distanceM); double coveredDistance = distanceM * x; + //FIXME very crude that doesn't harmonise with Legs SGGeodesy::direct(dep->geod(), course, coveredDistance, position, az2); - SG_LOG (SG_AI, SG_BULK, "Traffic Manager: Flight is in progress, %=" << x); + SG_LOG (SG_AI, SG_BULK, "Traffic Manager: " << flight->getCallSign() << " is in progress " << (x*100) << "%"); speed = ((distanceM - coveredDistance) * SG_METER_TO_NM) / 3600.0; } else { // not departed yet - //remainingTimeEnroute = totalTimeEnroute; + remainingTimeEnroute = totalTimeEnroute; elapsedTimeEnroute = 0; position = dep->geod(); - SG_LOG (SG_AI, SG_BULK, "Traffic Manager: Flight is pending, departure in " + SG_LOG (SG_AI, SG_BULK, "Traffic Manager: " << flight->getCallSign() << " is pending, departure in " << flight->getDepartureTime() - now << " seconds "); } } else { // departure / arrival coincident - //remainingTimeEnroute = totalTimeEnroute = 0.0; + remainingTimeEnroute = totalTimeEnroute = flight->getArrivalTime() - flight->getDepartureTime(); elapsedTimeEnroute = 0; position = dep->geod(); } @@ -320,12 +321,12 @@ bool FGAISchedule::update(time_t now, const SGVec3d& userCart) return true; // out of visual range, for the moment. } - if (!createAIAircraft(flight, speed, deptime)) { + if (!createAIAircraft(flight, speed, deptime, remainingTimeEnroute)) { valid = false; } - return true; // processing complete + return true; // processing complete } bool FGAISchedule::validModelPath(const std::string& modelPath) @@ -353,12 +354,13 @@ SGPath FGAISchedule::resolveModelPath(const std::string& modelPath) return SGPath(); } -bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime) +bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime, time_t remainingTime) { + //FIXME The position must be set here not in update FGAirport* dep = flight->getDepartureAirport(); FGAirport* arr = flight->getArrivalAirport(); string flightPlanName = dep->getId() + "-" + arr->getId() + ".xml"; - SG_LOG(SG_AI, SG_DEBUG, "Traffic manager: Creating AIModel from:" << flightPlanName); + SG_LOG(SG_AI, SG_DEBUG, flight->getCallSign() << "|Traffic manager: Creating AIModel from:" << flightPlanName); aiAircraft = new FGAIAircraft(this); aiAircraft->setPerformance(acType, m_class); //"jet_transport"; @@ -373,8 +375,15 @@ bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots aiAircraft->setBank(0); courseToDest = SGGeodesy::courseDeg(position, arr->geod()); - std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, flightPlanName, courseToDest, deptime, - dep, arr, true, radius, + std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, + flightPlanName, + courseToDest, + deptime, + remainingTime, + dep, + arr, + true, + radius, flight->getCruiseAlt()*100, position.getLatitudeDeg(), position.getLongitudeDeg(), @@ -409,7 +418,6 @@ bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots } } -// Create an initial heading for user controlled aircraft. void FGAISchedule::setHeading() { courseToDest = SGGeodesy::courseDeg((*flights.begin())->getDepartureAirport()->geod(), (*flights.begin())->getArrivalAirport()->geod()); diff --git a/src/Traffic/Schedule.hxx b/src/Traffic/Schedule.hxx index 63a0cc0b0..6cc58ecf4 100644 --- a/src/Traffic/Schedule.hxx +++ b/src/Traffic/Schedule.hxx @@ -23,7 +23,7 @@ * This file contains the definition of the class Schedule. * * A schedule is basically a number of scheduled flights, which can be - * assigned to an AI aircraft. + * assigned to an AI aircraft. **************************************************************************/ #ifndef _FGSCHEDULE_HXX_ @@ -69,13 +69,13 @@ class FGAISchedule bool scheduleFlights(time_t now); int groundTimeFromRadius(); - + /** * Transition this schedule from distant mode to AI mode; * create the AIAircraft (and flight plan) and register with the AIManager */ - bool createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime); - + bool createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime, time_t remainingTime); + // the aiAircraft associated with us SGSharedPtr aiAircraft; public: @@ -85,12 +85,12 @@ class FGAISchedule const std::string& homePort, const std::string& registration, const std::string& flightId, - bool heavy, + bool heavy, const std::string& acType, const std::string& airline, const std::string& m_class, const std::string& flight_type, - double radius, + double radius, double offset); // construct & init FGAISchedule(const FGAISchedule &other); // copy constructor @@ -100,7 +100,7 @@ class FGAISchedule static bool validModelPath(const std::string& model); static SGPath resolveModelPath(const std::string& model); - + bool update(time_t now, const SGVec3d& userCart); bool init(); @@ -130,7 +130,8 @@ class FGAISchedule void setHits (unsigned int count) { hits = count; }; void setScore (); double getScore () { return score; }; - void setHeading (); + /**Create an initial heading for user controlled aircraft.*/ + void setHeading (); void assign (FGScheduledFlight *ref); void clearAllFlights(); void setFlightType (const std::string& val) { flightType = val; }; diff --git a/test_suite/unit_tests/AI/test_VectorMath.cxx b/test_suite/unit_tests/AI/test_VectorMath.cxx index 4841c2e19..713f96b04 100644 --- a/test_suite/unit_tests/AI/test_VectorMath.cxx +++ b/test_suite/unit_tests/AI/test_VectorMath.cxx @@ -67,4 +67,18 @@ void VectorMathTests::testInnerTangent2() auto angles = VectorMath::innerTangentsAngle(m1, m2, r1, r2); CPPUNIT_ASSERT_DOUBLES_EQUAL( 330, angles[0], 0.1); CPPUNIT_ASSERT_DOUBLES_EQUAL( 30, angles[1], 0.1); -} \ No newline at end of file +} + +void VectorMathTests::testOuterTanget() +{ + double r1 = 10; + double r2 = 50; + // when the circles are dist appart the angle will be 45° + double dist = 40; + SGGeod m1 = SGGeod::fromDeg(9,51); + SGGeod m2 = SGGeodesy::direct(m1, 90, dist); + + auto angles = VectorMath::outerTangentsAngle(m1, m2, r1, r2); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 45, angles[0], 0.1); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 135, angles[1], 0.1); +} diff --git a/test_suite/unit_tests/AI/test_VectorMath.hxx b/test_suite/unit_tests/AI/test_VectorMath.hxx index 16582676b..15e84a7e3 100644 --- a/test_suite/unit_tests/AI/test_VectorMath.hxx +++ b/test_suite/unit_tests/AI/test_VectorMath.hxx @@ -35,6 +35,7 @@ class VectorMathTests : public CppUnit::TestFixture CPPUNIT_TEST_SUITE(VectorMathTests); CPPUNIT_TEST(testInnerTanget); CPPUNIT_TEST(testInnerTangent2); + CPPUNIT_TEST(testOuterTanget); CPPUNIT_TEST_SUITE_END(); @@ -49,4 +50,5 @@ public: // The tests. void testInnerTanget(); void testInnerTangent2(); + void testOuterTanget(); }; diff --git a/test_suite/unit_tests/AI/test_traffic.cxx b/test_suite/unit_tests/AI/test_traffic.cxx index 65d88dd5a..50a8a18a0 100644 --- a/test_suite/unit_tests/AI/test_traffic.cxx +++ b/test_suite/unit_tests/AI/test_traffic.cxx @@ -156,7 +156,8 @@ void TrafficTests::testPushback() departureTime = departureTime + 90; std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, - flightPlanName, crs, departureTime, + flightPlanName, crs, + departureTime, departureTime+3000, departureAirport, arrivalAirport, true, radius, cruiseAltFt, // cruise alt position.getLatitudeDeg(), @@ -214,7 +215,8 @@ void TrafficTests::testPushbackCargo() std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, - flightPlanName, crs, departureTime, + flightPlanName, crs, + departureTime, departureTime+3000, egph, egpf, true, radius, cruiseAltFt, // cruise alt position.getLatitudeDeg(), @@ -230,6 +232,253 @@ void TrafficTests::testPushbackCargo() aiAircraft = flyAI(aiAircraft, "flight_cargo_EGPH_EGPF_" + std::to_string(departureTime)); } +void TrafficTests::testPushbackCargoInProgress() +{ + FGAirportRef egph = FGAirport::getByIdent("EGPH"); + + FGAirportRef egpf = FGAirport::getByIdent("EGPF"); + fgSetString("/sim/presets/airport-id", "EGPH"); + + // Time to depart + std::string dep = getTimeString(-100); + // Time to arrive + std::string arr = getTimeString(190); + + + FGAISchedule* schedule = new FGAISchedule( + "B737", "KLM", "EGPH", "G-BLA", "ID", false, "B737", "KLM", "N", "cargo", 24, 8); + FGScheduledFlight* flight = new FGScheduledFlight("testPushbackCargo", "", "EGPH", "EGPF", 24, dep, arr, "WEEK", "HBR_BN_2"); + schedule->assign(flight); + + SGSharedPtr aiAircraft = new FGAIAircraft{schedule}; + + const SGGeod position = SGGeodesy::direct(egph->geod(), 270, 50000); + const double crs = SGGeodesy::courseDeg(position, egpf->geod()); // direct course + ParkingAssignment parking = egph->getDynamics()->getParkingByName("north-cargo208"); + + FGTestApi::setPositionAndStabilise(egph->getDynamics()->getParkingByName("ga206").parking()->geod()); + + aiAircraft->setPerformance("jet_transport", ""); + aiAircraft->setCompany("KLM"); + aiAircraft->setAcType("B737"); + aiAircraft->setSpeed(0); + aiAircraft->setBank(0); + aiAircraft->setHeading(crs); + + const string flightPlanName = egph->getId() + "-" + egpf->getId() + ".xml"; + + const int radius = 16.0; + const int cruiseAltFt = 32000; + const int cruiseSpeedKnots = 80; + + time_t departureTime = globals->get_time_params()->get_cur_time(); + departureTime = departureTime - 6000; + + + std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, + flightPlanName, crs, + departureTime, 100, + egph, egpf, true, radius, + cruiseAltFt, // cruise alt + position.getLatitudeDeg(), + position.getLongitudeDeg(), + cruiseSpeedKnots, "cargo", + aiAircraft->getAcType(), + aiAircraft->getCompany())); + + CPPUNIT_ASSERT_EQUAL(fp->isValidPlan(), true); + aiAircraft->FGAIBase::setFlightPlan(std::move(fp)); + globals->get_subsystem()->attach(aiAircraft); + + aiAircraft = flyAI(aiAircraft, "flight_cargo_in_progress_EGPH_EGPF_" + std::to_string(departureTime)); +} + +void TrafficTests::testPushbackCargoInProgressDownWind() +{ + FGAirportRef egph = FGAirport::getByIdent("EGPH"); + + FGAirportRef egpf = FGAirport::getByIdent("EGPF"); + fgSetString("/sim/presets/airport-id", "EGPH"); + + // Time to depart + std::string dep = getTimeString(-100); + // Time to arrive + std::string arr = getTimeString(190); + + + FGAISchedule* schedule = new FGAISchedule( + "B737", "KLM", "EGPH", "G-BLA", "ID", false, "B737", "KLM", "N", "cargo", 24, 8); + FGScheduledFlight* flight = new FGScheduledFlight("testPushbackCargoInProgressDownWind", "", "EGPH", "EGPF", 24, dep, arr, "WEEK", "HBR_BN_2"); + schedule->assign(flight); + + SGSharedPtr aiAircraft = new FGAIAircraft{schedule}; + + const SGGeod position = SGGeodesy::direct(egph->geod(), 0, 50000); + const double crs = SGGeodesy::courseDeg(position, egpf->geod()); // direct course + ParkingAssignment parking = egph->getDynamics()->getParkingByName("north-cargo208"); + + FGTestApi::setPositionAndStabilise(egph->getDynamics()->getParkingByName("ga206").parking()->geod()); + + aiAircraft->setPerformance("jet_transport", ""); + aiAircraft->setCompany("KLM"); + aiAircraft->setAcType("B737"); + aiAircraft->setSpeed(0); + aiAircraft->setBank(0); + aiAircraft->setHeading(crs); + + const string flightPlanName = egph->getId() + "-" + egpf->getId() + ".xml"; + + const int radius = 16.0; + const int cruiseAltFt = 32000; + const int cruiseSpeedKnots = 80; + + time_t departureTime = globals->get_time_params()->get_cur_time(); + departureTime = departureTime - 6000; + + + std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, + flightPlanName, crs, + departureTime, 100, + egph, egpf, true, radius, + cruiseAltFt, // cruise alt + position.getLatitudeDeg(), + position.getLongitudeDeg(), + cruiseSpeedKnots, "cargo", + aiAircraft->getAcType(), + aiAircraft->getCompany())); + + CPPUNIT_ASSERT_EQUAL(fp->isValidPlan(), true); + aiAircraft->FGAIBase::setFlightPlan(std::move(fp)); + globals->get_subsystem()->attach(aiAircraft); + + aiAircraft = flyAI(aiAircraft, "flight_cargo_in_progress_downwind_EGPH_EGPF_" + std::to_string(departureTime)); +} + +void TrafficTests::testPushbackCargoInProgressNotBeyond() +{ + FGAirportRef egph = FGAirport::getByIdent("EGPH"); + + FGAirportRef egpf = FGAirport::getByIdent("EGPF"); + fgSetString("/sim/presets/airport-id", "EGPH"); + + // Time to depart + std::string dep = getTimeString(-100); + // Time to arrive + std::string arr = getTimeString(190); + + + FGAISchedule* schedule = new FGAISchedule( + "B737", "KLM", "EGPH", "G-BLA", "ID", false, "B737", "KLM", "N", "cargo", 24, 8); + FGScheduledFlight* flight = new FGScheduledFlight("testPushbackCargo", "", "EGPH", "EGPF", 24, dep, arr, "WEEK", "HBR_BN_2"); + schedule->assign(flight); + + SGSharedPtr aiAircraft = new FGAIAircraft{schedule}; + + // Position west of runway + const SGGeod position = SGGeodesy::direct(egpf->geod(), 270, 5000); + const double crs = SGGeodesy::courseDeg(position, egpf->geod()); // direct course + ParkingAssignment parking = egph->getDynamics()->getParkingByName("north-cargo208"); + + FGTestApi::setPositionAndStabilise(egph->getDynamics()->getParkingByName("ga206").parking()->geod()); + + aiAircraft->setPerformance("jet_transport", ""); + aiAircraft->setCompany("KLM"); + aiAircraft->setAcType("B737"); + aiAircraft->setSpeed(0); + aiAircraft->setBank(0); + aiAircraft->setHeading(crs); + + const string flightPlanName = egph->getId() + "-" + egpf->getId() + ".xml"; + + const int radius = 16.0; + const int cruiseAltFt = 32000; + const int cruiseSpeedKnots = 80; + + time_t departureTime = globals->get_time_params()->get_cur_time(); + departureTime = departureTime - 6000; + + + std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, + flightPlanName, crs, + departureTime, 100, + egph, egpf, true, radius, + cruiseAltFt, // cruise alt + position.getLatitudeDeg(), + position.getLongitudeDeg(), + cruiseSpeedKnots, "cargo", + aiAircraft->getAcType(), + aiAircraft->getCompany())); + + CPPUNIT_ASSERT_EQUAL(fp->isValidPlan(), true); + aiAircraft->FGAIBase::setFlightPlan(std::move(fp)); + globals->get_subsystem()->attach(aiAircraft); + + aiAircraft = flyAI(aiAircraft, "flight_cargo_in_progress_not_beyond_EGPH_EGPF_" + std::to_string(departureTime)); +} + +void TrafficTests::testPushbackCargoInProgressBeyond() +{ + FGAirportRef egph = FGAirport::getByIdent("EGPH"); + + FGAirportRef egpf = FGAirport::getByIdent("EGPF"); + fgSetString("/sim/presets/airport-id", "EGPH"); + + // Time to depart + std::string dep = getTimeString(-100); + // Time to arrive + std::string arr = getTimeString(190); + + + FGAISchedule* schedule = new FGAISchedule( + "B737", "KLM", "EGPH", "G-BLA", "ID", false, "B737", "KLM", "N", "cargo", 24, 8); + FGScheduledFlight* flight = new FGScheduledFlight("testPushbackCargo", "", "EGPH", "EGPF", 24, dep, arr, "WEEK", "HBR_BN_2"); + schedule->assign(flight); + + SGSharedPtr aiAircraft = new FGAIAircraft{schedule}; + + // Position east of runway pointing away from runway + const SGGeod position = SGGeodesy::direct(egpf->geod(), 90, 5000); + + const double crs = SGMiscd::normalizePeriodic(0, 360, SGGeodesy::courseDeg(position, egpf->geod())); + ParkingAssignment parking = egph->getDynamics()->getParkingByName("north-cargo208"); + + FGTestApi::setPositionAndStabilise(egph->getDynamics()->getParkingByName("ga206").parking()->geod()); + + aiAircraft->setPerformance("jet_transport", ""); + aiAircraft->setCompany("KLM"); + aiAircraft->setAcType("B737"); + aiAircraft->setSpeed(0); + aiAircraft->setBank(0); + aiAircraft->setHeading(crs); + + const string flightPlanName = egph->getId() + "-" + egpf->getId() + ".xml"; + + const int radius = 16.0; + const int cruiseAltFt = 32000; + const int cruiseSpeedKnots = 80; + + time_t departureTime = globals->get_time_params()->get_cur_time(); + departureTime = departureTime - 6000; + + + std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, + flightPlanName, crs, + departureTime, 100, + egph, egpf, true, radius, + cruiseAltFt, // cruise alt + position.getLatitudeDeg(), + position.getLongitudeDeg(), + cruiseSpeedKnots, "cargo", + aiAircraft->getAcType(), + aiAircraft->getCompany())); + + CPPUNIT_ASSERT_EQUAL(fp->isValidPlan(), true); + aiAircraft->FGAIBase::setFlightPlan(std::move(fp)); + globals->get_subsystem()->attach(aiAircraft); + + aiAircraft = flyAI(aiAircraft, "flight_cargo_in_progress_beyond_EGPH_EGPF_" + std::to_string(departureTime)); +} + void TrafficTests::testChangeRunway() { FGAirportRef departureAirport = FGAirport::getByIdent("EGPH"); @@ -274,7 +523,8 @@ void TrafficTests::testChangeRunway() std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, - flightPlanName, crs, departureTime, + flightPlanName, crs, + departureTime, departureTime+3000, departureAirport, arrivalAirport, true, radius, cruiseAltFt, // cruise alt position.getLatitudeDeg(), @@ -287,7 +537,7 @@ void TrafficTests::testChangeRunway() aiAircraft->FGAIBase::setFlightPlan(std::move(fp)); globals->get_subsystem()->attach(aiAircraft); - aiAircraft = flyAI(aiAircraft, "flight_runway_EGPH_EGPF_" + std::to_string(departureTime)); + aiAircraft = flyAI(aiAircraft, "flight_change_runway_EGPH_EGPF_" + std::to_string(departureTime)); } @@ -332,7 +582,8 @@ void TrafficTests::testPushforward() std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, - flightPlanName, crs, departureTime, + flightPlanName, crs, + departureTime, departureTime+3000, departureAirport, arrivalAirport, true, radius, cruiseAltFt, // cruise alt position.getLatitudeDeg(), @@ -389,7 +640,8 @@ void TrafficTests::testPushforwardSpeedy() std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, - flightPlanName, crs, departureTime, + flightPlanName, crs, + departureTime, departureTime+3000, departureAirport, arrivalAirport, true, radius, cruiseAltFt, // cruise alt position.getLatitudeDeg(), @@ -447,7 +699,8 @@ void TrafficTests::testPushforwardParkYBBN() std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, - flightPlanName, crs, departureTime, + flightPlanName, crs, + departureTime, departureTime+3000, departureAirport, arrivalAirport, true, radius, cruiseAltFt, // cruise alt position.getLatitudeDeg(), @@ -528,7 +781,7 @@ void TrafficTests::testPushforwardParkYBBNRepeatGa() std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, - flightPlanName, crs, departureTime, + flightPlanName, crs, departureTime, departureTime+3000, departureAirport, arrivalAirport, true, radius, cruiseAltFt, // cruise alt position.getLatitudeDeg(), @@ -604,7 +857,8 @@ void TrafficTests::testPushforwardParkYBBNRepeatGaDelayed() std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, - flightPlanName, crs, departureTime, + flightPlanName, crs, + departureTime, departureTime+3000, departureAirport, arrivalAirport, true, radius, cruiseAltFt, // cruise alt position.getLatitudeDeg(), @@ -700,7 +954,8 @@ void TrafficTests::testPushforwardParkYBBNRepeatGate() std::unique_ptr fp(new FGAIFlightPlan(aiAircraft, - flightPlanName, crs, departureTime, + flightPlanName, crs, + departureTime, departureTime+3000, departureAirport, arrivalAirport, true, radius, cruiseAltFt, // cruise alt position.getLatitudeDeg(), diff --git a/test_suite/unit_tests/AI/test_traffic.hxx b/test_suite/unit_tests/AI/test_traffic.hxx index 16aa9cbeb..60302eab6 100644 --- a/test_suite/unit_tests/AI/test_traffic.hxx +++ b/test_suite/unit_tests/AI/test_traffic.hxx @@ -37,12 +37,16 @@ class TrafficTests : public CppUnit::TestFixture CPPUNIT_TEST_SUITE(TrafficTests); CPPUNIT_TEST(testPushback); CPPUNIT_TEST(testPushbackCargo); + CPPUNIT_TEST(testPushbackCargoInProgress); + CPPUNIT_TEST(testPushbackCargoInProgressDownWind); + CPPUNIT_TEST(testPushbackCargoInProgressNotBeyond); + CPPUNIT_TEST(testPushbackCargoInProgressBeyond); CPPUNIT_TEST(testChangeRunway); CPPUNIT_TEST(testPushforward); CPPUNIT_TEST(testPushforwardSpeedy); CPPUNIT_TEST(testPushforwardParkYBBN); CPPUNIT_TEST(testPushforwardParkYBBNRepeatGa); - CPPUNIT_TEST(testPushforwardParkYBBNRepeatGaDelayed); + CPPUNIT_TEST(testPushforwardParkYBBNRepeatGaDelayed); CPPUNIT_TEST(testPushforwardParkYBBNRepeatGate); CPPUNIT_TEST_SUITE_END(); public: @@ -55,6 +59,10 @@ public: // Pushback Tests void testPushback(); void testPushbackCargo(); + void testPushbackCargoInProgress(); + void testPushbackCargoInProgressDownWind(); + void testPushbackCargoInProgressNotBeyond(); + void testPushbackCargoInProgressBeyond(); void testChangeRunway(); //GA Tests with forward push void testPushforward();