- Better transistion between taxi and takeoff phases.
- Skipping the pushback stages when a gate doesn't require push-back.
- Some test code always chooses the user aircraft as the one that has to hold position.
_performance = 0; //TODO initialize to JET_TRANSPORT from PerformanceDB
dt = 0;
- scheduledForTakeoff = false;
+ takeOffStatus = 0;
}
}
}
-void FGAIAircraft::scheduleForATCTowerDepartureControl() {
- if (!scheduledForTakeoff) {
+void FGAIAircraft::scheduleForATCTowerDepartureControl(int state) {
+ if (!takeOffStatus) {
int leg = fp->getLeg();
if (trafficRef) {
if (trafficRef->getDepartureAirport()->getDynamics()) {
towerController->announcePosition(getID(), fp, fp->getCurrentWaypoint()->getRouteIndex(),
_getLatitude(), _getLongitude(), hdg, speed, altitude_ft,
trafficRef->getRadius(), leg, this);
+ cerr << "Scheduling " << trafficRef->getCallSign() << " for takeoff " << endl;
}
}
}
- scheduledForTakeoff = true;
+ takeOffStatus = state;
}
// Process ATC instructions and report back
// This waypoint marks the fact that the aircraft has passed the initial taxi
// departure waypoint, so it can release the parking.
//cerr << trafficRef->getCallSign() << " has passed waypoint " << prev->name << " at speed " << speed << endl;
+ //cerr << "Passing waypoint : " << prev->getName() << endl;
if (prev->contains("PushBackPoint")) {
dep->getDynamics()->releaseParking(fp->getGate());
AccelTo(0.0);
- setTaxiClearanceRequest(true);
+ //setTaxiClearanceRequest(true);
}
if (prev->contains("legend")) {
fp->incrementLeg();
}
if (prev->contains(string("DepartureHold"))) {
- scheduleForATCTowerDepartureControl();
+ cerr << "Passing point DepartureHold" << endl;
+ scheduleForATCTowerDepartureControl(2);
}
// This is the last taxi waypoint, and marks the the end of the flight plan
if (nextDeparture < (now+1200)) {
nextDeparture = now + 1200;
}
- fp->setTime(nextDeparture); // should be "next departure"
+ fp->setTime(nextDeparture);
}
return true;
bool getTaxiClearanceRequest() { return needsTaxiClearance; };
FGAISchedule * getTrafficRef() { return trafficRef; };
void setTrafficRef(FGAISchedule *ref) { trafficRef = ref; };
- void scheduleForATCTowerDepartureControl();
+ void resetTakeOffStatus() { takeOffStatus = 0;};
+ void setTakeOffStatus(int status) { takeOffStatus = status; };
+ void scheduleForATCTowerDepartureControl(int state);
- inline bool isScheduledForTakeoff() { return scheduledForTakeoff; };
+ //inline bool isScheduledForTakeoff() { return scheduledForTakeoff; };
virtual const char* getTypeString(void) const { return "aircraft"; }
inline double airspeed() const { return props->getFloatValue("velocities/airspeed-kt");};
std::string atGate();
+ int getTakeOffStatus() { return takeOffStatus; };
+
void checkTcas();
FGATCController * getATCController() { return controller; };
bool reachedWaypoint;
bool needsTaxiClearance;
bool _needsGroundElevation;
- bool scheduledForTakeoff;
+ int takeOffStatus; // 1 = joined departure cue; 2 = Passed DepartureHold waypoint; handover control to tower; 0 = any other state.
time_t timeElapsed;
PerformanceData* _performance; // the performance data for this aircraft
if ((timeDiff > 60) && (timeDiff < 1200))
leg = 2;
- else if ((timeDiff >= 1200) && (timeDiff < 1500))
+ else if ((timeDiff >= 1200) && (timeDiff < 1500)) {
leg = 3;
+ ac->setTakeOffStatus(2);
+ }
else if ((timeDiff >= 1500) && (timeDiff < 2000))
leg = 4;
else if (timeDiff >= 2000)
-// // FGAIFlightPlan - class for loading and storing AI flight plans
+// FGAIFlightPlan - class for loading and storing AI flight plans
// Written by David Culp, started May 2004
// - davidculp2@comcast.net
//
double getDistanceToGo(double lat, double lon, FGAIWaypoint* wp) const;
int getLeg () const { return leg;};
+
void setLeadDistance(double speed, double bearing, FGAIWaypoint* current, FGAIWaypoint* next);
void setLeadDistance(double distance_ft);
double getLeadDistance( void ) const {return lead_distance;}
// push each node on the taxi route as a waypoint
int route;
+ //cerr << "Building taxi route" << endl;
while (taxiRoute->next(&node, &route)) {
char buffer[10];
snprintf(buffer, 10, "%d", node);
createOnGround(ac, buffer, tn->getGeod(), apt->getElevation(),
ac->getPerformance()->vTaxi());
wpt->setRouteIndex(route);
- if (taxiRoute->size() == 1) {
+ //cerr << "Nodes left " << taxiRoute->nodesLeft() << " ";
+ if (taxiRoute->nodesLeft() == 1) {
// Note that we actually have hold points in the ground network, but this is just an initial test.
+ //cerr << "Setting departurehold point: " << endl;
wpt->setName( wpt->getName() + string("DepartureHold"));
}
waypoints.push_back(wpt);
}
+ // Acceleration point, 105 meters into the runway,
+ SGGeod accelPoint = rwy->pointOnCenterline(105.0);
+ FGAIWaypoint *wpt = createOnGround(ac, "accel", accelPoint, apt->getElevation(), ac->getPerformance()->vRotate());
+ waypoints.push_back(wpt);
+
+ //cerr << "[done]" << endl;
return true;
}
double airportElev = apt->getElevation();
- // Acceleration point, 105 meters into the runway,
- SGGeod accelPoint = rwy->pointOnCenterline(105.0);
- wpt = createOnGround(ac, "accel", accelPoint, airportElev, vRotate);
- waypoints.push_back(wpt);
-
+
accelDistance =
(vTakeoffMetric * vTakeoffMetric -
vTaxiMetric * vTaxiMetric) / (2 * accelMetric);
//cerr << "Using " << accelDistance << " " << accelMetric << " " << vTakeoffMetric << endl;
- accelPoint = rwy->pointOnCenterline(105.0 + accelDistance);
+ SGGeod accelPoint = rwy->pointOnCenterline(105.0 + accelDistance);
wpt = createOnGround(ac, "rotate", accelPoint, airportElev, vTakeoff);
waypoints.push_back(wpt);
// some special considerations for the last point:
waypoints.back()->setName(string("PushBackPoint"));
waypoints.back()->setSpeed(vTaxi);
+ ac->setTaxiClearanceRequest(true);
} else { // In case of a push forward departure...
+ ac->setTaxiClearanceRequest(false);
double lat2 = 0.0, lon2 = 0.0, az2 = 0.0;
//cerr << "Creating final push forward point for gate " << gateId << endl;
string fltType = "ga";
fp->setRunway(runway);
fp->createTakeOff(&ai_ac, false, apt, 0, fltType);
+ ai_ac.setTakeOffStatus(2);
} else {
controller = apt->getDynamics()->getStartupController();
int stationFreq = apt->getDynamics()->getGroundFrequency(1);
}
//exit(1);
}
+/**
+ * Check if another aircraft is ahead of the current one, and on the same
+ * return true / false is the is/isn't the case.
+ *
+ ****************************************************************************/
bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other)
{
string transponderCode;
FGAIFlightPlan *fp;
string fltRules;
+ string instructionText;
//double commFreqD;
sender = rec->getAircraft()->getTrafficRef()->getCallSign();
+ if (rec->getAircraft()->getTaxiClearanceRequest()) {
+ instructionText = "push-back and taxi";
+ } else {
+ instructionText = "taxi";
+ }
//cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
switch (rec->getLeg()) {
case 1:
receiver + ". Start-up approved. " + atisInformation +
" correct, runway " + activeRunway + ", " + SID + ", squawk " +
transponderCode + ". " +
- "For push-back and taxi clearance call " + taxiFreqStr + ". " +
+ "For "+ instructionText + " clearance call " + taxiFreqStr + ". " +
sender + " control.";
break;
case MSG_DENY_ENGINE_START:
taxiFreqStr = formatATCFrequency3_2(taxiFreq);
activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
transponderCode = rec->getAircraft()->GetTransponderCode();
+
text =
receiver + ". Start-up approved. " + atisInformation +
" correct, runway " + activeRunway + ", " + SID + ", squawk " +
transponderCode + ". " +
- "For push-back and taxi clearance call " + taxiFreqStr + ". " +
+ "For " + instructionText + " clearance call " + taxiFreqStr + ". " +
sender;
break;
case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
text = receiver + ". Roger. " + sender;
break;
case MSG_REQUEST_PUSHBACK_CLEARANCE:
- text = receiver + ". Request push-back. " + sender;
+ if (rec->getAircraft()->getTaxiClearanceRequest()) {
+ text = receiver + ". Request push-back. " + sender;
+ } else {
+ text = receiver + ". Request Taxi clearance. " + sender;
+ }
break;
case MSG_PERMIT_PUSHBACK_CLEARANCE:
- text = receiver + ". Push-back approved. " + sender;
+ if (rec->getAircraft()->getTaxiClearanceRequest()) {
+ text = receiver + ". Push-back approved. " + sender;
+ } else {
+ text = receiver + ". Cleared to Taxi." + sender;
+ }
break;
case MSG_HOLD_PUSHBACK_CLEARANCE:
text = receiver + ". Standby. " + sender;
rec.setRunway(intendedRoute->getRunway());
rec.setLeg(leg);
//rec.setCallSign(callsign);
+ rec.setRadius(radius);
rec.setAircraft(ref);
activeTraffic.push_back(rec);
- cerr << ref->getTrafficRef()->getCallSign() << " You are number " << activeTraffic.size() << " for takeoff " << endl;
+ // Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway.
+ ActiveRunwayVecIterator rwy = activeRunways.begin();
+ if (activeRunways.size()) {
+ while (rwy != activeRunways.end()) {
+ if (rwy->getRunwayName() == intendedRoute->getRunway()) {
+ break;
+ }
+ rwy++;
+ }
+ }
+ if (rwy == activeRunways.end()) {
+ ActiveRunway aRwy(intendedRoute->getRunway(), id);
+ aRwy.addToDepartureCue(ref);
+ activeRunways.push_back(aRwy);
+ rwy = (activeRunways.end()-1);
+ } else {
+ rwy->addToDepartureCue(ref);
+ }
+
+ cerr << ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getDepartureCueSize() << " for takeoff " << endl;
} else {
i->setPositionAndHeading(lat, lon, heading, speed, alt);
}
}
setDt(getDt() + dt);
-// // see if we already have a clearance record for the currently active runway
+ // see if we already have a clearance record for the currently active runway
+ // NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is
+ // already exists here. So, we can simplify the current code.
ActiveRunwayVecIterator rwy = activeRunways.begin();
- // again, a map might be more efficient here
- if (activeRunways.size()) {
- //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
- while (rwy != activeRunways.end()) {
- if (rwy->getRunwayName() == current->getRunway()) {
- break;
- }
- rwy++;
+ while (rwy != activeRunways.end()) {
+ if (rwy->getRunwayName() == current->getRunway()) {
+ break;
}
+ rwy++;
}
- if (rwy == activeRunways.end()) {
- ActiveRunway aRwy(current->getRunway(), id);
- activeRunways.push_back(aRwy); // Since there are no clearance records for this runway yet
- current->setHoldPosition(false); // Clear the current aircraft to continue
- } else {
- // Okay, we have a clearance record for this runway, so check
- // whether the clearence ID matches that of the current aircraft
- if (id == rwy->getCleared()) {
- current->setHoldPosition(false);
+
+ // only bother running the following code if the current aircraft is the
+ // first in line for depature
+ /* if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
+ if (rwy->getCleared()) {
+ if (id == rwy->getCleared()) {
+ current->setHoldPosition(false);
+ } else {
+ current->setHoldPosition(true);
+ }
} else {
- current->setHoldPosition(true);
+ // For now. At later stages, this will probably be the place to check for inbound traffc.
+ rwy->setCleared(id);
+ }
+ } */
+ // only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control
+ if (current->getAircraft()->getTakeOffStatus() == 2) {
+ current->setHoldPosition(true);
+ int clearanceId = rwy->getCleared();
+ if (clearanceId) {
+ if (id == clearanceId) {
+ current->setHoldPosition(false);
+ }
+ } else {
+ if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
+ rwy->setCleared(id);
+ }
}
}
}
rwy++;
}
if (rwy != activeRunways.end()) {
- rwy = activeRunways.erase(rwy);
+ rwy->setCleared(0);
+ rwy->updateDepartureCue();
} else {
SG_LOG(SG_GENERAL, SG_ALERT,
"AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
SG_LOG(SG_GENERAL, SG_ALERT,
"AI error: Aircraft without traffic record is signing off from tower");
} else {
+ i->getAircraft()->resetTakeOffStatus();
i = activeTraffic.erase(i);
//cerr << "Signing off from tower controller" << endl;
}
typedef vector<time_t> TimeVector;
typedef vector<time_t>::iterator TimeVectorIterator;
+typedef vector<FGAIAircraft*> AircraftVec;
+typedef vector<FGAIAircraft*>::iterator AircraftVecIterator;
/***********************************************************************
* Active runway, a utility class to keep track of which aircraft has
int currentlyCleared;
double distanceToFinal;
TimeVector estimatedArrivalTimes;
+ AircraftVec departureCue;
+
public:
ActiveRunway(string r, int cc) { rwy = r; currentlyCleared = cc; distanceToFinal = 6.0 * SG_NM_TO_METER; };
//time_t getEstApproachTime() { return estimatedArrival; };
//void setEstApproachTime(time_t time) { estimatedArrival = time; };
+ void addToDepartureCue(FGAIAircraft *ac) { departureCue.push_back(ac); };
+ void setCleared(int number) { currentlyCleared = number; };
time_t requestTimeSlot(time_t eta);
+
+ int getDepartureCueSize() { return departureCue.size(); };
+ FGAIAircraft* getFirstAircraftInDepartureCue() { return departureCue.size() ? *(departureCue.begin()) : NULL; };
+ void updateDepartureCue() { departureCue.erase(departureCue.begin()); }
};
typedef vector<ActiveRunway> ActiveRunwayVec;
return true;
};
+
void FGTaxiRoute::rewind(int route)
{
int currPoint;
i = activeTraffic.erase(i);
}
}
-
+/**
+ * The ground network can deal with the following states:
+ * 0 = Normal; no action required
+ * 1 = "Acknowledge "Hold position
+ * 2 = "Acknowledge "Resume taxi".
+ * 3 = "Issue TaxiClearance"
+ * 4 = =Acknowledge Taxi Clearance"
+ *
+ *************************************************************************************************************************/
bool FGGroundNetwork::checkTransmissionState(int minState, int maxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId,
AtcMsgDir msgDir)
{
if ((state == 5) && available) {
current->setState(0);
current->getAircraft()->setTaxiClearanceRequest(false);
- current->setHoldPosition(true);
+ current->setHoldPosition(false);
available = false;
}
if (bearing > 180)
bearing = 360 - bearing;
if ((dist < mindist) && (bearing < 60.0)) {
+ //cerr << "Current aircraft " << current->getAircraft()->getTrafficRef()->getCallSign()
+ // << " is closest to " << i->getAircraft()->getTrafficRef()->getCallSign()
+ // << ", which has status " << i->getAircraft()->isScheduledForTakeoff()
+ // << endl;
mindist = dist;
closest = i;
minbearing = bearing;
return;
else
current->setWaitsForId(closest->getId());
- if (closest->getId() != current->getId())
+ if (closest->getId() != current->getId()) {
current->setSpeedAdjustment(closest->getSpeed() *
(mindist / 100));
- if (closest->getAircraft()->isScheduledForTakeoff())
- current->getAircraft()->scheduleForATCTowerDepartureControl();
- else
+ if (
+ closest->getAircraft()->getTakeOffStatus() &&
+ (current->getAircraft()->getTrafficRef()->getDepartureAirport() == closest->getAircraft()->getTrafficRef()->getDepartureAirport()) &&
+ (current->getAircraft()->GetFlightPlan()->getRunway() == closest->getAircraft()->GetFlightPlan()->getRunway())
+ )
+ current->getAircraft()->scheduleForATCTowerDepartureControl(1);
+ } else {
current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
+ }
if (mindist < maxAllowableDistance) {
//double newSpeed = (maxAllowableDistance-mindist);
//current->setSpeedAdjustment(newSpeed);
//(!(current->getSpeedAdjustment())))
{
- current->setHoldPosition(true);
- current->setWaitsForId(i->getId());
+ if (!(isUserAircraft(i->getAircraft()))) { // test code. Don't wait for the user, let the user wait for you.
+ current->setHoldPosition(true);
+ current->setWaitsForId(i->getId());
+ }
//cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
// << dist << " meters. Waiting for " << i->getCallSign();
//if (opposing)
void first() { currNode = nodes.begin(); currRoute = routes.begin(); };
int size() { return nodes.size(); };
+ int nodesLeft() { return nodes.end() - currNode; };
+
// int getDepth() { return depth; };
};