// simgear
#include <simgear/props/props_io.hxx>
#include <simgear/structure/exception.hxx>
+#include <simgear/structure/event_mgr.hxx>
#include "globals.hxx"
#include "fg_props.hxx"
#include <Navaids/navlist.hxx>
#include <Airports/runways.hxx>
-#include <Airports/simple.hxx>
+#include <Airports/airport.hxx>
#include <Airports/dynamics.hxx>
#include <AIModel/AIManager.hxx>
+#include <GUI/MessageBox.hxx>
+
using std::endl;
+using std::string;
namespace flightgear
{
+
+/// to avoid blocking when metar-fetch is enabled, but the network is
+/// unresponsive, we need a timeout value. This value is reset on initPosition,
+/// and tracked through each call to finalizePosition.
+static SGTimeStamp global_finalizeTime;
+static bool global_callbackRegistered = false;
+
+static void finalizePosition();
// Set current tower position lon/lat given an airport id
static bool fgSetTowerPosFromAirportID( const string& id) {
}
-struct FGTowerLocationListener : SGPropertyChangeListener {
+class FGTowerLocationListener : public SGPropertyChangeListener {
+
void valueChanged(SGPropertyNode* node) {
string id(node->getStringValue());
if (fgGetBool("/sim/tower/auto-position",true))
}
};
-struct FGClosestTowerLocationListener : SGPropertyChangeListener
+class FGClosestTowerLocationListener : public SGPropertyChangeListener
{
void valueChanged(SGPropertyNode* )
{
};
void initTowerLocationListener() {
+
+ SGPropertyChangeListener* tll = new FGTowerLocationListener();
+ globals->addListenerToCleanup(tll);
fgGetNode("/sim/tower/airport-id", true)
- ->addChangeListener( new FGTowerLocationListener(), true );
+ ->addChangeListener( tll, true );
+
FGClosestTowerLocationListener* ntcl = new FGClosestTowerLocationListener();
+ globals->addListenerToCleanup(ntcl);
fgGetNode("/sim/airport/closest-airport-id", true)
->addChangeListener(ntcl , true );
fgGetNode("/sim/tower/auto-position", true)
}
// Set current_options lon/lat given an airport id and heading (degrees)
-bool setPosFromAirportIDandHdg( const string& id, double tgt_hdg ) {
+static bool setPosFromAirportIDandHdg( const string& id, double tgt_hdg ) {
if ( id.empty() )
return false;
const FGAirport* apt = fgFindAirportID(id);
if (!apt) return false;
- FGRunway* r = apt->findBestRunwayForHeading(tgt_hdg);
- fgSetString("/sim/atc/runway", r->ident().c_str());
- SGGeod startPos = r->pointOnCenterline(fgGetDouble("/sim/airport/runways/start-offset-m", 5.0));
- fgApplyStartOffset(startPos, r->headingDeg(), tgt_hdg);
+ SGGeod startPos;
+ double heading = tgt_hdg;
+ if (apt->type() == FGPositioned::HELIPORT) {
+ if (apt->numHelipads() > 0) {
+ startPos = apt->getHelipadByIndex(0)->geod();
+ } else {
+ startPos = apt->geod();
+ }
+ } else {
+ FGRunway* r = apt->findBestRunwayForHeading(tgt_hdg);
+ fgSetString("/sim/atc/runway", r->ident().c_str());
+ startPos = r->pointOnCenterline(fgGetDouble("/sim/airport/runways/start-offset-m", 5.0));
+ heading = r->headingDeg();
+ }
+
+ fgApplyStartOffset(startPos, heading, tgt_hdg);
return true;
}
// Set current_options lon/lat given an airport id and parkig position name
static bool fgSetPosFromAirportIDandParkpos( const string& id, const string& parkpos )
{
+ string fltType;
+ string acOperator;
+ string acType; // Currently not used by findAvailable parking, so safe to leave empty.
+ SGPath acData;
if ( id.empty() )
return false;
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find airport " << id );
return false;
}
- FGAirportDynamics* dcs = apt->getDynamics();
+ FGAirportDynamicsRef dcs = apt->getDynamics();
if (!dcs) {
SG_LOG( SG_GENERAL, SG_ALERT,
"Airport " << id << "does not appear to have parking information available");
return false;
}
- int gateID;
+ ParkingAssignment pka;
double radius = fgGetDouble("/sim/dimensions/radius-m");
if ((parkpos == string("AVAILABLE")) && (radius > 0)) {
- string fltType;
- string acOperator;
- SGPath acData;
+
try {
acData = globals->get_fg_home();
acData.append("aircraft-data");
acOperator = fgGetString("/sim/aircraft-operator" );
}
- string acType; // Currently not used by findAvailable parking, so safe to leave empty.
- gateID = dcs->getAvailableParking(radius, fltType, acType, acOperator);
- if (gateID >=0 ) {
+
+ pka = dcs->getAvailableParking(radius, fltType, acType, acOperator);
+ if (pka.isValid()) {
+ // why is the following line necessary?
fgGetString("/sim/presets/parkpos");
- fgSetString("/sim/presets/parkpos", dcs->getParking(gateID)->getName());
+ fgSetString("/sim/presets/parkpos", pka.parking()->getName());
} else {
SG_LOG( SG_GENERAL, SG_ALERT,
"Failed to find a suitable parking at airport " << id );
return false;
}
} else {
- gateID = dcs->findParkingByName(parkpos);
- if (gateID < 0) {
+
+ pka = dcs->getParkingByName(parkpos);
+ if (!pka.isValid()) {
SG_LOG( SG_GENERAL, SG_ALERT,
"Failed to find a parking at airport " << id << ":" << parkpos);
return false;
}
}
-
- FGParking* parking = dcs->getParking(gateID);
- parking->setAvailable(false);
- fgApplyStartOffset(parking->getGeod(), parking->getHeading());
+ // Why is the following line necessary?
+ fgGetString("/sim/presets/parkpos");
+ fgSetString("/sim/presets/parkpos", pka.parking()->getName());
+ // The problem is, this line doesn't work because the ParkingAssignment's refcounting mechanism:
+ // The parking will be released after this function returns.
+ // As a temporary measure, I'll try to reserve the parking via the atc_manager, which should work, because it uses the same
+ // mechanism as the AI traffic code.
+ dcs->setParkingAvailable(pka.parking(), false);
+ fgApplyStartOffset(pka.parking()->geod(), pka.parking()->getHeading());
return true;
}
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find airport:" << id);
return false;
}
-
- if (!apt->hasRunwayWithIdent(rwy)) {
- SG_LOG( SG_GENERAL, rwy_req ? SG_ALERT : SG_INFO,
- "Failed to find runway " << rwy <<
+
+ if (apt->hasRunwayWithIdent(rwy)) {
+ FGRunway* r(apt->getRunwayByIdent(rwy));
+ fgSetString("/sim/atc/runway", r->ident().c_str());
+ SGGeod startPos = r->pointOnCenterline( fgGetDouble("/sim/airport/runways/start-offset-m", 5.0));
+ fgApplyStartOffset(startPos, r->headingDeg());
+ return true;
+ } else if (apt->hasHelipadWithIdent(rwy)) {
+ FGHelipad* h(apt->getHelipadByIdent(rwy));
+ fgApplyStartOffset(h->geod(), h->headingDeg());
+ return true;
+ }
+
+ if (rwy_req) {
+ flightgear::modalMessageBox("Runway not available", "Runway/helipad "
+ + rwy + " not found at airport " + apt->getId()
+ + " - " + apt->getName() );
+ } else {
+ SG_LOG( SG_GENERAL, SG_INFO,
+ "Failed to find runway/helipad " << rwy <<
" at airport " << id << ". Using default runway." );
- return false;
}
-
- FGRunway* r(apt->getRunwayByIdent(rwy));
- fgSetString("/sim/atc/runway", r->ident().c_str());
- SGGeod startPos = r->pointOnCenterline( fgGetDouble("/sim/airport/runways/start-offset-m", 5.0));
- fgApplyStartOffset(startPos, r->headingDeg());
- return true;
+ return false;
}
// Set current_options lon/lat given an airport id and heading (degrees)
-static bool fgSetPosFromNAV( const string& id, const double& freq, FGPositioned::Type type )
+static bool fgSetPosFromNAV( const string& id,
+ const double& freq,
+ FGPositioned::Type type,
+ PositionedID guid)
{
- FGNavList::TypeFilter filter(type);
- const nav_list_type navlist = FGNavList::findByIdentAndFreq( id.c_str(), freq, &filter );
-
- if (navlist.size() == 0 ) {
- SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate NAV = "
- << id << ":" << freq );
- return false;
- }
-
- if( navlist.size() > 1 ) {
- std::ostringstream buf;
- buf << "Ambigous NAV-ID: '" << id << "'. Specify id and frequency. Available stations:" << endl;
- for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
- // NDB stored in kHz, VOR stored in MHz * 100 :-P
- double factor = (*it)->type() == FGPositioned::NDB ? 1.0 : 1/100.0;
- string unit = (*it)->type() == FGPositioned::NDB ? "kHz" : "MHz";
- buf << (*it)->ident() << " "
- << std::setprecision(5) << (double)((*it)->get_freq() * factor) << " "
- << (*it)->get_lat() << "/" << (*it)->get_lon()
- << endl;
+ FGNavRecord* nav = 0;
+
+
+ if (guid != 0) {
+ nav = FGPositioned::loadById<FGNavRecord>(guid);
+ if (!nav)
+ return false;
+ } else {
+ FGNavList::TypeFilter filter(type);
+ const nav_list_type navlist = FGNavList::findByIdentAndFreq( id.c_str(), freq, &filter );
+
+ if (navlist.empty()) {
+ SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate NAV = "
+ << id << ":" << freq );
+ return false;
+ }
+
+ if( navlist.size() > 1 ) {
+ std::ostringstream buf;
+ buf << "Ambigous NAV-ID: '" << id << "'. Specify id and frequency. Available stations:" << endl;
+ for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
+ // NDB stored in kHz, VOR stored in MHz * 100 :-P
+ double factor = (*it)->type() == FGPositioned::NDB ? 1.0 : 1/100.0;
+ string unit = (*it)->type() == FGPositioned::NDB ? "kHz" : "MHz";
+ buf << (*it)->ident() << " "
+ << std::setprecision(5) << (double)((*it)->get_freq() * factor) << " "
+ << (*it)->get_lat() << "/" << (*it)->get_lon()
+ << endl;
+ }
+
+ SG_LOG( SG_GENERAL, SG_ALERT, buf.str() );
+ return false;
+ }
+
+ // nav list must be of length 1
+ nav = navlist[0];
}
-
- SG_LOG( SG_GENERAL, SG_ALERT, buf.str() );
- return false;
- }
-
- FGNavRecord *nav = navlist[0];
- fgApplyStartOffset(nav->geod(), fgGetDouble("/sim/presets/heading-deg"));
- return true;
+
+ fgApplyStartOffset(nav->geod(), fgGetDouble("/sim/presets/heading-deg"));
+ return true;
}
// Set current_options lon/lat given an aircraft carrier id
}
}
-// Set current_options lon/lat given an airport id and heading (degrees)
-static bool fgSetPosFromFix( const string& id )
+// Set current_options lon/lat given a fix ident and GUID
+static bool fgSetPosFromFix( const string& id, PositionedID guid )
{
- FGPositioned::TypeFilter fixFilter(FGPositioned::FIX);
- FGPositioned* fix = FGPositioned::findFirstWithIdent(id, &fixFilter);
+ FGPositioned* fix = NULL;
+ if (guid != 0) {
+ fix = FGPositioned::loadById<FGPositioned>(guid);
+ } else {
+ FGPositioned::TypeFilter fixFilter(FGPositioned::FIX);
+ fix = FGPositioned::findFirstWithIdent(id, &fixFilter);
+ }
+
if (!fix) {
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate fix = " << id );
return false;
// Set the initial position based on presets (or defaults)
bool initPosition()
{
- // cout << "initPosition()" << endl;
+ global_finalizeTime = SGTimeStamp(); // reset to invalid
+ if (!global_callbackRegistered) {
+ globals->get_event_mgr()->addTask("finalizePosition", &finalizePosition, 0.1);
+ global_callbackRegistered = true;
+ }
+
double gs = fgGetDouble("/sim/presets/glideslope-deg")
* SG_DEGREES_TO_RADIANS ;
double od = fgGetDouble("/sim/presets/offset-distance-nm");
string carrier = fgGetString("/sim/presets/carrier");
string parkpos = fgGetString("/sim/presets/parkpos");
string fix = fgGetString("/sim/presets/fix");
+
+ // the launcher sets this to precisely identify a navaid
+ PositionedID navaidId = fgGetInt("/sim/presets/navaid-id");
+
SGPropertyNode *hdg_preset = fgGetNode("/sim/presets/heading-deg", true);
double hdg = hdg_preset->getDoubleValue();
if ( !set_pos && !vor.empty() ) {
// a VOR is requested
- if ( fgSetPosFromNAV( vor, vor_freq, FGPositioned::VOR ) ) {
+ if ( fgSetPosFromNAV( vor, vor_freq, FGPositioned::VOR, navaidId ) ) {
set_pos = true;
}
}
if ( !set_pos && !ndb.empty() ) {
// an NDB is requested
- if ( fgSetPosFromNAV( ndb, ndb_freq, FGPositioned::NDB ) ) {
+ if ( fgSetPosFromNAV( ndb, ndb_freq, FGPositioned::NDB, navaidId ) ) {
set_pos = true;
}
}
if ( !set_pos && !fix.empty() ) {
// a Fix is requested
- if ( fgSetPosFromFix( fix ) ) {
+ if ( fgSetPosFromFix( fix, navaidId ) ) {
set_pos = true;
}
}
fgSetBool("/sim/presets/onground", true);
}
+ fgSetBool("/sim/position-finalized", false);
+
+// Initialize the longitude, latitude and altitude to the initial position
+ fgSetDouble("/position/altitude-ft", fgGetDouble("/sim/presets/altitude-ft"));
+ fgSetDouble("/position/longitude-deg", fgGetDouble("/sim/presets/longitude-deg"));
+ fgSetDouble("/position/latitude-deg", fgGetDouble("/sim/presets/latitude-deg"));
+
+ return true;
+}
+
+bool finalizeMetar()
+{
+ if (!fgGetBool("/environment/realwx/enabled")) {
+ return true;
+ }
+
+ double hdg = fgGetDouble( "/environment/metar/base-wind-dir-deg", 9999.0 );
+ string apt = fgGetString("/sim/presets/airport-id");
+ string rwy = fgGetString("/sim/presets/runway");
+ double strthdg = fgGetDouble( "/sim/startup/options/heading-deg", 9999.0 );
+ string parkpos = fgGetString( "/sim/presets/parkpos" );
+ bool onground = fgGetBool( "/sim/presets/onground", false );
+ // this logic is taken from former startup.nas
+ bool needMetar = (hdg < 360.0) && !apt.empty() && (strthdg > 360.0) &&
+ rwy.empty() && onground && parkpos.empty();
+
+ if (needMetar) {
+ // timeout so we don't spin forever if the network is down
+ if (global_finalizeTime.elapsedMSec() > fgGetInt("/sim/startup/metar-fetch-timeout-msec", 6000)) {
+ SG_LOG(SG_GENERAL, SG_WARN, "finalizePosition: timed out waiting for METAR fetch");
+ return true;
+ }
+
+ if (fgGetBool( "/environment/metar/failure" )) {
+ SG_LOG(SG_ENVIRONMENT, SG_INFO, "metar download failed, not waiting");
+ return true;
+ }
+
+ if (!fgGetBool( "/environment/metar/valid" )) {
+ return false;
+ }
+
+ SG_LOG(SG_ENVIRONMENT, SG_INFO,
+ "Using METAR for runway selection: '" << fgGetString("/environment/metar/data") << "'" );
+ setPosFromAirportIDandHdg( apt, hdg );
+ // fall through to return true
+ } // of need-metar case
+
return true;
}
+void finalizePosition()
+{
+ // first call to finalize after an initPosition call
+ if (global_finalizeTime.get_usec() == 0) {
+ global_finalizeTime = SGTimeStamp::now();
+ }
+
+ bool done = true;
+
+ /* Scenarios require Nasal, so FGAIManager loads the scenarios,
+ * including its models such as a/c carriers, in its 'postinit',
+ * which is the very last thing we do.
+ * flightgear::initPosition is called very early in main.cxx/fgIdleFunction,
+ * one of the first things we do, long before scenarios/carriers are
+ * loaded. => When requested "initial preset position" relates to a
+ * carrier, recalculate the 'initial' position here
+ */
+ std::string carrier = fgGetString("/sim/presets/carrier","");
+ if (!carrier.empty())
+ {
+ SG_LOG(SG_GENERAL, SG_INFO, "finalizePositioned: re-init-ing position on carrier");
+ // clear preset location and re-trigger position setup
+ fgSetDouble("/sim/presets/longitude-deg", 9999);
+ fgSetDouble("/sim/presets/latitude-deg", 9999);
+ initPosition();
+ } else {
+ done = finalizeMetar();
+ }
+
+ fgSetBool("/sim/position-finalized", done);
+ if (done) {
+ globals->get_event_mgr()->removeTask("finalizePosition");
+ global_callbackRegistered = false;
+ }
+}
+
} // of namespace flightgear