#include "simple.hxx"
+#include <cassert>
+
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <Airports/pavement.hxx>
#include <Airports/dynamics.hxx>
#include <Airports/xmlloader.hxx>
+#include <Navaids/procedure.hxx>
+#include <Navaids/waypoint.hxx>
+#include <Navaids/PositionedBinding.hxx>
+#include <ATC/CommStation.hxx>
+
+using std::vector;
+using std::pair;
+
+using namespace flightgear;
// magic import of a helper which uses FGPositioned internals
extern char** searchAirportNamesAndIdents(const std::string& aFilter);
mRunwaysLoaded(false),
mTaxiwaysLoaded(true)
{
+ init(true); // init FGPositioned
}
FGAirportDynamics * FGAirport::getDynamics()
{
- if (_dynamics != 0) {
+ if (_dynamics) {
return _dynamics;
- } else {
- //cerr << "Trying to load dynamics for " << _id << endl;
- _dynamics = new FGAirportDynamics(this);
- XMLLoader::load(_dynamics);
-
- FGRunwayPreference rwyPrefs(this);
- XMLLoader::load(&rwyPrefs);
- _dynamics->setRwyUse(rwyPrefs);
+ }
+
+ _dynamics = new FGAirportDynamics(this);
+ XMLLoader::load(_dynamics);
- //FGSidStar SIDs(this);
- XMLLoader::load(_dynamics->getSIDs());
- }
+ FGRunwayPreference rwyPrefs(this);
+ XMLLoader::load(&rwyPrefs);
+ _dynamics->setRwyUse(rwyPrefs);
+ XMLLoader::load(_dynamics->getSIDs());
+
return _dynamics;
}
FGAirport::Runway_iterator
FGAirport::getIteratorForRunwayIdent(const string& aIdent) const
-{
+{
+ if (aIdent.empty())
+ return mRunways.end();
+
loadRunways();
string ident(aIdent);
return result;
}
+FGRunway* FGAirport::findBestRunwayForPos(const SGGeod& aPos) const
+{
+ loadRunways();
+
+ Runway_iterator it = mRunways.begin();
+ FGRunway* result = NULL;
+ double currentLowestDev = 180.0;
+
+ for (; it != mRunways.end(); ++it) {
+ double inboundCourse = SGGeodesy::courseDeg(aPos, (*it)->end());
+ double dev = inboundCourse - (*it)->headingDeg();
+ SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
+
+ dev = fabs(dev);
+ if (dev < currentLowestDev) { // new best match
+ currentLowestDev = dev;
+ result = *it;
+ }
+ } // of runway iteration
+
+ return result;
+
+}
+
bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const
{
loadRunways();
envMgr = (FGEnvironmentMgr *) globals->get_subsystem("environment");
}
- FGEnvironment stationWeather(envMgr->getEnvironment(mPosition));
+ // This forces West-facing rwys to be used in no-wind situations
+ // which is consistent with Flightgear's initial setup.
+ double hdg = 270;
+
+ if (envMgr) {
+ FGEnvironment stationWeather(envMgr->getEnvironment(mPosition));
- double windSpeed = stationWeather.get_wind_speed_kt();
- double hdg = stationWeather.get_wind_from_heading_deg();
- if (windSpeed <= 0.0) {
- hdg = 270; // This forces West-facing rwys to be used in no-wind situations
- // which is consistent with Flightgear's initial setup.
+ double windSpeed = stationWeather.get_wind_speed_kt();
+ if (windSpeed > 0.0) {
+ hdg = stationWeather.get_wind_from_heading_deg();
+ }
}
return findBestRunwayForHeading(hdg);
FGAirport* FGAirport::findByIdent(const std::string& aIdent)
{
FGPositionedRef r;
- AirportFilter filter;
+ PortsFilter filter;
r = FGPositioned::findNextWithPartialId(r, aIdent, &filter);
if (!r) {
return NULL; // we don't warn here, let the caller do that
FGAirport* FGAirport::getByIdent(const std::string& aIdent)
{
FGPositionedRef r;
- AirportFilter filter;
+ PortsFilter filter;
r = FGPositioned::findNextWithPartialId(r, aIdent, &filter);
if (!r) {
throw sg_range_exception("No such airport with ident: " + aIdent);
}
mRunwaysLoaded = true;
- loadSceneryDefintions();
+ loadSceneryDefinitions();
}
void FGAirport::loadTaxiways() const
}
}
-void FGAirport::loadSceneryDefintions() const
+void FGAirport::loadProcedures() const
+{
+ if (mProceduresLoaded) {
+ return;
+ }
+
+ mProceduresLoaded = true;
+ SGPath path;
+ if (!XMLLoader::findAirportData(ident(), "procedures", path)) {
+ SG_LOG(SG_GENERAL, SG_INFO, "no procedures data available for " << ident());
+ return;
+ }
+
+ SG_LOG(SG_GENERAL, SG_INFO, ident() << ": loading procedures from " << path.str());
+ Route::loadAirportProcedures(path, const_cast<FGAirport*>(this));
+}
+
+void FGAirport::loadSceneryDefinitions() const
{
// allow users to disable the scenery data in the short-term
// longer term, this option can probably disappear
- if (!fgGetBool("/sim/use-scenery-airport-data")) {
+ if (!fgGetBool("/sim/paths/use-custom-scenery-data")) {
return;
}
// first, let's identify the current runway
string id(aThreshold->getStringValue("rwy"));
if (!hasRunwayWithIdent(id)) {
- SG_LOG(SG_GENERAL, SG_WARN, "FGAirport::processThreshold: "
+ SG_LOG(SG_GENERAL, SG_DEBUG, "FGAirport::processThreshold: "
"found runway not defined in the global data:" << ident() << "/" << id);
return;
}
SGPropertyNode* twrNode = aRoot->getChild("tower")->getChild("twr");
double lat = twrNode->getDoubleValue("lat"),
lon = twrNode->getDoubleValue("lon"),
- elevM = twrNode->getDoubleValue("elev-m");
+ elevM = twrNode->getDoubleValue("elev-m");
+// tower elevation is AGL, not AMSL. Since we don't want to depend on the
+// scenery for a precise terrain elevation, we use the field elevation
+// (this is also what the apt.dat code does)
+ double fieldElevationM = geod().getElevationM();
+
+ _tower_location = SGGeod::fromDegM(lon, lat, fieldElevationM + elevM);
+}
+
+bool FGAirport::buildApproach(Waypt* aEnroute, STAR* aSTAR, FGRunway* aRwy, WayptVec& aRoute)
+{
+ loadProcedures();
+
+ if ((aRwy && (aRwy->airport() != this))) {
+ throw sg_exception("invalid parameters", "FGAirport::buildApproach");
+ }
+
+ if (aSTAR) {
+ bool ok = aSTAR->route(aRwy, aEnroute, aRoute);
+ if (!ok) {
+ SG_LOG(SG_GENERAL, SG_WARN, ident() << ": build approach, STAR " << aSTAR->ident()
+ << " failed to route from transition " << aEnroute->ident());
+ return false;
+ }
+ } else if (aEnroute) {
+ // no a STAR specified, just use enroute point directly
+ aRoute.push_back(aEnroute);
+ }
+
+ if (!aRwy) {
+ // no runway selected yet, but we loaded the STAR, so that's fine, we're done
+ return true;
+ }
+
+// build the approach (possibly including transition), and including the missed segment
+ vector<Approach*> aps;
+ for (unsigned int j=0; j<mApproaches.size();++j) {
+ if (mApproaches[j]->runway() == aRwy) {
+ aps.push_back(mApproaches[j]);
+ }
+ } // of approach filter by runway
+
+ if (aps.empty()) {
+ SG_LOG(SG_GENERAL, SG_INFO, ident() << "; no approaches defined for runway " << aRwy->ident());
+ // could build a fallback approach here
+ return false;
+ }
+
+ for (unsigned int k=0; k<aps.size(); ++k) {
+ if (aps[k]->route(aRoute.back(), aRoute)) {
+ return true;
+ }
+ } // of initial approach iteration
+
+ SG_LOG(SG_GENERAL, SG_INFO, ident() << ": unable to find transition to runway "
+ << aRwy->ident() << ", assume vectors");
+
+ WayptRef v(new ATCVectors(NULL, this));
+ aRoute.push_back(v);
+ return aps.front()->routeFromVectors(aRoute);
+}
+
+pair<flightgear::SID*, WayptRef>
+FGAirport::selectSID(const SGGeod& aDest, FGRunway* aRwy)
+{
+ loadProcedures();
+
+ WayptRef enroute;
+ flightgear::SID* sid = NULL;
+ double d = 1e9;
+
+ for (unsigned int i=0; i<mSIDs.size(); ++i) {
+ if (aRwy && !mSIDs[i]->isForRunway(aRwy)) {
+ continue;
+ }
+
+ WayptRef e = mSIDs[i]->findBestTransition(aDest);
+ if (!e) {
+ continue; // strange, but let's not worry about it
+ }
+
+ // assert(e->isFixedPosition());
+ double ed = SGGeodesy::distanceM(aDest, e->position());
+ if (ed < d) { // new best match
+ enroute = e;
+ d = ed;
+ sid = mSIDs[i];
+ }
+ } // of SID iteration
+
+ if (!mSIDs.empty() && !sid) {
+ SG_LOG(SG_GENERAL, SG_INFO, ident() << "selectSID, no SID found (runway="
+ << (aRwy ? aRwy->ident() : "no runway preference"));
+ }
+
+ return std::make_pair(sid, enroute);
+}
- _tower_location = SGGeod::fromDegM(lon, lat, elevM);
+pair<STAR*, WayptRef>
+FGAirport::selectSTAR(const SGGeod& aOrigin, FGRunway* aRwy)
+{
+ loadProcedures();
+
+ WayptRef enroute;
+ STAR* star = NULL;
+ double d = 1e9;
+
+ for (unsigned int i=0; i<mSTARs.size(); ++i) {
+ if (!mSTARs[i]->isForRunway(aRwy)) {
+ continue;
+ }
+
+ SG_LOG(SG_GENERAL, SG_INFO, "STAR " << mSTARs[i]->ident() << " is valid for runway");
+ WayptRef e = mSTARs[i]->findBestTransition(aOrigin);
+ if (!e) {
+ continue; // strange, but let's not worry about it
+ }
+
+ // assert(e->isFixedPosition());
+ double ed = SGGeodesy::distanceM(aOrigin, e->position());
+ if (ed < d) { // new best match
+ enroute = e;
+ d = ed;
+ star = mSTARs[i];
+ }
+ } // of STAR iteration
+
+ return std::make_pair(star, enroute);
+}
+
+
+void FGAirport::addSID(flightgear::SID* aSid)
+{
+ mSIDs.push_back(aSid);
+}
+
+void FGAirport::addSTAR(STAR* aStar)
+{
+ mSTARs.push_back(aStar);
+}
+
+void FGAirport::addApproach(Approach* aApp)
+{
+ mApproaches.push_back(aApp);
+}
+
+unsigned int FGAirport::numSIDs() const
+{
+ loadProcedures();
+ return mSIDs.size();
+}
+
+flightgear::SID* FGAirport::getSIDByIndex(unsigned int aIndex) const
+{
+ loadProcedures();
+ return mSIDs[aIndex];
+}
+
+flightgear::SID* FGAirport::findSIDWithIdent(const std::string& aIdent) const
+{
+ loadProcedures();
+ for (unsigned int i=0; i<mSIDs.size(); ++i) {
+ if (mSIDs[i]->ident() == aIdent) {
+ return mSIDs[i];
+ }
+ }
+
+ return NULL;
+}
+
+unsigned int FGAirport::numSTARs() const
+{
+ loadProcedures();
+ return mSTARs.size();
+}
+
+STAR* FGAirport::getSTARByIndex(unsigned int aIndex) const
+{
+ loadProcedures();
+ return mSTARs[aIndex];
+}
+
+STAR* FGAirport::findSTARWithIdent(const std::string& aIdent) const
+{
+ loadProcedures();
+ for (unsigned int i=0; i<mSTARs.size(); ++i) {
+ if (mSTARs[i]->ident() == aIdent) {
+ return mSTARs[i];
+ }
+ }
+
+ return NULL;
+}
+
+unsigned int FGAirport::numApproaches() const
+{
+ loadProcedures();
+ return mApproaches.size();
+}
+
+Approach* FGAirport::getApproachByIndex(unsigned int aIndex) const
+{
+ loadProcedures();
+ return mApproaches[aIndex];
+}
+
+class AirportNodeListener : public SGPropertyChangeListener
+{
+public:
+ AirportNodeListener()
+ {
+ SGPropertyNode* airports = fgGetNode("/sim/airport");
+ airports->addChangeListener(this, false);
+ }
+
+ virtual void valueChanged(SGPropertyNode*)
+ {
+ }
+
+ virtual void childAdded(SGPropertyNode* pr, SGPropertyNode* child)
+ {
+ FGAirport* apt = FGAirport::findByIdent(child->getName());
+ if (!apt) {
+ return;
+ }
+
+ flightgear::PositionedBinding::bind(apt, child);
+ }
+};
+
+void FGAirport::installPropertyListener()
+{
+ new AirportNodeListener;
+}
+
+flightgear::PositionedBinding*
+FGAirport::createBinding(SGPropertyNode* nd) const
+{
+ return new flightgear::AirportBinding(this, nd);
+}
+
+void FGAirport::setCommStations(CommStationList& comms)
+{
+ mCommStations.swap(comms);
+ for (unsigned int c=0; c<mCommStations.size(); ++c) {
+ mCommStations[c]->setAirport(this);
+ }
+}
+
+CommStationList
+FGAirport::commStationsOfType(FGPositioned::Type aTy) const
+{
+ CommStationList result;
+ for (unsigned int c=0; c<mCommStations.size(); ++c) {
+ if (mCommStations[c]->type() == aTy) {
+ result.push_back(mCommStations[c]);
+ }
+ }
+ return result;
}
// get airport elevation