AI plane should go around instead of landing on user if user dawdles on runway now
This commit is contained in:
parent
cd08f83aa5
commit
0adae696aa
4 changed files with 177 additions and 40 deletions
|
@ -85,6 +85,8 @@ FGAILocalTraffic::FGAILocalTraffic() {
|
|||
|
||||
descending = false;
|
||||
targetDescentRate = 0.0;
|
||||
goAround = false;
|
||||
goAroundCalled = false;
|
||||
}
|
||||
|
||||
FGAILocalTraffic::~FGAILocalTraffic() {
|
||||
|
@ -189,6 +191,9 @@ bool FGAILocalTraffic::Init(string ICAO, OperatingState initialState, PatternLeg
|
|||
} else {
|
||||
//cout << "Unable to find airport details in FGAILocalTraffic::Init()\n";
|
||||
}
|
||||
|
||||
// Get the active runway details (and copy them into rwy)
|
||||
GetRwyDetails();
|
||||
|
||||
// Get the airport elevation
|
||||
aptElev = dclGetAirportElev(airportID.c_str()) * SG_FEET_TO_METER;
|
||||
|
@ -233,29 +238,12 @@ bool FGAILocalTraffic::Init(string ICAO, OperatingState initialState, PatternLeg
|
|||
// since we've got the implementation for this case already.
|
||||
// TODO - implement proper generic in_pattern startup.
|
||||
|
||||
// 18/10/03 - adding the ability to start on downwind (mainly to speed testing of the go-around code!!)
|
||||
|
||||
//cout << "Starting in pattern...\n";
|
||||
|
||||
tuned_station = tower;
|
||||
|
||||
// Get the active runway details (and copy them into rwy)
|
||||
GetRwyDetails();
|
||||
|
||||
// Initial position on threshold for now
|
||||
pos.setlat(rwy.threshold_pos.lat());
|
||||
pos.setlon(rwy.threshold_pos.lon());
|
||||
pos.setelev(rwy.threshold_pos.elev());
|
||||
hdg = rwy.hdg;
|
||||
|
||||
// Now we've set the position we can do the ground elev
|
||||
// This might not always be necessary if we implement in-air start
|
||||
elevInitGood = false;
|
||||
inAir = false;
|
||||
DoGroundElev();
|
||||
|
||||
pitch = 0.0;
|
||||
roll = 0.0;
|
||||
leg = TAKEOFF_ROLL;
|
||||
vel = 0.0;
|
||||
slope = 0.0;
|
||||
|
||||
circuitsToFly = 0; // ie just fly this circuit and then stop
|
||||
touchAndGo = false;
|
||||
// FIXME TODO - pattern direction is still hardwired
|
||||
|
@ -264,7 +252,42 @@ bool FGAILocalTraffic::Init(string ICAO, OperatingState initialState, PatternLeg
|
|||
if(rwy.rwyID.size() == 3) {
|
||||
patternDirection = (rwy.rwyID.substr(2,1) == "R" ? 1 : -1);
|
||||
}
|
||||
|
||||
|
||||
if(initialLeg == DOWNWIND) {
|
||||
pos = ortho.ConvertFromLocal(Point3D(1000*patternDirection, 800, 0.0));
|
||||
pos.setelev(rwy.threshold_pos.elev() + 1000 * SG_FEET_TO_METER);
|
||||
hdg = rwy.hdg + 180.0;
|
||||
leg = DOWNWIND;
|
||||
elevInitGood = false;
|
||||
inAir = true;
|
||||
track = rwy.hdg - (180 * patternDirection); //should tend to bring track back into the 0->360 range
|
||||
slope = 0.0;
|
||||
pitch = 0.0;
|
||||
roll = 0.0;
|
||||
IAS = 90.0;
|
||||
descending = false;
|
||||
aip.setVisible(true);
|
||||
tower->RegisterAIPlane(plane, this, CIRCUIT, DOWNWIND);
|
||||
} else {
|
||||
// Default to initial position on threshold for now
|
||||
pos.setlat(rwy.threshold_pos.lat());
|
||||
pos.setlon(rwy.threshold_pos.lon());
|
||||
pos.setelev(rwy.threshold_pos.elev());
|
||||
hdg = rwy.hdg;
|
||||
|
||||
// Now we've set the position we can do the ground elev
|
||||
// This might not always be necessary if we implement in-air start
|
||||
elevInitGood = false;
|
||||
inAir = false;
|
||||
DoGroundElev();
|
||||
|
||||
pitch = 0.0;
|
||||
roll = 0.0;
|
||||
leg = TAKEOFF_ROLL;
|
||||
vel = 0.0;
|
||||
slope = 0.0;
|
||||
}
|
||||
|
||||
operatingState = IN_PATTERN;
|
||||
|
||||
Transform();
|
||||
|
@ -370,15 +393,17 @@ void FGAILocalTraffic::Update(double dt) {
|
|||
switch(operatingState) {
|
||||
case IN_PATTERN:
|
||||
//cout << "In IN_PATTERN\n";
|
||||
if(!inAir) DoGroundElev();
|
||||
if(!elevInitGood) {
|
||||
if(aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
|
||||
pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
|
||||
//cout << "TAKEOFF_ROLL, POS = " << pos.lon() << ", " << pos.lat() << ", " << pos.elev() << '\n';
|
||||
//Transform();
|
||||
aip.setVisible(true);
|
||||
//cout << "Making plane visible!\n";
|
||||
elevInitGood = true;
|
||||
if(!inAir) {
|
||||
DoGroundElev();
|
||||
if(!elevInitGood) {
|
||||
if(aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
|
||||
pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
|
||||
//cout << "TAKEOFF_ROLL, POS = " << pos.lon() << ", " << pos.lat() << ", " << pos.elev() << '\n';
|
||||
//Transform();
|
||||
aip.setVisible(true);
|
||||
//cout << "Making plane visible!\n";
|
||||
elevInitGood = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
FlyTrafficPattern(dt);
|
||||
|
@ -451,7 +476,7 @@ void FGAILocalTraffic::Update(double dt) {
|
|||
if(circuitsToFly) {
|
||||
if((taxiRequestPending) && (taxiRequestCleared)) {
|
||||
//cout << "&" << flush;
|
||||
// Get the active runway details (and copy them into rwy)
|
||||
// Get the active runway details (in case they've changed since init)
|
||||
GetRwyDetails();
|
||||
|
||||
// Get the takeoff node for the active runway, get a path to it and start taxiing
|
||||
|
@ -544,11 +569,11 @@ void FGAILocalTraffic::RegisterTransmission(int code) {
|
|||
clearedToTakeOff = true;
|
||||
SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " cleared to take-off...");
|
||||
break;
|
||||
// case 13: // Go around!
|
||||
// responseCounter = 0;
|
||||
// goAround = true;
|
||||
// SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " told to go-around!!");
|
||||
// break;
|
||||
case 13: // Go around!
|
||||
responseCounter = 0;
|
||||
goAround = true;
|
||||
SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " told to go-around!!");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -612,9 +637,11 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
|||
break;
|
||||
case CLIMBOUT:
|
||||
track = rwy.hdg;
|
||||
// Turn to crosswind if above 600ft AND if other traffic allows
|
||||
// Turn to crosswind if above 700ft AND if other traffic allows
|
||||
// (decided in FGTower and accessed through GetCrosswindConstraint(...)).
|
||||
if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 600) {
|
||||
// According to AIM, traffic should climb to within 300ft of pattern altitude before commencing crosswind turn.
|
||||
// TODO - At hot 'n high airports this may be 500ft AGL though - need to make this a variable.
|
||||
if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 700) {
|
||||
double cc = 0.0;
|
||||
if(tower->GetCrosswindConstraint(cc)) {
|
||||
if(orthopos.y() > cc) {
|
||||
|
@ -622,6 +649,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
|||
leg = TURN1;
|
||||
}
|
||||
} else if(orthopos.y() > 1500.0) { // Added this constraint as a hack to prevent turning too early when going around.
|
||||
// TODO - We should be doing it as a distance from takeoff end, not theshold end though.
|
||||
cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n';
|
||||
leg = TURN1;
|
||||
}
|
||||
|
@ -633,6 +661,14 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
|||
pitch = 0.0;
|
||||
IAS = 80.0; // FIXME - use smooth transistion to new speed and attitude.
|
||||
}
|
||||
if(goAround && !goAroundCalled) {
|
||||
if(responseCounter > 5.5) {
|
||||
pending_transmission = plane.callsign;
|
||||
pending_transmission += " going around";
|
||||
Transmit();
|
||||
goAroundCalled = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TURN1:
|
||||
track += (360.0 / turn_time) * dt * patternDirection;
|
||||
|
@ -642,6 +678,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
|||
}
|
||||
break;
|
||||
case CROSSWIND:
|
||||
goAround = false;
|
||||
LevelWings();
|
||||
track = rwy.hdg + (90.0 * patternDirection);
|
||||
if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) {
|
||||
|
@ -783,6 +820,15 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
|
|||
}
|
||||
break;
|
||||
case FINAL:
|
||||
if(goAround && responseCounter > 2.0) {
|
||||
leg = CLIMBOUT;
|
||||
pitch = 8.0;
|
||||
IAS = best_rate_of_climb_speed;
|
||||
slope = 5.0; // A bit less steep than the initial climbout.
|
||||
inAir = true;
|
||||
goAroundCalled = false;
|
||||
break;
|
||||
}
|
||||
LevelWings();
|
||||
if(!transmitted) {
|
||||
TransmitPatternPositionReport();
|
||||
|
|
|
@ -176,6 +176,8 @@ private:
|
|||
bool clearedToLineUp;
|
||||
bool clearedToTakeOff;
|
||||
bool liningUp; // Set true when the turn onto the runway heading is commenced when taxiing out
|
||||
bool goAround; // Set true if need to go-around
|
||||
bool goAroundCalled; // Set true during go-around only after we have called our go-around on the radio
|
||||
bool contactTower; // we have been told to contact tower
|
||||
bool contactGround; // we have been told to contact ground
|
||||
bool changeFreq; // true when we need to change frequency
|
||||
|
|
|
@ -44,6 +44,7 @@ longFinalReported(false),
|
|||
longFinalAcknowledged(false),
|
||||
finalReported(false),
|
||||
finalAcknowledged(false),
|
||||
instructedToGoAround(false),
|
||||
onRwy(false),
|
||||
nextOnRwy(false),
|
||||
opType(TTT_UNKNOWN),
|
||||
|
@ -64,6 +65,7 @@ longFinalReported(false),
|
|||
longFinalAcknowledged(false),
|
||||
finalReported(false),
|
||||
finalAcknowledged(false),
|
||||
instructedToGoAround(false),
|
||||
onRwy(false),
|
||||
nextOnRwy(false),
|
||||
opType(TTT_UNKNOWN),
|
||||
|
@ -84,6 +86,7 @@ longFinalReported(false),
|
|||
longFinalAcknowledged(false),
|
||||
finalReported(false),
|
||||
finalAcknowledged(false),
|
||||
instructedToGoAround(false),
|
||||
onRwy(false),
|
||||
nextOnRwy(false),
|
||||
opType(TTT_UNKNOWN),
|
||||
|
@ -105,6 +108,7 @@ longFinalReported(false),
|
|||
longFinalAcknowledged(false),
|
||||
finalReported(false),
|
||||
finalAcknowledged(false),
|
||||
instructedToGoAround(false),
|
||||
onRwy(false),
|
||||
nextOnRwy(false),
|
||||
opType(TTT_UNKNOWN),
|
||||
|
@ -273,6 +277,7 @@ void FGTower::Update(double dt) {
|
|||
// cout << " dt = " << dt << " timeSinceLastDeparture = " << timeSinceLastDeparture << '\n';
|
||||
}
|
||||
|
||||
//cout << ident << " respond = " << respond << " responseReqd = " << responseReqd << '\n';
|
||||
if(respond) {
|
||||
if(!responseReqd) SG_LOG(SG_ATC, SG_ALERT, "ERROR - respond is true and responseReqd is false in FGTower::Update(...)");
|
||||
Respond();
|
||||
|
@ -411,6 +416,7 @@ void FGTower::Respond() {
|
|||
}
|
||||
t->holdShortReported = false;
|
||||
} else if(t->finalReported && !(t->finalAcknowledged)) {
|
||||
bool disp = true;
|
||||
string trns = t->plane.callsign;
|
||||
if(t->nextOnRwy && !rwyOccupied) {
|
||||
if(t->landingType == FULL_STOP) {
|
||||
|
@ -420,11 +426,15 @@ void FGTower::Respond() {
|
|||
}
|
||||
// TODO - add winds
|
||||
t->clearedToLand = true;
|
||||
} else if(t->eta < 20) {
|
||||
// Do nothing - we'll be telling it to go around in less than 10 seconds if the
|
||||
// runway doesn't clear so no point in calling "continue approach".
|
||||
disp = false;
|
||||
} else {
|
||||
trns += " continue approach";
|
||||
t->clearedToLand = false;
|
||||
}
|
||||
if(display) {
|
||||
if(display && disp) {
|
||||
globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
|
||||
}
|
||||
t->finalAcknowledged = true;
|
||||
|
@ -714,6 +724,7 @@ void FGTower::CheckCircuitList(double dt) {
|
|||
case CROSSWIND:
|
||||
crosswind_leg_pos = tortho.y();
|
||||
//cout << "crosswind_leg_pos = " << crosswind_leg_pos << '\n';
|
||||
t->instructedToGoAround = false;
|
||||
break;
|
||||
case TURN1:
|
||||
// Fall through to climbout
|
||||
|
@ -735,6 +746,22 @@ void FGTower::CheckCircuitList(double dt) {
|
|||
|
||||
if(t->leg == FINAL) {
|
||||
if(t->landingType == FULL_STOP) t->opType = INBOUND;
|
||||
if(t->eta < 12 && rwyList.size() && !(t->instructedToGoAround)) {
|
||||
// TODO - need to make this more sophisticated
|
||||
// eg. is the plane accelerating down the runway taking off [OK],
|
||||
// or stationary near the start [V. BAD!!].
|
||||
// For now this should stop the AI plane landing on top of the user.
|
||||
string trns = t->plane.callsign;
|
||||
trns += " GO AROUND TRAFFIC ON RUNWAY I REPEAT GO AROUND";
|
||||
if(display) {
|
||||
globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
|
||||
}
|
||||
t->instructedToGoAround = true;
|
||||
if(t->planePtr) {
|
||||
cout << "Registering Go-around transmission with AI plane\n";
|
||||
t->planePtr->RegisterTransmission(13);
|
||||
}
|
||||
}
|
||||
} else if(t->leg == LANDING_ROLL) {
|
||||
rwyList.push_front(t);
|
||||
// TODO - if(!clearedToLand) shout something!!
|
||||
|
@ -1053,6 +1080,37 @@ bool FGTower::AddToTrafficList(TowerPlaneRec* t, bool holding) {
|
|||
return(holding ? firstTime : conflict);
|
||||
}
|
||||
|
||||
// Add a tower plane rec with ETA to the circuit list in the correct position ETA-wise
|
||||
// Returns true if this might cause a separation conflict (based on ETA) with other traffic, false otherwise.
|
||||
bool FGTower::AddToCircuitList(TowerPlaneRec* t) {
|
||||
//cout << "ADD: " << circuitList.size();
|
||||
//cout << "AddToCircuitList called, currently size = " << circuitList.size() << endl;
|
||||
double separation_time = 60.0; // seconds - this is currently a guess for light plane separation, and includes a few seconds for a holding plane to taxi onto the rwy.
|
||||
bool conflict = false;
|
||||
tower_plane_rec_list_iterator twrItr;
|
||||
for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) {
|
||||
TowerPlaneRec* tpr = *twrItr;
|
||||
|
||||
if(t->eta < tpr->eta) {
|
||||
// Ugg - this one's tricky.
|
||||
// It depends on what the two planes are doing and whether there's a conflict what we do.
|
||||
if(tpr->eta - t->eta > separation_time) { // No probs, plane 2 can squeeze in before plane 1 with no apparent conflict
|
||||
circuitList.insert(twrItr, t);
|
||||
} else { // Ooops - this ones tricky - we have a potential conflict!
|
||||
conflict = true;
|
||||
// HACK - just add anyway for now and flag conflict.
|
||||
circuitList.insert(twrItr, t);
|
||||
}
|
||||
//cout << "\tC\t" << circuitList.size() << '\n';
|
||||
return(conflict);
|
||||
}
|
||||
}
|
||||
// If we get here we must be at the end of the list, or maybe the list is empty.
|
||||
circuitList.push_back(t); // TODO - check the separation with the preceding plane for the conflict flag.
|
||||
//cout << "\tE\t" << circuitList.size() << endl;
|
||||
return(conflict);
|
||||
}
|
||||
|
||||
|
||||
// Calculate the eta of a plane to the threshold.
|
||||
// For ground traffic this is the fastest they can get there.
|
||||
|
@ -1267,8 +1325,10 @@ void FGTower::ContactAtHoldShort(PlaneRec plane, FGAIPlane* requestee, tower_tra
|
|||
t->clearedToLineUp = false;
|
||||
t->clearedToTakeOff = false;
|
||||
t->opType = operation;
|
||||
t->pos = requestee->GetPos();
|
||||
|
||||
//cout << "Hold Short reported by " << plane.callsign << '\n';
|
||||
SG_LOG(SG_ATC, SG_BULK, "Hold Short reported by " << plane.callsign);
|
||||
|
||||
/*
|
||||
bool next = AddToTrafficList(t, true);
|
||||
|
@ -1288,6 +1348,28 @@ void FGTower::ContactAtHoldShort(PlaneRec plane, FGAIPlane* requestee, tower_tra
|
|||
responseReqd = true;
|
||||
}
|
||||
|
||||
// Register the presence of an AI plane at a point where contact would already have been made in real life
|
||||
// CAUTION - currently it is assumed that this plane's callsign is unique - it is up to AIMgr to generate unique callsigns.
|
||||
void FGTower::RegisterAIPlane(PlaneRec plane, FGAIPlane* ai, tower_traffic_type op, PatternLeg lg) {
|
||||
// At the moment this is only going to be tested with inserting an AI plane on downwind
|
||||
TowerPlaneRec* t = new TowerPlaneRec;
|
||||
t->plane = plane;
|
||||
t->planePtr = ai;
|
||||
t->opType = op;
|
||||
t->leg = lg;
|
||||
t->pos = ai->GetPos();
|
||||
|
||||
CalcETA(t);
|
||||
|
||||
if(op == CIRCUIT && lg != LEG_UNKNOWN) {
|
||||
AddToCircuitList(t);
|
||||
} else {
|
||||
// FLAG A WARNING
|
||||
}
|
||||
|
||||
doThresholdUseOrder();
|
||||
}
|
||||
|
||||
void FGTower::RequestLandingClearance(string ID) {
|
||||
//cout << "Request Landing Clearance called...\n";
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ public:
|
|||
bool longFinalAcknowledged;
|
||||
bool finalReported;
|
||||
bool finalAcknowledged;
|
||||
bool instructedToGoAround; // set true if told by tower to go around
|
||||
bool onRwy; // is physically on the runway
|
||||
bool nextOnRwy; // currently projected by tower to be the next on the runway
|
||||
|
||||
|
@ -128,6 +129,10 @@ public:
|
|||
// Contact tower when at a hold short for departure - for now we'll assume plane - maybe vehicles might want to cross runway eventually?
|
||||
void ContactAtHoldShort(PlaneRec plane, FGAIPlane* requestee, tower_traffic_type operation);
|
||||
|
||||
// Register the presence of an AI plane at a point where contact would already have been made in real life
|
||||
// CAUTION - currently it is assumed that this plane's callsign is unique - it is up to AIMgr to generate unique callsigns.
|
||||
void RegisterAIPlane(PlaneRec plane, FGAIPlane* ai, tower_traffic_type op, PatternLeg lg = LEG_UNKNOWN);
|
||||
|
||||
// Public interface to the active runway - this will get more complex
|
||||
// in the future and consider multi-runway use, airplane weight etc.
|
||||
inline string GetActiveRunway() { return activeRwy; }
|
||||
|
@ -272,6 +277,8 @@ private:
|
|||
// Add a tower plane rec with ETA to the traffic list in the correct position ETA-wise.
|
||||
// Returns true if this could cause a threshold ETA conflict with other traffic, false otherwise.
|
||||
bool AddToTrafficList(TowerPlaneRec* t, bool holding = false);
|
||||
|
||||
bool AddToCircuitList(TowerPlaneRec* t);
|
||||
|
||||
// Ground can be separate or handled by tower in real life.
|
||||
// In the program we will always use a separate FGGround class, but we need to know
|
||||
|
|
Loading…
Reference in a new issue