1
0
Fork 0

AI plane should go around instead of landing on user if user dawdles on runway now

This commit is contained in:
daveluff 2003-10-19 20:38:08 +00:00
parent cd08f83aa5
commit 0adae696aa
4 changed files with 177 additions and 40 deletions

View file

@ -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();

View file

@ -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

View file

@ -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";

View file

@ -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