AI
* Parking on parking with right heading * Leg 6 Descent improved (teardrop like entry) * Inner/Outer tangents in VectorMath TODO Move to SG
This commit is contained in:
parent
3ee54cbd72
commit
e85e5d2e5b
13 changed files with 611 additions and 266 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,21 +805,91 @@ 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 && 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);
|
||||
|
||||
if (fabs(headingDiffRunway)>=30) {
|
||||
// Entering not "straight" into runway so we do a s-curve
|
||||
int rightAngle = initialHeadingDiff<0?90:-90;
|
||||
int rightAngle = headingDiffRunway>0?90:-90;
|
||||
int firstTurnIncrement = headingDiffRunway>0?2:-2;
|
||||
|
||||
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");
|
||||
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);
|
||||
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 {
|
||||
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) {
|
||||
|
@ -832,15 +910,99 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt,
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -60,3 +60,21 @@ double VectorMath::innerTangentsLength( SGGeod m1, SGGeod m2, double r1, double
|
|||
|
||||
return SGGeodesy::distanceM(p1, p2);
|
||||
}
|
||||
|
||||
std::array<double, 2> VectorMath::outerTangentsAngle( SGGeod m1, SGGeod m2, double r1, double r2) {
|
||||
std::array<double, 2> 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;
|
||||
}
|
||||
|
|
|
@ -27,4 +27,8 @@ class VectorMath {
|
|||
static std::array<double, 2> 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<double, 2> 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);
|
||||
};
|
||||
|
|
|
@ -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,7 +321,7 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -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<FGAIFlightPlan> fp(new FGAIFlightPlan(aiAircraft, flightPlanName, courseToDest, deptime,
|
||||
dep, arr, true, radius,
|
||||
std::unique_ptr<FGAIFlightPlan> 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());
|
||||
|
|
|
@ -74,7 +74,7 @@ class FGAISchedule
|
|||
* 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<FGAIAircraft> aiAircraft;
|
||||
|
@ -130,6 +130,7 @@ class FGAISchedule
|
|||
void setHits (unsigned int count) { hits = count; };
|
||||
void setScore ();
|
||||
double getScore () { return score; };
|
||||
/**Create an initial heading for user controlled aircraft.*/
|
||||
void setHeading ();
|
||||
void assign (FGScheduledFlight *ref);
|
||||
void clearAllFlights();
|
||||
|
|
|
@ -68,3 +68,17 @@ void VectorMathTests::testInnerTangent2()
|
|||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 330, angles[0], 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 30, angles[1], 0.1);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -156,7 +156,8 @@ void TrafficTests::testPushback()
|
|||
departureTime = departureTime + 90;
|
||||
|
||||
std::unique_ptr<FGAIFlightPlan> 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<FGAIFlightPlan> 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<FGAIAircraft> 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<FGAIFlightPlan> 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<FGAIManager>()->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<FGAIAircraft> 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<FGAIFlightPlan> 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<FGAIManager>()->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<FGAIAircraft> 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<FGAIFlightPlan> 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<FGAIManager>()->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<FGAIAircraft> 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<FGAIFlightPlan> 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<FGAIManager>()->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<FGAIFlightPlan> 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<FGAIManager>()->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<FGAIFlightPlan> 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<FGAIFlightPlan> 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<FGAIFlightPlan> 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<FGAIFlightPlan> 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<FGAIFlightPlan> 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<FGAIFlightPlan> fp(new FGAIFlightPlan(aiAircraft,
|
||||
flightPlanName, crs, departureTime,
|
||||
flightPlanName, crs,
|
||||
departureTime, departureTime+3000,
|
||||
departureAirport, arrivalAirport, true, radius,
|
||||
cruiseAltFt, // cruise alt
|
||||
position.getLatitudeDeg(),
|
||||
|
|
|
@ -37,6 +37,10 @@ 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);
|
||||
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue