diff --git a/src/ATC/AILocalTraffic.cxx b/src/ATC/AILocalTraffic.cxx index 4a7b0da31..65557cdf9 100644 --- a/src/ATC/AILocalTraffic.cxx +++ b/src/ATC/AILocalTraffic.cxx @@ -605,6 +605,8 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) { double wind_from = wind_from_hdg->getDoubleValue(); double wind_speed = wind_speed_knots->getDoubleValue(); + double dveldt; + switch(leg) { case TAKEOFF_ROLL: //inAir = false; @@ -627,9 +629,26 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) { break; case CLIMBOUT: 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) { - cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n'; - leg = TURN1; + double cc = 0.0; + if(tower->GetCrosswindConstraint(cc)) { + if(orthopos.y() > cc) { + cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n'; + 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; case TURN1: @@ -647,9 +666,18 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) { pitch = 0.0; 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) { - leg = TURN2; + 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; + } + } else { + cout << "Turning to downwind, distance from centerline = " << fabs(orthopos.x()) << '\n'; + leg = TURN2; + } } break; case TURN2: @@ -681,15 +709,26 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) { transmitted = true; } 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!!) pitch = -3.0; IAS = 85.0; } if(orthopos.y() < -980) { - //roll = -20; - leg = TURN3; - transmitted = false; - IAS = 80.0; + 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; + transmitted = false; + IAS = 80.0; + } + } else { + cout << "Turning to base, distance from threshold = " << fabs(orthopos.y()) << '\n'; + leg = TURN3; + transmitted = false; + IAS = 80.0; + } } break; case TURN3: @@ -759,7 +798,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) { pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset); } track = rwy.hdg; - double dveldt = -5.0; + dveldt = -5.0; vel += dveldt * dt; // FIXME - differentiate between touch and go and full stops if(vel <= 15.0) { @@ -775,6 +814,8 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) { } } break; + case LEG_UNKNOWN: + break; } if(inAir) { diff --git a/src/ATC/tower.cxx b/src/ATC/tower.cxx index db95044a5..62aade716 100644 --- a/src/ATC/tower.cxx +++ b/src/ATC/tower.cxx @@ -44,6 +44,7 @@ longFinalAcknowledged(false), finalReported(false), finalAcknowledged(false), opType(TTT_UNKNOWN), +leg(LEG_UNKNOWN), isUser(false) { plane.callsign = "UNKNOWN"; @@ -59,6 +60,7 @@ longFinalAcknowledged(false), finalReported(false), finalAcknowledged(false), opType(TTT_UNKNOWN), +leg(LEG_UNKNOWN), isUser(false) { plane = p; @@ -74,6 +76,7 @@ longFinalAcknowledged(false), finalReported(false), finalAcknowledged(false), opType(TTT_UNKNOWN), +leg(LEG_UNKNOWN), isUser(false) { plane.callsign = "UNKNOWN"; @@ -90,6 +93,7 @@ longFinalAcknowledged(false), finalReported(false), finalAcknowledged(false), opType(TTT_UNKNOWN), +leg(LEG_UNKNOWN), isUser(false) { plane = p; @@ -230,7 +234,6 @@ void FGTower::Update(double dt) { // Do one plane from the hold list if(ii == 4) { if(holdList.size()) { - //cout << "A" << endl; //cout << "*holdListItr = " << *holdListItr << endl; if(holdListItr == holdList.end()) { holdListItr = holdList.begin(); @@ -240,14 +243,10 @@ void FGTower::Update(double dt) { TowerPlaneRec* t = *holdListItr; //cout << "t = " << t << endl; if(t->holdShortReported) { - //cout << "B" << endl; double responseTime = 10.0; // seconds - this should get more sophisticated at some point if(t->clearanceCounter > responseTime) { - //cout << "C" << endl; if(t->nextOnRwy) { - //cout << "D" << endl; if(rwyOccupied) { - //cout << "E" << endl; // Do nothing for now - consider acknowloging hold short eventually } else { // Lets Roll !!!! @@ -337,20 +336,49 @@ void FGTower::Update(double dt) { 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!! 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); if(t->isUser) { // 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); + //cout << " ho = " << ho << '\n'; // 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(abs(ho) < 30) { // could be either takeoff, climbout or landing - check orthopos.y - if((tortho.y() < 0) || (t->leg == TURN4) || (t->leg == LANDING_ROLL)) { - t->leg = LANDING_ROLL; - //cout << "Landing_roll\n"; + //cout << "tortho.y = " << tortho.y() << '\n'; + if((tortho.y() < 0) || (t->leg == TURN4) || (t->leg == FINAL)) { + t->leg = FINAL; + //cout << "Final\n"; } else { t->leg = CLIMBOUT; // TODO - check elev wrt. apt elev to differentiate takeoff roll and climbout //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) { // turn1 or turn 4 @@ -394,29 +422,35 @@ void FGTower::Update(double dt) { 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_pos = tortho.y(); + //cout << "base_leg_pos = " << base_leg_pos << '\n'; break; case TURN4: // Fall through to base case BASE: base_leg_pos = tortho.y(); + //cout << "base_leg_pos = " << base_leg_pos << '\n'; break; case TURN3: // Fall through to downwind case DOWNWIND: // 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); + //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 + //cout << " downwind_leg_pos = " << downwind_leg_pos << '\n'; break; case TURN2: // Fall through to crosswind case CROSSWIND: crosswind_leg_pos = tortho.y(); + //cout << "crosswind_leg_pos = " << crosswind_leg_pos << '\n'; break; case TURN1: // Fall through to climbout case CLIMBOUT: // 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; case TAKEOFF_ROLL: 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. // 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 diff --git a/src/ATC/tower.hxx b/src/ATC/tower.hxx index 13a0b6833..dee2d5e5e 100644 --- a/src/ATC/tower.hxx +++ b/src/ATC/tower.hxx @@ -135,6 +135,12 @@ public: inline atc_type GetType() { return TOWER; } 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: FGATCMgr* ATCmgr; @@ -224,7 +230,7 @@ private: SGPropertyNode* user_lon_node; SGPropertyNode* user_lat_node; SGPropertyNode* user_elev_node; - SGPropertyNode* user_hdg_node; + SGPropertyNode* user_hdg_node; // Details of the general traffic flow etc in the circuit double crosswind_leg_pos; // Distance from threshold crosswind leg is being turned to in meters (actual operation - *not* ideal circuit)