#include <Airports/runways.hxx>
#include "tower.hxx"
+#include "ground.hxx"
#include "ATCmgr.hxx"
#include "ATCutils.hxx"
#include "ATCDialog.hxx"
#include "commlist.hxx"
-#include "AILocalTraffic.hxx"
using std::cout;
// TowerPlaneRec
TowerPlaneRec::TowerPlaneRec() :
- planePtr(NULL),
+ eta(0),
+ dist_out(0),
clearedToLand(false),
clearedToLineUp(false),
clearedToTakeOff(false),
instructedToGoAround(false),
onRwy(false),
nextOnRwy(false),
+ gearWasUp(false),
+ gearUpReported(false),
vfrArrivalReported(false),
vfrArrivalAcknowledged(false),
opType(TTT_UNKNOWN),
leg(LEG_UNKNOWN),
landingType(AIP_LT_UNKNOWN),
- gearWasUp(false),
- gearUpReported(false),
isUser(false)
{
plane.callsign = "UNKNOWN";
}
TowerPlaneRec::TowerPlaneRec(const PlaneRec& p) :
- planePtr(NULL),
+ eta(0),
+ dist_out(0),
clearedToLand(false),
clearedToLineUp(false),
clearedToTakeOff(false),
instructedToGoAround(false),
onRwy(false),
nextOnRwy(false),
+ gearWasUp(false),
+ gearUpReported(false),
vfrArrivalReported(false),
vfrArrivalAcknowledged(false),
opType(TTT_UNKNOWN),
leg(LEG_UNKNOWN),
landingType(AIP_LT_UNKNOWN),
- gearWasUp(false),
- gearUpReported(false),
isUser(false)
{
plane = p;
}
-TowerPlaneRec::TowerPlaneRec(const Point3D& pt) :
- planePtr(NULL),
+TowerPlaneRec::TowerPlaneRec(const SGGeod& pt) :
+ eta(0),
+ dist_out(0),
clearedToLand(false),
clearedToLineUp(false),
clearedToTakeOff(false),
instructedToGoAround(false),
onRwy(false),
nextOnRwy(false),
+ gearWasUp(false),
+ gearUpReported(false),
vfrArrivalReported(false),
vfrArrivalAcknowledged(false),
opType(TTT_UNKNOWN),
leg(LEG_UNKNOWN),
landingType(AIP_LT_UNKNOWN),
- gearWasUp(false),
- gearUpReported(false),
isUser(false)
{
plane.callsign = "UNKNOWN";
- pos = pt;
+ pos = pt;
}
-TowerPlaneRec::TowerPlaneRec(const PlaneRec& p, const Point3D& pt) :
- planePtr(NULL),
+TowerPlaneRec::TowerPlaneRec(const PlaneRec& p, const SGGeod& pt) :
+ eta(0),
+ dist_out(0),
clearedToLand(false),
clearedToLineUp(false),
clearedToTakeOff(false),
instructedToGoAround(false),
onRwy(false),
nextOnRwy(false),
+ gearWasUp(false),
+ gearUpReported(false),
vfrArrivalReported(false),
vfrArrivalAcknowledged(false),
opType(TTT_UNKNOWN),
leg(LEG_UNKNOWN),
landingType(AIP_LT_UNKNOWN),
- gearWasUp(false),
- gearUpReported(false),
isUser(false)
{
plane = p;
- pos = pt;
+ pos = pt;
}
DoRwyDetails();
// TODO - this currently assumes only one active runway.
- rwyOccupied = OnActiveRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0));
+ rwyOccupied = OnActiveRunway(SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0));
- if(!OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), false)) {
+ if(!OnAnyRunway(SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), false)) {
//cout << ident << " ADD 0\n";
current_atcdialog->add_entry(ident, "@AP Tower, @CS @MI miles @CD of the airport for full stop@AT",
"Contact tower for VFR arrival (full stop)", TOWER,
t->landingType = AIP_LT_UNKNOWN;
t->leg = TAKEOFF_ROLL;
t->isUser = true;
- t->planePtr = NULL;
t->clearedToTakeOff = false;
rwyList.push_back(t);
rwyListItr = rwyList.begin();
// Call the base class update for the response time handling.
FGATC::Update(dt);
-
- /*
- if(ident == "KEMT") {
- // For AI debugging convienience - may be removed
- Point3D user_pos;
- user_pos.setlon(user_lon_node->getDoubleValue());
- user_pos.setlat(user_lat_node->getDoubleValue());
- user_pos.setelev(user_elev_node->getDoubleValue());
- Point3D user_ortho_pos = ortho.ConvertToLocal(user_pos);
- fgSetDouble("/AI/user/ortho-x", user_ortho_pos.x());
- fgSetDouble("/AI/user/ortho-y", user_ortho_pos.y());
- fgSetDouble("/AI/user/elev", user_elev_node->getDoubleValue());
- }
- */
-
- //cout << "Done T" << endl;
}
void FGTower::ReceiveUserCallback(int code) {
// Should we clear staight in or for downwind entry?
// For now we'll clear straight in if greater than 1km from a line drawn through the threshold perpendicular to the rwy.
// Later on we might check the actual heading and direct some of those to enter on downwind or base.
- Point3D op = ortho.ConvertToLocal(t->pos);
+ SGVec3d op = ortho.ConvertToLocal(t->pos);
float gp = fgGetFloat("/gear/gear/position-norm");
if(gp < 1)
t->gearWasUp = true; // This will be needed on final to tell "Gear down, ready to land."
t->opType = STRAIGHT_IN;
if(t->isUser) {
current_atcdialog->add_entry(ident, "@CS @MI mile final runway @RW@GR", "Report Final", TOWER, (int)USER_REPORT_3_MILE_FINAL);
- } else {
- t->planePtr->RegisterTransmission(14);
}
} else {
// For now we'll just request reporting downwind.
// leave it in the app list until it gets into pattern though.
if(t->isUser) {
current_atcdialog->add_entry(ident, "@AP Tower, @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND);
- } else {
- t->planePtr->RegisterTransmission(15);
}
}
trns += ConvertRwyNumToSpokenString(activeRwy);
trns += " Cleared for take-off" + wtr;
t->clearedToTakeOff = true;
} else {
- if(!OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), true)) {
+ if(!OnAnyRunway(SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), true)) {
// TODO: Check if any AI Planes on final and tell something like: "After the landing CALLSIGN line up runway two eight right"
trns += " Line up runway " + ConvertRwyNumToSpokenString(activeRwy);
t->clearedToTakeOff = false;
// or put rwy vacated at the top since that'll be more common?
current_atcdialog->add_entry(ident, "@CS Going Around", "Report going around", TOWER, USER_REPORT_GOING_AROUND);
current_atcdialog->add_entry(ident, "@CS Clear of the runway", "Report runway vacated", TOWER, USER_REPORT_RWY_VACATED);
- } else {
- t->planePtr->RegisterTransmission(7);
}
} else if(t->eta < 20) {
// Do nothing - we'll be telling it to go around in less than 10 seconds if the
t->opType = CIRCUIT;
if(t->isUser) {
current_atcdialog->add_entry(ident, "@AP Tower, @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND);
- } else {
- t->planePtr->RegisterTransmission(15);
}
t->clearedToLand = false;
}
if((i == 1) && rwyList.empty() && (t->nextOnRwy) && (!cf)) { // Unfortunately nextOnRwy currently doesn't handle circuit/straight-in ordering properly at present, hence the cf check below.
trns += "Cleared to land"; // TODO - clear for the option if appropriate
t->clearedToLand = true;
- if(!t->isUser) t->planePtr->RegisterTransmission(7);
} else if((i+a) > 1) {
//First set tt to point to the correct preceding plane - final or circuit
if(tc && tf) {
PatternLeg leg;
if(tt->isUser) {
leg = tt->leg;
- } else {
- leg = tt->planePtr->GetLeg();
}
if(leg == FINAL) {
trns += " on final";
sprintf(buf, "%.2f", f);
trns += buf;
trns += " Good Day";
- if(!t->isUser) t->planePtr->RegisterTransmission(5);
} else {
// Cop-out!!
trns += " cleared for taxi to general aviation parking";
- if(!t->isUser) t->planePtr->RegisterTransmission(6); // TODO - this is a mega-hack!!
}
//cout << "trns = " << trns << '\n';
if(_display) {
//if(timeSinceLastDeparture <= 60.0 && departed == true) {
trns += " line up runway " + ConvertRwyNumToSpokenString(activeRwy);
t->clearedToLineUp = true;
- t->planePtr->RegisterTransmission(3); // cleared to line-up
//} else if(arriving plane < some threshold away) {
} else if(GetTrafficETA(2) < 150.0 && (timeSinceLastDeparture > 60.0 || departed == false)) { // Hack - hardwired time
trns += " cleared immediate take-off";
SG_LOG(SG_ATC, SG_WARN, "Warning: Departing traffic cleared for *immediate* take-off despite no arriving traffic in FGTower");
}
t->clearedToTakeOff = true;
- t->planePtr->RegisterTransmission(4); // cleared to take-off - TODO differentiate between immediate and normal take-off
departed = false;
timeSinceLastDeparture = 0.0;
} else {
trns += " cleared for take-off";
// TODO - add traffic is... ?
t->clearedToTakeOff = true;
- t->planePtr->RegisterTransmission(4); // cleared to take-off
departed = false;
timeSinceLastDeparture = 0.0;
}
TowerPlaneRec* t = *circuitListItr;
//cout << ident << ' ' << circuitList.size() << ' ' << t->plane.callsign << " " << t->leg << " eta " << t->eta << '\n';
if(t->isUser) {
- t->pos.setlon(user_lon_node->getDoubleValue());
- t->pos.setlat(user_lat_node->getDoubleValue());
- t->pos.setelev(user_elev_node->getDoubleValue());
+ t->pos.setLongitudeDeg(user_lon_node->getDoubleValue());
+ t->pos.setLatitudeDeg(user_lat_node->getDoubleValue());
+ t->pos.setElevationM(user_elev_node->getDoubleValue());
//cout << ident << ' ' << circuitList.size() << ' ' << t->plane.callsign << " " << t->leg << " eta " << t->eta << '\n';
- } else {
- t->pos = t->planePtr->GetPos(); // We should probably only set the pos's on one walk through the traffic list in the update function, to save a few CPU should we end up duplicating this.
- t->landingType = t->planePtr->GetLandingOption();
- //cout << "AI plane landing option is " << t->landingType << '\n';
}
- Point3D tortho = ortho.ConvertToLocal(t->pos);
+ SGVec3d 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();
t->leg = LANDING_ROLL;
}
}
- } else {
- t->leg = t->planePtr->GetLeg();
}
// Set the constraints IF this is the first plane in the circuit
// Assume it complies!!!
t->opType = CIRCUIT;
t->leg = CLIMBOUT;
- if(t->planePtr) {
- //cout << "Registering Go-around transmission with AI plane\n";
- t->planePtr->RegisterTransmission(13);
- }
}
} else if(!t->clearedToLand) {
// The whip through the appList is a hack since currently t->nextOnRwy doesn't always work
Transmit();
//if(t->isUser) cout << "Transmitting cleared to Land!!!\n";
t->clearedToLand = true;
- if(!t->isUser) {
- t->planePtr->RegisterTransmission(7);
- }
}
} else {
//if(t->isUser) cout << "Not next\n";
rwyListItr = rwyList.begin();
TowerPlaneRec* t = *rwyListItr;
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 {
- t->pos = t->planePtr->GetPos(); // We should probably only set the pos's on one walk through the traffic list in the update function, to save a few CPU should we end up duplicating this.
+ t->pos.setLongitudeDeg(user_lon_node->getDoubleValue());
+ t->pos.setLatitudeDeg(user_lat_node->getDoubleValue());
+ t->pos.setElevationM(user_elev_node->getDoubleValue());
}
bool on_rwy = OnActiveRunway(t->pos);
if(!on_rwy) {
//cout << "t = " << t << endl;
//cout << "Checking " << t->plane.callsign << endl;
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
+ t->pos.setLongitudeDeg(user_lon_node->getDoubleValue());
+ t->pos.setLatitudeDeg(user_lat_node->getDoubleValue());
+ t->pos.setElevationM(user_elev_node->getDoubleValue());
}
doThresholdETACalc(); // We need this here because planes in the lists are not guaranteed to *always* have the correct ETA
//cout << "eta is " << t->eta << ", rwy is " << (rwyList.size() ? "occupied " : "clear ") << '\n';
- Point3D tortho = ortho.ConvertToLocal(t->pos);
+ SGVec3d tortho = ortho.ConvertToLocal(t->pos);
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],
// Assume it complies!!!
t->opType = CIRCUIT;
t->leg = CLIMBOUT;
- if(!t->isUser) {
- if(t->planePtr) {
- //cout << "Registering Go-around transmission with AI plane\n";
- t->planePtr->RegisterTransmission(13);
- }
- } else {
+ if(t->isUser) {
// TODO - add Go-around ack to comm options,
// remove report rwy vacated. (possibly).
}
Transmit();
//if(t->isUser) cout << "Transmitting cleared to Land!!!\n";
t->clearedToLand = true;
- if(!t->isUser) {
- t->planePtr->RegisterTransmission(7);
- }
}
} else {
//if(t->isUser) cout << "Not next\n";
// Check for landing...
bool landed = false;
- if(!t->isUser) {
- if(t->planePtr) {
- if(t->planePtr->GetLeg() == LANDING_ROLL) {
- landed = true;
- }
- } else {
- SG_LOG(SG_ATC, SG_ALERT, "WARNING - not user and null planePtr in CheckApproachList!");
- }
- } else { // user
+ if(t->isUser) {
if(OnActiveRunway(t->pos)) {
landed = true;
}
//cout << "Dep list, checking " << t->plane.callsign;
double distout; // meters
- if(t->isUser) distout = dclGetHorizontalSeparation(Point3D(lon, lat, elev), Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue()));
- else distout = dclGetHorizontalSeparation(Point3D(lon, lat, elev), t->planePtr->GetPos());
+ if(t->isUser) {
+ distout = dclGetHorizontalSeparation(_geod,
+ SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue()));
+ }
//cout << " distout = " << distout << '\n';
if(t->isUser && !(t->clearedToTakeOff)) { // HACK - we use clearedToTakeOff to check if ATC already contacted with plane (and cleared take-off) or not
- if(!OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), false)) {
+ if(!OnAnyRunway(SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), false)) {
current_atcdialog->remove_entry(ident, USER_REQUEST_TAKE_OFF, TOWER);
t->clearedToTakeOff = true; // FIXME
}
RemoveAllUserDialogOptions();
//cout << "ADD A\n";
current_atcdialog->add_entry(ident, "@AP Tower, @CS @MI miles @CD of the airport for full stop@AT", "Contact tower for VFR arrival (full stop)", TOWER, (int)USER_REQUEST_VFR_ARRIVAL_FULL_STOP);
- } else {
- // Send a clear-of-airspace signal
- // TODO - implement this once we actually have departing AI traffic (currently all circuits or arrivals).
}
RemovePlane(t->plane.callsign);
} else {
// Based on the airport-id and wind get the active runway
const FGAirport* apt = fgFindAirportID(ident);
- assert(apt);
+ if (!apt) {
+ SG_LOG(SG_ATC, SG_WARN, "FGTower::DoRwyDetails: unknown ICAO:" << ident);
+ return;
+ }
+
FGRunway* runway = apt->getActiveRunwayForUsage();
activeRwy = runway->ident();
}
// move to the +l end/center of the runway
//cout << "Runway center is at " << runway._lon << ", " << runway._lat << '\n';
- Point3D origin = Point3D(runway->longitude(), runway->latitude(), aptElev);
- Point3D ref = origin;
- double tshlon, tshlat, tshr;
- double tolon, tolat, tor;
+ double tshlon = 0.0, tshlat = 0.0, tshr = 0.0;
+ double tolon = 0.0, tolat = 0.0, tor = 0.0;
rwy.length = runway->lengthM();
- geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), other_way,
- rwy.length / 2.0 - 25.0, &tshlat, &tshlon, &tshr );
- geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), runway->headingDeg(),
- rwy.length / 2.0 - 25.0, &tolat, &tolon, &tor );
-
+ geo_direct_wgs_84 ( aptElev, runway->latitude(), runway->longitude(), other_way,
+ rwy.length / 2.0 - 25.0, &tshlat, &tshlon, &tshr );
+ geo_direct_wgs_84 ( aptElev, runway->latitude(), runway->longitude(), runway->headingDeg(),
+ rwy.length / 2.0 - 25.0, &tolat, &tolon, &tor );
+
// Note - 25 meters in from the runway end is a bit of a hack to put the plane ahead of the user.
// now copy what we need out of runway into rwy
- rwy.threshold_pos = Point3D(tshlon, tshlat, aptElev);
- Point3D takeoff_end = Point3D(tolon, tolat, aptElev);
+ rwy.threshold_pos = SGGeod::fromDegM(tshlon, tshlat, aptElev);
+ SGGeod takeoff_end = SGGeod::fromDegM(tolon, tolat, aptElev);
//cout << "Threshold position = " << tshlon << ", " << tshlat << ", " << aptElev << '\n';
//cout << "Takeoff position = " << tolon << ", " << tolat << ", " << aptElev << '\n';
rwy.hdg = runway->headingDeg();
// Figure out if a given position lies on the active runway
// Might have to change when we consider more than one active rwy.
-bool FGTower::OnActiveRunway(const Point3D& pt) {
+bool FGTower::OnActiveRunway(const SGGeod& pt) {
// TODO - check that the centre calculation below isn't confused by displaced thesholds etc.
- Point3D xyc((rwy.end1ortho.x() + rwy.end2ortho.x())/2.0, (rwy.end1ortho.y() + rwy.end2ortho.y())/2.0, 0.0);
- Point3D xyp = ortho.ConvertToLocal(pt);
+ SGVec3d xyc((rwy.end1ortho.x() + rwy.end2ortho.x())/2.0, (rwy.end1ortho.y() + rwy.end2ortho.y())/2.0, 0.0);
+ SGVec3d xyp = ortho.ConvertToLocal(pt);
//cout << "Length offset = " << fabs(xyp.y() - xyc.y()) << '\n';
//cout << "Width offset = " << fabs(xyp.x() - xyc.x()) << '\n';
// Figure out if a given position lies on any runway or not
// Only call this at startup - reading the runways database is expensive and needs to be fixed!
-bool FGTower::OnAnyRunway(const Point3D& pt, bool onGround) {
+bool FGTower::OnAnyRunway(const SGGeod& pt, bool onGround) {
ATCData ad;
- double dist = current_commlist->FindClosest(lon, lat, elev, ad, TOWER, 7.0);
+ double dist = current_commlist->FindClosest(_geod, ad, TOWER, 7.0);
if(dist < 0.0) {
return(false);
}
// Sign convention - dist_out is -ve for approaching planes and +ve for departing planes
// dist_across is +ve in the pattern direction - ie a plane correctly on downwind will have a +ve dist_across
- Point3D op = ortho.ConvertToLocal(tpr->pos);
+ SGVec3d op = ortho.ConvertToLocal(tpr->pos);
//if(printout) {
//if(!tpr->isUser) cout << "Orthopos is " << op.x() << ", " << op.y() << ' ';
//cout << "opType is " << tpr->opType << '\n';
// Do the approach list first
for(twrItr = appList.begin(); twrItr != appList.end(); twrItr++) {
TowerPlaneRec* tpr = *twrItr;
- if(!(tpr->isUser)) tpr->pos = tpr->planePtr->GetPos();
- //cout << "APP: ";
CalcETA(tpr);
}
// Then the circuit list
//cout << "Circuit list size is " << circuitList.size() << '\n';
for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) {
TowerPlaneRec* tpr = *twrItr;
- if(!(tpr->isUser)) tpr->pos = tpr->planePtr->GetPos();
- //cout << "CIRC: ";
CalcETA(tpr);
}
//cout << "Done doThresholdETCCalc" << endl;
//cout << "ETA returned = " << tpr->eta << '\n';
return(tpr->eta);
}
-
-
-void FGTower::ContactAtHoldShort(const PlaneRec& plane, FGAIPlane* requestee, tower_traffic_type operation) {
- // HACK - assume that anything contacting at hold short is new for now - FIXME LATER
- TowerPlaneRec* t = new TowerPlaneRec;
- t->plane = plane;
- t->planePtr = requestee;
- t->holdShortReported = true;
- 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);
- if(next) {
- double teta = GetTrafficETA(2);
- if(teta < 150.0) {
- t->clearanceCounter = 7.0; // This reduces the delay before response to 3 secs if an immediate takeoff is reqd
- //cout << "Reducing response time to request due imminent traffic\n";
- }
- } else {
- }
-*/
- // TODO - possibly add the reduced interval to clearance when immediate back in under the new scheme
-
- holdList.push_back(t);
-
- 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(const PlaneRec& plane, FGAIPlane* ai, const tower_traffic_type& op, const 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::DeregisterAIPlane(const string& id) {
- RemovePlane(id);
-}
// Contact tower for VFR approach
// eg "Cessna Charlie Foxtrot Golf Foxtrot Sierra eight miles South of the airport for full stop with Bravo"
//cout << "NOT t\n";
t = new TowerPlaneRec;
t->isUser = true;
- t->pos.setlon(user_lon_node->getDoubleValue());
- t->pos.setlat(user_lat_node->getDoubleValue());
- t->pos.setelev(user_elev_node->getDoubleValue());
+ t->pos.setLongitudeDeg(user_lon_node->getDoubleValue());
+ t->pos.setLatitudeDeg(user_lat_node->getDoubleValue());
+ t->pos.setElevationM(user_elev_node->getDoubleValue());
} else {
//cout << "IS t\n";
// Oops - the plane is already registered with this tower - maybe we took off and flew a giant circuit without
t->plane.type = GA_SINGLE; // FIXME - Another assumption!
t->plane.callsign = usercall;
+ CalcETA(t);
t->vfrArrivalReported = true;
responseReqd = true;
current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO, TOWER);
}
-// landingType defaults to AIP_LT_UNKNOWN
-void FGTower::VFRArrivalContact(const PlaneRec& plane, FGAIPlane* requestee, const LandingType& lt) {
- //cout << "VFRArrivalContact called for plane " << plane.callsign << " at " << ident << '\n';
- // Possible hack - assume this plane is new for now - TODO - should check really
- TowerPlaneRec* t = new TowerPlaneRec;
- t->plane = plane;
- t->planePtr = requestee;
- t->landingType = lt;
- t->pos = requestee->GetPos();
-
- //cout << "Hold Short reported by " << plane.callsign << '\n';
- SG_LOG(SG_ATC, SG_BULK, "VFR arrival contact made by " << plane.callsign);
- //cout << "VFR arrival contact made by " << plane.callsign << '\n';
-
- // HACK - to get up and running I'm going to assume a staight-in final for now.
- t->opType = STRAIGHT_IN;
-
- t->vfrArrivalReported = true;
- responseReqd = true;
-
- //cout << "Before adding, appList.size = " << appList.size() << " at " << ident << '\n';
- appList.push_back(t); // Not necessarily permanent
- appListItr = appList.begin();
- //cout << "After adding, appList.size = " << appList.size() << " at " << ident << '\n';
- AddToTrafficList(t);
-}
-
void FGTower::RequestDepartureClearance(const string& ID) {
//cout << "Request Departure Clearance called...\n";
}
RemoveFromAppList(ID);
t->leg = DOWNWIND;
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 {
- // ASSERT(t->planePtr != NULL);
- t->pos = t->planePtr->GetPos();
+ t->pos.setLongitudeDeg(user_lon_node->getDoubleValue());
+ t->pos.setLatitudeDeg(user_lat_node->getDoubleValue());
+ t->pos.setElevationM(user_elev_node->getDoubleValue());
}
CalcETA(t);
AddToCircuitList(t);
RemoveFromAppList(ID);
t->leg = CLIMBOUT;
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 {
- // ASSERT(t->planePtr != NULL);
- t->pos = t->planePtr->GetPos();
+ t->pos.setLongitudeDeg(user_lon_node->getDoubleValue());
+ t->pos.setLatitudeDeg(user_lat_node->getDoubleValue());
+ t->pos.setElevationM(user_elev_node->getDoubleValue());
}
CalcETA(t);
AddToCircuitList(t);
if (rwyOccupied) {
tmp = "Ready for take-off";
} else {
- if (OnAnyRunway(Point3D(user_lon_node->getDoubleValue(),
+ if (OnAnyRunway(SGGeod::fromDegM(user_lon_node->getDoubleValue(),
user_lat_node->getDoubleValue(), 0.0),true)) {
tmp = "Request take-off clearance";
} else {
else if ( strcmp ( tag, "@MI" ) == 0 ) {
char buf[10];
//sprintf( buf, "%3.1f", tpars.miles );
- int dist_miles = (int)dclGetHorizontalSeparation(Point3D(lon, lat, elev), Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())) / 1600;
+ int dist_miles = (int)dclGetHorizontalSeparation(_geod, SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())) / 1600;
sprintf(buf, "%i", dist_miles);
strcat( &dum[0], &buf[0] );
}
}
}
else if(strcmp(tag, "@CD") == 0) { // @CD = compass direction
- double h = GetHeadingFromTo(Point3D(lon, lat, elev), Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue()));
+ double h = GetHeadingFromTo(_geod, SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue()));
while(h < 0.0) h += 360.0;
while(h > 360.0) h -= 360.0;
if(h < 22.5 || h > 337.5) {
}
string FGTower::GetATISID() {
- int hours = fgGetInt("/sim/time/utc/hour");
- int phonetic_id = current_commlist->GetCallSign(ident, hours, 0);
- return GetPhoneticIdent(phonetic_id);
+ double tstamp = atof(fgGetString("sim/time/elapsed-sec"));
+ const int minute(60); // in SI units
+ int interval = ATIS ? 60*minute : 2*minute; // AWOS updated frequently
+ int sequence = current_commlist->GetAtisSequence(ident,
+ tstamp, interval);
+
+ return GetPhoneticLetter(sequence); // the sequence letter
}
ostream& operator << (ostream& os, tower_traffic_type ttt) {
return(os << "ERROR - Unknown switch in tower_traffic_type operator << ");
}
+ostream& operator << (ostream& os, PatternLeg pl) {
+ switch(pl) {
+ case(TAKEOFF_ROLL): return(os << "TAKEOFF ROLL");
+ case(CLIMBOUT): return(os << "CLIMBOUT");
+ case(TURN1): return(os << "TURN1");
+ case(CROSSWIND): return(os << "CROSSWIND");
+ case(TURN2): return(os << "TURN2");
+ case(DOWNWIND): return(os << "DOWNWIND");
+ case(TURN3): return(os << "TURN3");
+ case(BASE): return(os << "BASE");
+ case(TURN4): return(os << "TURN4");
+ case(FINAL): return(os << "FINAL");
+ case(LANDING_ROLL): return(os << "LANDING ROLL");
+ case(LEG_UNKNOWN): return(os << "UNKNOWN");
+ }
+ return(os << "ERROR - Unknown switch in PatternLeg operator << ");
+}
+
+
+ostream& operator << (ostream& os, LandingType lt) {
+ switch(lt) {
+ case(FULL_STOP): return(os << "FULL STOP");
+ case(STOP_AND_GO): return(os << "STOP AND GO");
+ case(TOUCH_AND_GO): return(os << "TOUCH AND GO");
+ case(AIP_LT_UNKNOWN): return(os << "UNKNOWN");
+ }
+ return(os << "ERROR - Unknown switch in LandingType operator << ");
+}
+