Initial stab at making the AI plane take the user into account when flying a circuit. The AI plane will now delay it's turns to the various legs if the user has flown a wider pattern that it normally would in front of it. However, this is very much still work in progress - when the pattern gets extended the AI plane lands short of the runway, and it doesn't alter its speed around the circuit at all, only the turn positions. Still, its a start...
This commit is contained in:
parent
d975fc129c
commit
a714849cbd
3 changed files with 131 additions and 19 deletions
|
@ -605,6 +605,8 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
||||||
double wind_from = wind_from_hdg->getDoubleValue();
|
double wind_from = wind_from_hdg->getDoubleValue();
|
||||||
double wind_speed = wind_speed_knots->getDoubleValue();
|
double wind_speed = wind_speed_knots->getDoubleValue();
|
||||||
|
|
||||||
|
double dveldt;
|
||||||
|
|
||||||
switch(leg) {
|
switch(leg) {
|
||||||
case TAKEOFF_ROLL:
|
case TAKEOFF_ROLL:
|
||||||
//inAir = false;
|
//inAir = false;
|
||||||
|
@ -627,10 +629,27 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
||||||
break;
|
break;
|
||||||
case CLIMBOUT:
|
case CLIMBOUT:
|
||||||
track = rwy.hdg;
|
track = rwy.hdg;
|
||||||
|
// Turn to crosswind if above 600ft AND if other traffic allows
|
||||||
|
// (decided in FGTower and accessed through GetCrosswindConstraint(...)).
|
||||||
if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 600) {
|
if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 600) {
|
||||||
|
double cc = 0.0;
|
||||||
|
if(tower->GetCrosswindConstraint(cc)) {
|
||||||
|
if(orthopos.y() > cc) {
|
||||||
cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n';
|
cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n';
|
||||||
leg = TURN1;
|
leg = TURN1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n';
|
||||||
|
leg = TURN1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Need to check for levelling off in case we can't turn crosswind as soon
|
||||||
|
// as we would like due to other traffic.
|
||||||
|
if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) {
|
||||||
|
slope = 0.0;
|
||||||
|
pitch = 0.0;
|
||||||
|
IAS = 80.0; // FIXME - use smooth transistion to new speed and attitude.
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TURN1:
|
case TURN1:
|
||||||
track += (360.0 / turn_time) * dt * patternDirection;
|
track += (360.0 / turn_time) * dt * patternDirection;
|
||||||
|
@ -647,10 +666,19 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
||||||
pitch = 0.0;
|
pitch = 0.0;
|
||||||
IAS = 80.0; // FIXME - use smooth transistion to new speed
|
IAS = 80.0; // FIXME - use smooth transistion to new speed
|
||||||
}
|
}
|
||||||
// turn 1000m out for now
|
// turn 1000m out for now, taking other traffic into accout
|
||||||
if(fabs(orthopos.x()) > 980) {
|
if(fabs(orthopos.x()) > 980) {
|
||||||
|
double dd = 0.0;
|
||||||
|
if(tower->GetDownwindConstraint(dd)) {
|
||||||
|
if(fabs(orthopos.x()) > fabs(dd)) {
|
||||||
|
cout << "Turning to downwind, distance from centerline = " << fabs(orthopos.x()) << '\n';
|
||||||
leg = TURN2;
|
leg = TURN2;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
cout << "Turning to downwind, distance from centerline = " << fabs(orthopos.x()) << '\n';
|
||||||
|
leg = TURN2;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TURN2:
|
case TURN2:
|
||||||
track += (360.0 / turn_time) * dt * patternDirection;
|
track += (360.0 / turn_time) * dt * patternDirection;
|
||||||
|
@ -681,16 +709,27 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
||||||
transmitted = true;
|
transmitted = true;
|
||||||
}
|
}
|
||||||
if(orthopos.y() < -480) {
|
if(orthopos.y() < -480) {
|
||||||
|
// FIXME - TODO - take tower baseleg constraint ie. other traffic, into account when calculating start of descent
|
||||||
slope = -4.0; // FIXME - calculate to descent at 500fpm and hit the threshold (taking wind into account as well!!)
|
slope = -4.0; // FIXME - calculate to descent at 500fpm and hit the threshold (taking wind into account as well!!)
|
||||||
pitch = -3.0;
|
pitch = -3.0;
|
||||||
IAS = 85.0;
|
IAS = 85.0;
|
||||||
}
|
}
|
||||||
if(orthopos.y() < -980) {
|
if(orthopos.y() < -980) {
|
||||||
//roll = -20;
|
double bb = 0.0;
|
||||||
|
if(tower->GetDownwindConstraint(bb)) {
|
||||||
|
if(fabs(orthopos.y()) > fabs(bb)) {
|
||||||
|
cout << "Turning to base, distance from threshold = " << fabs(orthopos.y()) << '\n';
|
||||||
leg = TURN3;
|
leg = TURN3;
|
||||||
transmitted = false;
|
transmitted = false;
|
||||||
IAS = 80.0;
|
IAS = 80.0;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
cout << "Turning to base, distance from threshold = " << fabs(orthopos.y()) << '\n';
|
||||||
|
leg = TURN3;
|
||||||
|
transmitted = false;
|
||||||
|
IAS = 80.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TURN3:
|
case TURN3:
|
||||||
track += (360.0 / turn_time) * dt * patternDirection;
|
track += (360.0 / turn_time) * dt * patternDirection;
|
||||||
|
@ -759,7 +798,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
||||||
pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
|
pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
|
||||||
}
|
}
|
||||||
track = rwy.hdg;
|
track = rwy.hdg;
|
||||||
double dveldt = -5.0;
|
dveldt = -5.0;
|
||||||
vel += dveldt * dt;
|
vel += dveldt * dt;
|
||||||
// FIXME - differentiate between touch and go and full stops
|
// FIXME - differentiate between touch and go and full stops
|
||||||
if(vel <= 15.0) {
|
if(vel <= 15.0) {
|
||||||
|
@ -775,6 +814,8 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case LEG_UNKNOWN:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inAir) {
|
if(inAir) {
|
||||||
|
|
|
@ -44,6 +44,7 @@ longFinalAcknowledged(false),
|
||||||
finalReported(false),
|
finalReported(false),
|
||||||
finalAcknowledged(false),
|
finalAcknowledged(false),
|
||||||
opType(TTT_UNKNOWN),
|
opType(TTT_UNKNOWN),
|
||||||
|
leg(LEG_UNKNOWN),
|
||||||
isUser(false)
|
isUser(false)
|
||||||
{
|
{
|
||||||
plane.callsign = "UNKNOWN";
|
plane.callsign = "UNKNOWN";
|
||||||
|
@ -59,6 +60,7 @@ longFinalAcknowledged(false),
|
||||||
finalReported(false),
|
finalReported(false),
|
||||||
finalAcknowledged(false),
|
finalAcknowledged(false),
|
||||||
opType(TTT_UNKNOWN),
|
opType(TTT_UNKNOWN),
|
||||||
|
leg(LEG_UNKNOWN),
|
||||||
isUser(false)
|
isUser(false)
|
||||||
{
|
{
|
||||||
plane = p;
|
plane = p;
|
||||||
|
@ -74,6 +76,7 @@ longFinalAcknowledged(false),
|
||||||
finalReported(false),
|
finalReported(false),
|
||||||
finalAcknowledged(false),
|
finalAcknowledged(false),
|
||||||
opType(TTT_UNKNOWN),
|
opType(TTT_UNKNOWN),
|
||||||
|
leg(LEG_UNKNOWN),
|
||||||
isUser(false)
|
isUser(false)
|
||||||
{
|
{
|
||||||
plane.callsign = "UNKNOWN";
|
plane.callsign = "UNKNOWN";
|
||||||
|
@ -90,6 +93,7 @@ longFinalAcknowledged(false),
|
||||||
finalReported(false),
|
finalReported(false),
|
||||||
finalAcknowledged(false),
|
finalAcknowledged(false),
|
||||||
opType(TTT_UNKNOWN),
|
opType(TTT_UNKNOWN),
|
||||||
|
leg(LEG_UNKNOWN),
|
||||||
isUser(false)
|
isUser(false)
|
||||||
{
|
{
|
||||||
plane = p;
|
plane = p;
|
||||||
|
@ -230,7 +234,6 @@ void FGTower::Update(double dt) {
|
||||||
// Do one plane from the hold list
|
// Do one plane from the hold list
|
||||||
if(ii == 4) {
|
if(ii == 4) {
|
||||||
if(holdList.size()) {
|
if(holdList.size()) {
|
||||||
//cout << "A" << endl;
|
|
||||||
//cout << "*holdListItr = " << *holdListItr << endl;
|
//cout << "*holdListItr = " << *holdListItr << endl;
|
||||||
if(holdListItr == holdList.end()) {
|
if(holdListItr == holdList.end()) {
|
||||||
holdListItr = holdList.begin();
|
holdListItr = holdList.begin();
|
||||||
|
@ -240,14 +243,10 @@ void FGTower::Update(double dt) {
|
||||||
TowerPlaneRec* t = *holdListItr;
|
TowerPlaneRec* t = *holdListItr;
|
||||||
//cout << "t = " << t << endl;
|
//cout << "t = " << t << endl;
|
||||||
if(t->holdShortReported) {
|
if(t->holdShortReported) {
|
||||||
//cout << "B" << endl;
|
|
||||||
double responseTime = 10.0; // seconds - this should get more sophisticated at some point
|
double responseTime = 10.0; // seconds - this should get more sophisticated at some point
|
||||||
if(t->clearanceCounter > responseTime) {
|
if(t->clearanceCounter > responseTime) {
|
||||||
//cout << "C" << endl;
|
|
||||||
if(t->nextOnRwy) {
|
if(t->nextOnRwy) {
|
||||||
//cout << "D" << endl;
|
|
||||||
if(rwyOccupied) {
|
if(rwyOccupied) {
|
||||||
//cout << "E" << endl;
|
|
||||||
// Do nothing for now - consider acknowloging hold short eventually
|
// Do nothing for now - consider acknowloging hold short eventually
|
||||||
} else {
|
} else {
|
||||||
// Lets Roll !!!!
|
// Lets Roll !!!!
|
||||||
|
@ -337,20 +336,49 @@ void FGTower::Update(double dt) {
|
||||||
if(circuitList.size()) {
|
if(circuitList.size()) {
|
||||||
circuitListItr = circuitList.begin(); // TODO - at the moment we're constraining plane 2 based on plane 1 - this won't work for 3 planes in the circuit!!
|
circuitListItr = circuitList.begin(); // TODO - at the moment we're constraining plane 2 based on plane 1 - this won't work for 3 planes in the circuit!!
|
||||||
TowerPlaneRec* t = *circuitListItr;
|
TowerPlaneRec* t = *circuitListItr;
|
||||||
|
if(t->isUser) {
|
||||||
|
t->pos.setlon(user_lon_node->getDoubleValue());
|
||||||
|
t->pos.setlat(user_lat_node->getDoubleValue());
|
||||||
|
t->pos.setelev(user_elev_node->getDoubleValue());
|
||||||
|
} else {
|
||||||
|
// TODO - set/update the position if it's an AI plane
|
||||||
|
}
|
||||||
Point3D tortho = ortho.ConvertToLocal(t->pos);
|
Point3D tortho = ortho.ConvertToLocal(t->pos);
|
||||||
if(t->isUser) {
|
if(t->isUser) {
|
||||||
// Need to figure out which leg he's on
|
// Need to figure out which leg he's on
|
||||||
|
//cout << "rwy.hdg = " << rwy.hdg << " user hdg = " << user_hdg_node->getDoubleValue();
|
||||||
double ho = GetAngleDiff_deg(user_hdg_node->getDoubleValue(), rwy.hdg);
|
double ho = GetAngleDiff_deg(user_hdg_node->getDoubleValue(), rwy.hdg);
|
||||||
|
//cout << " ho = " << ho << '\n';
|
||||||
// TODO FIXME - get the wind and convert this to track, or otherwise use track somehow!!!
|
// TODO FIXME - get the wind and convert this to track, or otherwise use track somehow!!!
|
||||||
// If it's gusty might need to filter the value, although we are leaving 30 degrees each way leeway!
|
// If it's gusty might need to filter the value, although we are leaving 30 degrees each way leeway!
|
||||||
if(abs(ho) < 30) {
|
if(abs(ho) < 30) {
|
||||||
// could be either takeoff, climbout or landing - check orthopos.y
|
// could be either takeoff, climbout or landing - check orthopos.y
|
||||||
if((tortho.y() < 0) || (t->leg == TURN4) || (t->leg == LANDING_ROLL)) {
|
//cout << "tortho.y = " << tortho.y() << '\n';
|
||||||
t->leg = LANDING_ROLL;
|
if((tortho.y() < 0) || (t->leg == TURN4) || (t->leg == FINAL)) {
|
||||||
//cout << "Landing_roll\n";
|
t->leg = FINAL;
|
||||||
|
//cout << "Final\n";
|
||||||
} else {
|
} else {
|
||||||
t->leg = CLIMBOUT; // TODO - check elev wrt. apt elev to differentiate takeoff roll and climbout
|
t->leg = CLIMBOUT; // TODO - check elev wrt. apt elev to differentiate takeoff roll and climbout
|
||||||
//cout << "Climbout\n";
|
//cout << "Climbout\n";
|
||||||
|
// If it's the user we may be unsure of his/her intentions.
|
||||||
|
// (Hopefully the AI planes won't try confusing the sim!!!)
|
||||||
|
if(t->opType == TTT_UNKNOWN) {
|
||||||
|
if(tortho.y() > 5000) {
|
||||||
|
// 5 km out from threshold - assume it's a departure
|
||||||
|
t->opType = OUTBOUND;
|
||||||
|
// Since we are unknown operation we should be in depList already.
|
||||||
|
circuitList.erase(circuitListItr);
|
||||||
|
circuitListItr = circuitList.begin();
|
||||||
|
}
|
||||||
|
} else if(t->opType == CIRCUIT) {
|
||||||
|
if(tortho.y() > 10000) {
|
||||||
|
// 10 km out - assume the user has abandoned the circuit!!
|
||||||
|
t->opType = OUTBOUND;
|
||||||
|
depList.push_back(t);
|
||||||
|
circuitList.erase(circuitListItr);
|
||||||
|
circuitListItr = circuitList.begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if(abs(ho) < 60) {
|
} else if(abs(ho) < 60) {
|
||||||
// turn1 or turn 4
|
// turn1 or turn 4
|
||||||
|
@ -394,29 +422,35 @@ void FGTower::Update(double dt) {
|
||||||
case FINAL:
|
case FINAL:
|
||||||
// Base leg must be at least as far out as the plane is - actually possibly not necessary for separation, but we'll use that for now.
|
// Base leg must be at least as far out as the plane is - actually possibly not necessary for separation, but we'll use that for now.
|
||||||
base_leg_pos = tortho.y();
|
base_leg_pos = tortho.y();
|
||||||
|
//cout << "base_leg_pos = " << base_leg_pos << '\n';
|
||||||
break;
|
break;
|
||||||
case TURN4:
|
case TURN4:
|
||||||
// Fall through to base
|
// Fall through to base
|
||||||
case BASE:
|
case BASE:
|
||||||
base_leg_pos = tortho.y();
|
base_leg_pos = tortho.y();
|
||||||
|
//cout << "base_leg_pos = " << base_leg_pos << '\n';
|
||||||
break;
|
break;
|
||||||
case TURN3:
|
case TURN3:
|
||||||
// Fall through to downwind
|
// Fall through to downwind
|
||||||
case DOWNWIND:
|
case DOWNWIND:
|
||||||
// Only have the downwind leg pos as turn-to-base constraint if more negative than we already have.
|
// Only have the downwind leg pos as turn-to-base constraint if more negative than we already have.
|
||||||
base_leg_pos = (tortho.y() < base_leg_pos ? tortho.y() : base_leg_pos);
|
base_leg_pos = (tortho.y() < base_leg_pos ? tortho.y() : base_leg_pos);
|
||||||
|
//cout << "base_leg_pos = " << base_leg_pos;
|
||||||
downwind_leg_pos = tortho.x(); // Assume that a following plane can simply be constrained by the immediately in front downwind plane
|
downwind_leg_pos = tortho.x(); // Assume that a following plane can simply be constrained by the immediately in front downwind plane
|
||||||
|
//cout << " downwind_leg_pos = " << downwind_leg_pos << '\n';
|
||||||
break;
|
break;
|
||||||
case TURN2:
|
case TURN2:
|
||||||
// Fall through to crosswind
|
// Fall through to crosswind
|
||||||
case CROSSWIND:
|
case CROSSWIND:
|
||||||
crosswind_leg_pos = tortho.y();
|
crosswind_leg_pos = tortho.y();
|
||||||
|
//cout << "crosswind_leg_pos = " << crosswind_leg_pos << '\n';
|
||||||
break;
|
break;
|
||||||
case TURN1:
|
case TURN1:
|
||||||
// Fall through to climbout
|
// Fall through to climbout
|
||||||
case CLIMBOUT:
|
case CLIMBOUT:
|
||||||
// Only use current by constraint as largest
|
// Only use current by constraint as largest
|
||||||
crosswind_leg_pos = (tortho.y() > crosswind_leg_pos ? tortho.x() : crosswind_leg_pos);
|
crosswind_leg_pos = (tortho.y() > crosswind_leg_pos ? tortho.y() : crosswind_leg_pos);
|
||||||
|
//cout << "crosswind_leg_pos = " << crosswind_leg_pos << '\n';
|
||||||
break;
|
break;
|
||||||
case TAKEOFF_ROLL:
|
case TAKEOFF_ROLL:
|
||||||
break;
|
break;
|
||||||
|
@ -452,6 +486,37 @@ void FGTower::Update(double dt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns true if positions of crosswind/downwind/base leg turns should be constrained by previous traffic
|
||||||
|
// plus the constraint position as a rwy orientated orthopos (meters)
|
||||||
|
bool FGTower::GetCrosswindConstraint(double& cpos) {
|
||||||
|
if(crosswind_leg_pos != 0.0) {
|
||||||
|
cpos = crosswind_leg_pos;
|
||||||
|
return(true);
|
||||||
|
} else {
|
||||||
|
cpos = 0.0;
|
||||||
|
return(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool FGTower::GetDownwindConstraint(double& dpos) {
|
||||||
|
if(downwind_leg_pos != 0.0) {
|
||||||
|
dpos = downwind_leg_pos;
|
||||||
|
return(true);
|
||||||
|
} else {
|
||||||
|
dpos = 0.0;
|
||||||
|
return(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool FGTower::GetBaseConstraint(double& bpos) {
|
||||||
|
if(base_leg_pos != 0.0) {
|
||||||
|
bpos = base_leg_pos;
|
||||||
|
return(true);
|
||||||
|
} else {
|
||||||
|
bpos = 0.0;
|
||||||
|
return(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Figure out which runways are active.
|
// Figure out which runways are active.
|
||||||
// For now we'll just be simple and do one active runway - eventually this will get much more complex
|
// For now we'll just be simple and do one active runway - eventually this will get much more complex
|
||||||
// This is a private function - public interface to the results of this is through GetActiveRunway
|
// This is a private function - public interface to the results of this is through GetActiveRunway
|
||||||
|
|
|
@ -136,6 +136,12 @@ public:
|
||||||
|
|
||||||
inline FGGround* GetGroundPtr() { return ground; }
|
inline FGGround* GetGroundPtr() { return ground; }
|
||||||
|
|
||||||
|
// Returns true if positions of crosswind/downwind/base leg turns should be constrained by previous traffic
|
||||||
|
// plus the constraint position as a rwy orientated orthopos (meters)
|
||||||
|
bool GetCrosswindConstraint(double& cpos);
|
||||||
|
bool GetDownwindConstraint(double& dpos);
|
||||||
|
bool GetBaseConstraint(double& bpos);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FGATCMgr* ATCmgr;
|
FGATCMgr* ATCmgr;
|
||||||
// This is purely for synactic convienience to avoid writing globals->get_ATC_mgr()-> all through the code!
|
// This is purely for synactic convienience to avoid writing globals->get_ATC_mgr()-> all through the code!
|
||||||
|
|
Loading…
Add table
Reference in a new issue