// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+/*==========================================================
+
+TODO list.
+
+Should get pattern direction from tower.
+
+Need to continually monitor and adjust deviation from glideslope
+during descent to avoid occasionally landing short or long.
+
+============================================================*/
+
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
descending = false;
targetDescentRate = 0.0;
+ goAround = false;
+ goAroundCalled = false;
}
FGAILocalTraffic::~FGAILocalTraffic() {
hence the traffic must be initialised with respect to the user as well as each other.
To a certain extent it's FGAIMgr that has to worry about this, but we need to provide
sufficient initialisation functionality within the plane classes to allow the manager
-to initialy position them where and how required.
+to initially position them where and how required.
*/
bool FGAILocalTraffic::Init(string ICAO, OperatingState initialState, PatternLeg initialLeg) {
//cout << "FGAILocalTraffic.Init(...) called" << endl;
AirportATC a;
if(ATC->GetAirportATCDetails(airportID, &a)) {
if(a.tower_freq) { // Has a tower
- tower = (FGTower*)ATC->GetATCPointer((string)airportID, TOWER); // Maybe need some error checking here
+ tower = (FGTower*)ATC->GetATCPointer(airportID, TOWER); // Maybe need some error checking here
if(tower == NULL) {
// Something has gone wrong - abort or carry on with un-towered operation?
return(false);
} 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;
operatingState = initialState;
switch(operatingState) {
case PARKED:
+ tuned_station = ground;
ourGate = ground->GetGateNode();
if(ourGate == NULL) {
// Implies no available gates - what shall we do?
Transform();
break;
case TAXIING:
+ tuned_station = ground;
// FIXME - implement this case properly
return(false); // remove this line when fixed!
break;
// since we've got the implementation for this case already.
// TODO - implement proper generic in_pattern startup.
- // 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;
+ // 18/10/03 - adding the ability to start on downwind (mainly to speed testing of the go-around code!!)
- // 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();
+ //cout << "Starting in pattern...\n";
- pitch = 0.0;
- roll = 0.0;
- leg = TAKEOFF_ROLL;
- vel = 0.0;
- slope = 0.0;
+ tuned_station = tower;
circuitsToFly = 0; // ie just fly this circuit and then stop
touchAndGo = false;
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();
// Return what type of landing we're doing on this circuit
LandingType FGAILocalTraffic::GetLandingOption() {
+ //cout << "circuitsToFly = " << circuitsToFly << '\n';
if(circuitsToFly) {
return(touchAndGo ? TOUCH_AND_GO : STOP_AND_GO);
} else {
string trns = "Tower ";
double f = globals->get_ATC_mgr()->GetFrequency(airportID, TOWER) / 100.0;
char buf[10];
- sprintf(buf, "%f", f);
+ sprintf(buf, "%.2f", f);
trns += buf;
trns += " ";
trns += plane.callsign;
- Transmit(trns);
+ pending_transmission = trns;
+ ConditionalTransmit(30.0);
responseCounter = 0.0;
contactTower = false;
changeFreq = true;
if((changeFreq) && (responseCounter > 8.0)) {
switch(changeFreqType) {
case TOWER:
+ tuned_station = tower;
freq = (double)tower->get_freq() / 100.0;
//Transmit("DING!");
// Contact the tower, even if only virtually
changeFreq = false;
- tower->ContactAtHoldShort(plane, this, CIRCUIT);
+ pending_transmission = plane.callsign;
+ pending_transmission += " at hold short for runway ";
+ pending_transmission += ConvertRwyNumToSpokenString(rwy.rwyID);
+ pending_transmission += " traffic pattern ";
+ if(circuitsToFly) {
+ pending_transmission += ConvertNumToSpokenDigits(circuitsToFly + 1);
+ pending_transmission += " circuits touch and go";
+ } else {
+ pending_transmission += " one circuit to full stop";
+ }
+ Transmit(2);
break;
case GROUND:
+ tuned_station = ground;
freq = (double)ground->get_freq() / 100.0;
break;
// And to avoid compiler warnings...
- case APPROACH:
- break;
- case ATIS:
- break;
- case ENROUTE:
- break;
- case DEPARTURE:
- break;
- case INVALID:
- break;
+ case APPROACH: break;
+ case ATIS: break;
+ case ENROUTE: break;
+ case DEPARTURE: break;
+ case INVALID: break;
}
}
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);
holdingShort = false;
string trns = "Cleared for take-off ";
trns += plane.callsign;
- Transmit(trns);
+ pending_transmission = trns;
+ Transmit();
StartTaxi();
}
//cout << "^" << flush;
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
StartTaxi();
} else if(!taxiRequestPending) {
//cout << "(" << flush;
- ground->RequestDeparture(plane, this);
// Do some communication
// airport name + tower + airplane callsign + location + request taxi for + operation type + ?
string trns = "";
trns += plane.callsign;
trns += " on apron parking request taxi for traffic pattern";
//cout << "trns = " << trns << endl;
- Transmit(trns);
+ pending_transmission = trns;
+ Transmit(1);
taxiRequestCleared = false;
taxiRequestPending = true;
}
fgSetDouble("/AI/Local1/ortho-x", (ortho.ConvertToLocal(pos)).x());
fgSetDouble("/AI/Local1/ortho-y", (ortho.ConvertToLocal(pos)).y());
fgSetDouble("/AI/Local1/elev", pos.elev() * SG_METER_TO_FEET);
+
+ // And finally, call parent for transmission rendering
+ FGAIPlane::Update(dt);
}
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;
default:
break;
}
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) {
cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n';
leg = TURN1;
}
- } else {
+ } 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;
}
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;
}
break;
case CROSSWIND:
+ goAround = false;
LevelWings();
track = rwy.hdg + (90.0 * patternDirection);
if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) {
}
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();
break;
case LANDING_ROLL:
//inAir = false;
+ descending = false;
if(aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
}
//double turn_allowance = 150.0; // Approximate distance in meters that a 90deg corner is shortened by turned in a light plane.
double stod = pa / tan(ga * DCL_DEGREES_TO_RADIANS); // distance in meters from touchdown point to start descent
- cout << "Descent to start = " << stod << " meters out\n";
+ //cout << "Descent to start = " << stod << " meters out\n";
if(stod < blp) { // Start descending on final
SoD.leg = FINAL;
SoD.y = stod * -1.0;
void FGAILocalTraffic::TransmitPatternPositionReport(void) {
// airport name + "traffic" + airplane callsign + pattern direction + pattern leg + rwy + ?
string trns = "";
+ int code = 0;
trns += tower->get_name();
trns += " Traffic ";
// Fall through to DOWNWIND
case DOWNWIND:
trns += "downwind ";
+ code = 11;
break;
case TURN3:
// Fall through to BASE
// Fall through to FINAL
case FINAL: // maybe this should include long/short final if appropriate?
trns += "final ";
+ code = 13;
break;
default: // Hopefully this won't be used
trns += "pattern ";
break;
}
- // FIXME - I've hardwired the runway call as well!! (We could work this out from rwy heading and mag deviation)
- trns += ConvertRwyNumToSpokenString(1);
+ trns += ConvertRwyNumToSpokenString(rwy.rwyID);
// And add the airport name again
trns += tower->get_name();
- Transmit(trns);
+ pending_transmission = trns; // FIXME - make up pending_transmission natively
+ ConditionalTransmit(90.0, code); // Assume a report of this leg will be invalid if we can't transmit within a minute and a half.
+}
+
+// Callback handler
+// TODO - Really should enumerate these coded values.
+void FGAILocalTraffic::ProcessCallback(int code) {
+ // 1 - Request Departure from ground
+ // 2 - Report at hold short
+ // 10 - report crosswind
+ // 11 - report downwind
+ // 12 - report base
+ // 13 - report final
+ if(code == 1) {
+ ground->RequestDeparture(plane, this);
+ } else if(code == 2) {
+ tower->ContactAtHoldShort(plane, this, CIRCUIT);
+ } else if(code == 11) {
+ tower->ReportDownwind(plane.callsign);
+ } else if(code == 13) {
+ tower->ReportFinal(plane.callsign);
+ }
}
void FGAILocalTraffic::ExitRunway(Point3D orthopos) {