2012-09-18 19:50:28 +00:00
|
|
|
// positioninit.cxx - helpers relating to setting initial aircraft position
|
|
|
|
//
|
|
|
|
// Copyright (C) 2012 James Turner zakalawe@mac.com
|
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License as
|
|
|
|
// published by the Free Software Foundation; either version 2 of the
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful, but
|
|
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program; if not, write to the Free Software
|
|
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
2017-09-24 16:22:45 +00:00
|
|
|
#include "config.h"
|
2012-09-18 19:50:28 +00:00
|
|
|
|
2017-09-24 16:22:45 +00:00
|
|
|
#include "positioninit.hxx"
|
2012-09-18 19:50:28 +00:00
|
|
|
|
2017-09-08 21:31:22 +00:00
|
|
|
#include <osgViewer/Viewer>
|
|
|
|
#include <osg/PagedLOD>
|
2016-07-17 20:11:14 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
// simgear
|
2020-04-18 21:00:04 +00:00
|
|
|
#include <simgear/misc/strutils.hxx>
|
2012-09-18 19:50:28 +00:00
|
|
|
#include <simgear/props/props_io.hxx>
|
2012-09-23 20:42:40 +00:00
|
|
|
#include <simgear/structure/exception.hxx>
|
2012-12-15 15:25:45 +00:00
|
|
|
#include <simgear/structure/event_mgr.hxx>
|
2017-09-08 21:31:22 +00:00
|
|
|
#include <simgear/scene/model/CheckSceneryVisitor.hxx>
|
|
|
|
#include <simgear/scene/util/OsgMath.hxx>
|
2012-09-18 19:50:28 +00:00
|
|
|
|
|
|
|
#include "globals.hxx"
|
|
|
|
#include "fg_props.hxx"
|
2019-01-08 23:20:51 +00:00
|
|
|
#include "fg_io.hxx"
|
2012-09-18 19:50:28 +00:00
|
|
|
|
|
|
|
#include <Navaids/navlist.hxx>
|
|
|
|
#include <Airports/runways.hxx>
|
2013-02-21 11:32:02 +00:00
|
|
|
#include <Airports/airport.hxx>
|
2012-09-18 19:50:28 +00:00
|
|
|
#include <Airports/dynamics.hxx>
|
2016-11-13 21:20:40 +00:00
|
|
|
#include <Airports/groundnetwork.hxx>
|
2020-05-19 17:00:34 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
#include <AIModel/AIManager.hxx>
|
2017-09-08 21:31:22 +00:00
|
|
|
#include <AIModel/AICarrier.hxx>
|
2020-05-19 17:00:34 +00:00
|
|
|
#include <AIModel/AIAircraft.hxx>
|
|
|
|
#include <AIModel/AIFlightPlan.hxx>
|
2014-02-14 18:25:37 +00:00
|
|
|
|
2017-09-08 21:31:22 +00:00
|
|
|
#include <Scenery/scenery.hxx>
|
|
|
|
#include <GUI/MessageBox.hxx>
|
|
|
|
#include <Viewer/renderer.hxx>
|
2012-09-18 19:50:28 +00:00
|
|
|
|
|
|
|
using std::endl;
|
2013-03-28 16:49:52 +00:00
|
|
|
using std::string;
|
2012-09-18 19:50:28 +00:00
|
|
|
|
2020-03-18 16:34:02 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
namespace flightgear
|
|
|
|
{
|
2020-04-03 19:37:37 +00:00
|
|
|
|
|
|
|
|
2017-09-08 21:31:22 +00:00
|
|
|
enum InitPosResult {
|
|
|
|
ExactPosition,
|
|
|
|
VicinityPosition,
|
|
|
|
ContinueWaiting,
|
|
|
|
Failure
|
|
|
|
};
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-12-09 19:41:31 +00:00
|
|
|
/// 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;
|
2012-12-15 15:25:45 +00:00
|
|
|
static bool global_callbackRegistered = false;
|
|
|
|
|
2020-05-19 17:00:34 +00:00
|
|
|
void finalizePosition();
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2020-03-18 16:34:02 +00:00
|
|
|
namespace { // annonymous namepsace to avoid warnings about inline classes
|
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
// Set current tower position lon/lat given an airport id
|
2020-03-18 16:34:02 +00:00
|
|
|
bool fgSetTowerPosFromAirportID( const string& id)
|
|
|
|
{
|
2012-09-18 19:50:28 +00:00
|
|
|
const FGAirport *a = fgFindAirportID( id);
|
|
|
|
if (a) {
|
|
|
|
SGGeod tower = a->getTowerLocation();
|
|
|
|
fgSetDouble("/sim/tower/longitude-deg", tower.getLongitudeDeg());
|
|
|
|
fgSetDouble("/sim/tower/latitude-deg", tower.getLatitudeDeg());
|
|
|
|
fgSetDouble("/sim/tower/altitude-ft", tower.getElevationFt());
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-04 09:11:26 +00:00
|
|
|
class FGTowerLocationListener : public SGPropertyChangeListener {
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2020-03-18 16:34:02 +00:00
|
|
|
void valueChanged(SGPropertyNode* node) override
|
|
|
|
{
|
2012-09-18 19:50:28 +00:00
|
|
|
string id(node->getStringValue());
|
|
|
|
if (fgGetBool("/sim/tower/auto-position",true))
|
|
|
|
{
|
|
|
|
// enforce using closest airport when auto-positioning is enabled
|
|
|
|
const char* closest_airport = fgGetString("/sim/airport/closest-airport-id", "");
|
|
|
|
if (closest_airport && (id != closest_airport))
|
|
|
|
{
|
|
|
|
id = closest_airport;
|
|
|
|
node->setStringValue(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fgSetTowerPosFromAirportID(id);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-12-04 09:11:26 +00:00
|
|
|
class FGClosestTowerLocationListener : public SGPropertyChangeListener
|
2012-09-18 19:50:28 +00:00
|
|
|
{
|
|
|
|
void valueChanged(SGPropertyNode* )
|
|
|
|
{
|
|
|
|
// closest airport has changed
|
|
|
|
if (fgGetBool("/sim/tower/auto-position",true))
|
|
|
|
{
|
|
|
|
// update tower position
|
|
|
|
const char* id = fgGetString("/sim/airport/closest-airport-id", "");
|
|
|
|
if (id && *id!=0)
|
|
|
|
fgSetString("/sim/tower/airport-id", id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-03-18 16:34:02 +00:00
|
|
|
} // of anonymous namespace
|
|
|
|
|
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
void initTowerLocationListener() {
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2013-12-04 09:11:26 +00:00
|
|
|
SGPropertyChangeListener* tll = new FGTowerLocationListener();
|
|
|
|
globals->addListenerToCleanup(tll);
|
2012-09-18 19:50:28 +00:00
|
|
|
fgGetNode("/sim/tower/airport-id", true)
|
2013-12-04 09:11:26 +00:00
|
|
|
->addChangeListener( tll, true );
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
FGClosestTowerLocationListener* ntcl = new FGClosestTowerLocationListener();
|
2013-12-04 09:11:26 +00:00
|
|
|
globals->addListenerToCleanup(ntcl);
|
2012-09-18 19:50:28 +00:00
|
|
|
fgGetNode("/sim/airport/closest-airport-id", true)
|
|
|
|
->addChangeListener(ntcl , true );
|
|
|
|
fgGetNode("/sim/tower/auto-position", true)
|
|
|
|
->addChangeListener(ntcl, true );
|
|
|
|
}
|
|
|
|
|
2016-07-16 10:45:30 +00:00
|
|
|
static void setInitialPosition(const SGGeod& aPos, double aHeadingDeg)
|
|
|
|
{
|
|
|
|
// presets
|
|
|
|
fgSetDouble("/sim/presets/longitude-deg", aPos.getLongitudeDeg() );
|
|
|
|
fgSetDouble("/sim/presets/latitude-deg", aPos.getLatitudeDeg() );
|
|
|
|
fgSetDouble("/sim/presets/heading-deg", aHeadingDeg );
|
|
|
|
|
|
|
|
// other code depends on the actual values being set ...
|
|
|
|
fgSetDouble("/position/longitude-deg", aPos.getLongitudeDeg() );
|
|
|
|
fgSetDouble("/position/latitude-deg", aPos.getLatitudeDeg() );
|
|
|
|
fgSetDouble("/orientation/heading-deg", aHeadingDeg );
|
|
|
|
}
|
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
static void fgApplyStartOffset(const SGGeod& aStartPos, double aHeading, double aTargetHeading = HUGE_VAL)
|
|
|
|
{
|
|
|
|
SGGeod startPos(aStartPos);
|
|
|
|
if (aTargetHeading == HUGE_VAL) {
|
|
|
|
aTargetHeading = aHeading;
|
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
if ( fabs( fgGetDouble("/sim/presets/offset-distance-nm") ) > SG_EPSILON ) {
|
|
|
|
double offsetDistance = fgGetDouble("/sim/presets/offset-distance-nm");
|
|
|
|
offsetDistance *= SG_NM_TO_METER;
|
|
|
|
double offsetAzimuth = aHeading;
|
|
|
|
if ( fabs(fgGetDouble("/sim/presets/offset-azimuth-deg")) > SG_EPSILON ) {
|
|
|
|
offsetAzimuth = fgGetDouble("/sim/presets/offset-azimuth-deg");
|
|
|
|
aHeading = aTargetHeading;
|
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2020-03-18 16:34:02 +00:00
|
|
|
startPos = SGGeodesy::direct(startPos, offsetAzimuth + 180, offsetDistance);
|
2012-09-18 19:50:28 +00:00
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2016-07-16 10:45:30 +00:00
|
|
|
setInitialPosition(startPos, aHeading);
|
2012-09-18 19:50:28 +00:00
|
|
|
}
|
|
|
|
|
2017-09-24 16:22:45 +00:00
|
|
|
std::tuple<SGGeod, double> runwayStartPos(FGRunwayRef runway)
|
2016-07-17 20:11:14 +00:00
|
|
|
{
|
|
|
|
fgSetString("/sim/atc/runway", runway->ident().c_str());
|
2016-09-07 21:51:08 +00:00
|
|
|
double offsetNm = fgGetDouble("/sim/presets/offset-distance-nm");
|
2016-11-13 21:20:40 +00:00
|
|
|
double startOffset = fgGetDouble("/sim/airport/runways/start-offset-m", 5.0);
|
|
|
|
SGGeod pos = runway->pointOnCenterline(startOffset);
|
2016-07-17 20:11:14 +00:00
|
|
|
|
2020-04-09 14:48:03 +00:00
|
|
|
const bool overrideHoldShort = fgGetBool("/sim/presets/mp-hold-short-override", false);
|
|
|
|
|
|
|
|
if (!overrideHoldShort && FGIO::isMultiplayerRequested() && (fabs(offsetNm) <0.1)) {
|
2020-09-27 09:19:45 +00:00
|
|
|
SG_LOG( SG_GENERAL, SG_MANDATORY_INFO, "Requested to start on " << runway->airport()->ident() << "/" <<
|
2016-07-17 20:11:14 +00:00
|
|
|
runway->ident() << ", MP is enabled so computing hold short position to avoid runway incursion");
|
|
|
|
|
2016-11-13 21:20:40 +00:00
|
|
|
FGGroundNetwork* groundNet = runway->airport()->groundNetwork();
|
2020-11-02 10:52:32 +00:00
|
|
|
|
2020-10-27 22:34:15 +00:00
|
|
|
if (groundNet) {
|
|
|
|
// add a margin, try to keep the entire aeroplane comfortable off the
|
|
|
|
// runway.
|
|
|
|
double margin = startOffset + (runway->widthM() * 1.5);
|
|
|
|
FGTaxiNodeRef taxiNode = groundNet->findNearestNodeOffRunway(pos, runway, margin);
|
|
|
|
if (taxiNode) {
|
|
|
|
// set this so multiplayer.nas can inform the user
|
|
|
|
fgSetBool("/sim/presets/avoided-mp-runway", true);
|
|
|
|
return std::make_tuple(taxiNode->geod(), SGGeodesy::courseDeg(taxiNode->geod(), pos));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// if we couldn't find a suitable taxi-node, give up. Guessing a position
|
|
|
|
// causes too much pain (starting in the water or similar bad things)
|
2020-11-02 10:52:32 +00:00
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Unable to position off runway because groundnet has no taxi node.");
|
2020-10-27 22:34:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2020-11-02 10:52:32 +00:00
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Unable to position off runway because no groundnet.");
|
2016-11-13 21:20:40 +00:00
|
|
|
}
|
2016-07-17 20:11:14 +00:00
|
|
|
}
|
|
|
|
|
2017-09-24 16:22:45 +00:00
|
|
|
return std::make_tuple(pos, runway->headingDeg());
|
2016-07-17 20:11:14 +00:00
|
|
|
}
|
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
// Set current_options lon/lat given an airport id and heading (degrees)
|
2012-12-09 19:41:31 +00:00
|
|
|
static bool setPosFromAirportIDandHdg( const string& id, double tgt_hdg ) {
|
2012-09-18 19:50:28 +00:00
|
|
|
if ( id.empty() )
|
|
|
|
return false;
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
// set initial position from runway and heading
|
|
|
|
SG_LOG( SG_GENERAL, SG_INFO,
|
|
|
|
"Attempting to set starting position from airport code "
|
|
|
|
<< id << " heading " << tgt_hdg );
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
const FGAirport* apt = fgFindAirportID(id);
|
|
|
|
if (!apt) return false;
|
2016-07-17 20:11:14 +00:00
|
|
|
|
2013-03-05 13:19:10 +00:00
|
|
|
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);
|
2017-09-24 16:22:45 +00:00
|
|
|
std::tie(startPos, heading) = runwayStartPos(r);
|
2013-03-05 13:19:10 +00:00
|
|
|
}
|
|
|
|
|
2016-09-07 21:51:08 +00:00
|
|
|
fgApplyStartOffset(startPos, heading);
|
|
|
|
return true;
|
2012-09-18 19:50:28 +00:00
|
|
|
}
|
|
|
|
|
2020-05-19 17:00:34 +00:00
|
|
|
static bool airportParkingSetVicinity(const string& id)
|
2012-09-23 20:42:40 +00:00
|
|
|
{
|
2020-05-19 17:00:34 +00:00
|
|
|
const FGAirport* apt = fgFindAirportID(id);
|
|
|
|
if (!apt) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find airport " << id );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
setInitialPosition(apt->geod(), 0.0);
|
|
|
|
return true;
|
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2020-05-19 17:00:34 +00:00
|
|
|
// Set current_options lon/lat given an airport id and parkig position name
|
|
|
|
static bool finalizePositionForParkpos( const string& id, const string& parkpos )
|
|
|
|
{
|
|
|
|
auto aiManager = globals->get_subsystem<FGAIManager>();
|
|
|
|
if (!aiManager || !aiManager->getUserAircraft()) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "finalizePositionForParkpos: >> failed to find AI manager / user aircraft");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto userAIFP = aiManager->getUserAircraft()->GetFlightPlan();
|
|
|
|
if (!userAIFP) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "finalizePositionForParkpos: >> failed to find user aircraft AI flight-plan");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto pkr = userAIFP->getParkingGate();
|
|
|
|
if (!pkr) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT,
|
|
|
|
"Failed to find a parking at airport " << id << ":" << parkpos);
|
|
|
|
return false;
|
|
|
|
}
|
2019-04-09 03:21:51 +00:00
|
|
|
|
|
|
|
fgSetString("/sim/presets/parkpos", pkr->getName());
|
|
|
|
fgApplyStartOffset(pkr->geod(), pkr->getHeading());
|
2012-09-18 19:50:28 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Set current_options lon/lat given an airport id and runway number
|
|
|
|
static bool fgSetPosFromAirportIDandRwy( const string& id, const string& rwy, bool rwy_req ) {
|
|
|
|
if ( id.empty() )
|
|
|
|
return false;
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
// set initial position from airport and runway number
|
|
|
|
SG_LOG( SG_GENERAL, SG_INFO,
|
|
|
|
"Attempting to set starting position for "
|
|
|
|
<< id << ":" << rwy );
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
const FGAirport* apt = fgFindAirportID(id);
|
|
|
|
if (!apt) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find airport:" << id);
|
|
|
|
return false;
|
|
|
|
}
|
2014-02-14 18:25:37 +00:00
|
|
|
|
|
|
|
if (apt->hasRunwayWithIdent(rwy)) {
|
|
|
|
FGRunway* r(apt->getRunwayByIdent(rwy));
|
2016-07-17 20:11:14 +00:00
|
|
|
SGGeod startPos;
|
|
|
|
double heading;
|
2017-09-24 16:22:45 +00:00
|
|
|
std::tie(startPos, heading) = runwayStartPos(r);
|
2016-09-07 21:51:08 +00:00
|
|
|
fgApplyStartOffset(startPos, heading);
|
2014-02-14 18:25:37 +00:00
|
|
|
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 <<
|
2012-09-18 19:50:28 +00:00
|
|
|
" at airport " << id << ". Using default runway." );
|
|
|
|
}
|
2014-02-14 18:25:37 +00:00
|
|
|
return false;
|
2012-09-18 19:50:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-18 16:34:02 +00:00
|
|
|
static void fgSetDistOrAltFromGlideSlope()
|
|
|
|
{
|
2012-09-18 19:50:28 +00:00
|
|
|
string apt_id = fgGetString("/sim/presets/airport-id");
|
2020-03-18 16:34:02 +00:00
|
|
|
double gs = SGMiscd::deg2rad(fgGetDouble("/sim/presets/glideslope-deg"));
|
2012-09-18 19:50:28 +00:00
|
|
|
double od = fgGetDouble("/sim/presets/offset-distance-nm");
|
|
|
|
double alt = fgGetDouble("/sim/presets/altitude-ft");
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
double apt_elev = 0.0;
|
|
|
|
if ( ! apt_id.empty() ) {
|
|
|
|
apt_elev = fgGetAirportElev( apt_id );
|
|
|
|
if ( apt_elev < -9990.0 ) {
|
|
|
|
apt_elev = 0.0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
apt_elev = 0.0;
|
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
if( fabs(gs) > 0.01 && fabs(od) > 0.1 && alt < -9990 ) {
|
|
|
|
// set altitude from glideslope and offset-distance
|
|
|
|
od *= SG_NM_TO_METER * SG_METER_TO_FEET;
|
|
|
|
alt = fabs(od*tan(gs)) + apt_elev;
|
|
|
|
fgSetDouble("/sim/presets/altitude-ft", alt);
|
|
|
|
fgSetBool("/sim/presets/onground", false);
|
|
|
|
SG_LOG( SG_GENERAL, SG_INFO, "Calculated altitude as: "
|
|
|
|
<< alt << " ft" );
|
|
|
|
} else if( fabs(gs) > 0.01 && alt > 0 && fabs(od) < 0.1) {
|
|
|
|
// set offset-distance from glideslope and altitude
|
|
|
|
od = (alt - apt_elev) / tan(gs);
|
|
|
|
od *= -1*SG_FEET_TO_METER * SG_METER_TO_NM;
|
|
|
|
fgSetDouble("/sim/presets/offset-distance-nm", od);
|
|
|
|
fgSetBool("/sim/presets/onground", false);
|
|
|
|
SG_LOG( SG_GENERAL, SG_INFO, "Calculated offset distance as: "
|
|
|
|
<< od << " nm" );
|
|
|
|
} else if( fabs(gs) > 0.01 ) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT,
|
|
|
|
"Glideslope given but not altitude or offset-distance." );
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Resetting glideslope to zero" );
|
|
|
|
fgSetDouble("/sim/presets/glideslope-deg", 0);
|
|
|
|
fgSetBool("/sim/presets/onground", true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-04-12 20:45:20 +00:00
|
|
|
// Set current_options lon/lat given a Nav ID or GUID
|
2015-11-10 22:54:57 +00:00
|
|
|
static bool fgSetPosFromNAV( const string& id,
|
|
|
|
const double& freq,
|
|
|
|
FGPositioned::Type type,
|
|
|
|
PositionedID guid)
|
2012-08-27 23:26:36 +00:00
|
|
|
{
|
2020-03-18 16:34:02 +00:00
|
|
|
FGNavRecordRef nav;
|
2015-11-10 22:54:57 +00:00
|
|
|
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;
|
2020-03-18 16:34:02 +00:00
|
|
|
for( const auto& nav : navlist ) {
|
2015-11-10 22:54:57 +00:00
|
|
|
// NDB stored in kHz, VOR stored in MHz * 100 :-P
|
2020-03-18 16:34:02 +00:00
|
|
|
double factor = nav->type() == FGPositioned::NDB ? 1.0 : 1/100.0;
|
|
|
|
string unit = nav->type() == FGPositioned::NDB ? "kHz" : "MHz";
|
|
|
|
buf << nav->ident() << " "
|
|
|
|
<< std::setprecision(5) << static_cast<double>(nav->get_freq() * factor) << " "
|
|
|
|
<< nav->get_lat() << "/" << nav->get_lon()
|
|
|
|
<< endl;
|
2015-11-10 22:54:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, buf.str() );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// nav list must be of length 1
|
|
|
|
nav = navlist[0];
|
2012-09-18 19:50:28 +00:00
|
|
|
}
|
2015-11-10 22:54:57 +00:00
|
|
|
|
|
|
|
fgApplyStartOffset(nav->geod(), fgGetDouble("/sim/presets/heading-deg"));
|
|
|
|
return true;
|
2012-09-18 19:50:28 +00:00
|
|
|
}
|
|
|
|
|
2017-09-08 21:31:22 +00:00
|
|
|
static InitPosResult setInitialPosFromCarrier( const string& carrier )
|
2017-04-05 01:01:50 +00:00
|
|
|
{
|
2017-09-09 18:03:53 +00:00
|
|
|
const auto initialPos = FGAICarrier::initialPositionForCarrier(carrier);
|
|
|
|
if (initialPos.first) {
|
|
|
|
// set these so scenery system has a vicinity to work with, and
|
|
|
|
// so our PagedLOD is loaded
|
|
|
|
fgSetDouble("/sim/presets/longitude-deg", initialPos.second.getLongitudeDeg());
|
|
|
|
fgSetDouble("/sim/presets/latitude-deg", initialPos.second.getLatitudeDeg());
|
2020-03-18 16:34:02 +00:00
|
|
|
SG_LOG( SG_GENERAL, SG_DEBUG, "Initial carrier pos = " << initialPos.second );
|
2017-09-09 18:03:53 +00:00
|
|
|
return VicinityPosition;
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-09-09 18:03:53 +00:00
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate aircraft carrier = " << carrier );
|
|
|
|
return Failure;
|
2017-09-08 21:31:22 +00:00
|
|
|
}
|
2017-09-09 18:03:53 +00:00
|
|
|
|
2020-03-18 16:34:02 +00:00
|
|
|
static InitPosResult checkCarrierSceneryLoaded(const SGSharedPtr<FGAICarrier> carrierRef)
|
|
|
|
{
|
|
|
|
SGVec3d cartPos = carrierRef->getCartPos();
|
CompositeViewer: Support for multiple view windows using osgViewer::CompositeViewer.
Overview:
Previously Flightgear always used a single osgViewer::Viewer(), which
inherits from both osgViewer::ViewerBase and osgViewer::View, giving a
single view window.
If CompositeViewer is enabled, we instead use a osgViewer::CompositeViewer
which contains a list of osgViewer::View's. Each of these View's can have
its own eye position, so we can have multiple different views of the same
scene.
Enable at runtime with: --composite-viewer=1
Changes to allow use of osgViewer::CompositeViewer:
Previously FGRenderer had this method:
osgViewer::Viewer* getViewer();
This has been replaced by these two new methods:
osgViewer::ViewerBase* getViewerBase();
osgViewer::View* getView();
If CompositeViewer is not enabled (the default), the actual runtime state
is unchanged, and getViewerBase() and getView() both return a pointer to
the singleton osgViewer::Viewer() object.
If CompositeViewer is enabled, getViewerBase() returns a pointer to a
singleton osgViewer::CompositeViewer object, and getView() returns a
pointer to the first osgViewer::View in the osgViewer::CompositeViewer's
list.
The other significant change to FGRenderer() is the new method:
osg::FrameStamp* getFrameStamp()
If CompositeViewer is not enabled, this simply returns
getView()->getFrameStamp(). If CompositeViewer is enabled it returns
getViewerBase()->getFrameStamp(). It is important that code that previously
called getView()->getFrameStamp() is changed to use the new method, because
when CompositeViewer is enabled individual osgViewer::View's appear to
return an osg::FrameStamp with zero frame number).
All code that uses FGRenderer has been patched up to use the new methods so
that things work as before regardless of whether CompositeViewer is enabled
or not.
We make FGRenderer::update() call SviewUpdate() which updates any extra
views.
Extra view windows:
If CompositeViewer is enabled, one can create top-level extra view windows
by calling SviewCreate(). See src/Viewer/sview.hxx for details.
Each extra view window has its own simgear::compositor::Compositor
instance.
Currently SviewCreate() can create extra view windows that clone the
current view, or view from one point to another (e.g. from one multiplayer
aircraft to the user's aircradt) or keep two aircraft in view, one at a
fixed distance in the foreground.
SviewCreate() can be called from nasal via new nasal commands "view-clone",
"view-last-pair", "view-last-pair-double" and "view-push". Associated
changes to fgdata gives access to these via the View menu. The "view-push"
command tags the current view for later use by "view-last-pair" and
"view-last-pair-double".
Extra view windows created by SviewCreate() use a new view system called
Sview, which allows views to be constructed at runtime instead of being
hard-coded in *-set.xml files. This is work in progress and views aren't
all fully implemented. For example Pilot view gets things slightly wrong
with large roll values, Tower View AGL is not implemented, and we don't
implement damping. See top of src/Viewer/sview.cxx for an overview.
OpenSceneGraph-3.4 issues:
OSG-3.4's event handling seems to be incorrect with CompositeViewer -
events get sent for the wrong window which causes issues with resize and
closing. It doesn't seem to be possible to work around this, so closing
extra view windows can end up closing the main window for example.
OSG-3.6 seems to fix the problems.
We warn if CompositeViewer is enabled and OpenSceneGraph is 3.4.
2020-11-16 18:43:46 +00:00
|
|
|
auto framestamp = globals->get_renderer()->getFrameStamp();
|
2020-03-18 16:34:02 +00:00
|
|
|
simgear::CheckSceneryVisitor csnv(globals->get_scenery()->getPager(),
|
|
|
|
toOsg(cartPos),
|
|
|
|
100.0 /* range in metres */,
|
|
|
|
framestamp);
|
|
|
|
|
|
|
|
// currently the PagedLODs will not be loaded by the DatabasePager
|
|
|
|
// while the splashscreen is there, so CheckSceneryVisitor force-loads
|
|
|
|
// missing objects in the main thread
|
|
|
|
carrierRef->getSceneBranch()->accept(csnv);
|
|
|
|
if (!csnv.isLoaded()) {
|
|
|
|
return ContinueWaiting;
|
|
|
|
}
|
|
|
|
|
|
|
|
// and then wait for the load to actually be synced to the main thread
|
|
|
|
if (carrierRef->getSceneBranch()->getNumChildren() < 1) {
|
|
|
|
return ContinueWaiting;
|
|
|
|
}
|
|
|
|
|
|
|
|
return VicinityPosition;
|
|
|
|
}
|
|
|
|
|
2017-09-08 21:31:22 +00:00
|
|
|
// Set current_options lon/lat given an aircraft carrier id
|
|
|
|
static InitPosResult setFinalPosFromCarrier( const string& carrier, const string& posid )
|
|
|
|
{
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-09-09 18:03:53 +00:00
|
|
|
SGSharedPtr<FGAICarrier> carrierRef = FGAICarrier::findCarrierByNameOrPennant(carrier);
|
|
|
|
if (!carrierRef) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate aircraft carrier = "
|
|
|
|
<< carrier );
|
|
|
|
return Failure;
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2020-03-18 16:34:02 +00:00
|
|
|
auto res = checkCarrierSceneryLoaded(carrierRef);
|
|
|
|
if (res != VicinityPosition) {
|
|
|
|
return res; // either failrue or keep waiting for scenery load
|
2017-09-09 18:03:53 +00:00
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-09-09 18:03:53 +00:00
|
|
|
SGGeod geodPos;
|
|
|
|
double heading;
|
|
|
|
SGVec3d uvw;
|
|
|
|
if (carrierRef->getParkPosition(posid, geodPos, heading, uvw)) {
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-09-09 18:03:53 +00:00
|
|
|
////////
|
|
|
|
double lon = geodPos.getLongitudeDeg();
|
|
|
|
double lat = geodPos.getLatitudeDeg();
|
|
|
|
double alt = geodPos.getElevationFt() + 2.0;
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-09-09 18:03:53 +00:00
|
|
|
SG_LOG( SG_GENERAL, SG_INFO, "Attempting to set starting position for "
|
|
|
|
<< carrier << " at lat = " << lat << ", lon = " << lon
|
|
|
|
<< ", alt = " << alt << ", heading = " << heading);
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-09-09 18:03:53 +00:00
|
|
|
fgSetDouble("/sim/presets/longitude-deg", lon);
|
|
|
|
fgSetDouble("/sim/presets/latitude-deg", lat);
|
|
|
|
fgSetDouble("/sim/presets/altitude-ft", alt);
|
|
|
|
fgSetDouble("/sim/presets/heading-deg", heading);
|
|
|
|
fgSetDouble("/position/longitude-deg", lon);
|
|
|
|
fgSetDouble("/position/latitude-deg", lat);
|
|
|
|
fgSetDouble("/position/altitude-ft", alt);
|
|
|
|
fgSetDouble("/orientation/heading-deg", heading);
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-09-09 18:03:53 +00:00
|
|
|
fgSetString("/sim/presets/speed-set", "UVW");
|
|
|
|
fgSetDouble("/velocities/uBody-fps", uvw(0));
|
|
|
|
fgSetDouble("/velocities/vBody-fps", uvw(1));
|
|
|
|
fgSetDouble("/velocities/wBody-fps", uvw(2));
|
|
|
|
fgSetDouble("/sim/presets/uBody-fps", uvw(0));
|
|
|
|
fgSetDouble("/sim/presets/vBody-fps", uvw(1));
|
|
|
|
fgSetDouble("/sim/presets/wBody-fps", uvw(2));
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-09-09 18:03:53 +00:00
|
|
|
fgSetBool("/sim/presets/onground", true);
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-09-09 18:03:53 +00:00
|
|
|
/////////
|
|
|
|
return ExactPosition;
|
|
|
|
}
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate aircraft carrier = " << carrier );
|
|
|
|
return Failure;
|
2012-09-18 19:50:28 +00:00
|
|
|
}
|
|
|
|
|
2020-04-05 22:13:32 +00:00
|
|
|
static InitPosResult setFinalPosFromCarrierFLOLS(const string& carrier, bool abeam)
|
2020-03-18 16:34:02 +00:00
|
|
|
{
|
|
|
|
SGSharedPtr<FGAICarrier> carrierRef = FGAICarrier::findCarrierByNameOrPennant(carrier);
|
|
|
|
if (!carrierRef) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate aircraft carrier = "
|
|
|
|
<< carrier );
|
|
|
|
return Failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto res = checkCarrierSceneryLoaded(carrierRef);
|
|
|
|
if (res != VicinityPosition) {
|
2020-04-12 20:45:20 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_DEBUG, "carrier scenery not yet loaded");
|
2020-03-18 16:34:02 +00:00
|
|
|
return res; // either failure or keep waiting for scenery load
|
|
|
|
}
|
|
|
|
|
|
|
|
SGGeod flolsPosition;
|
|
|
|
double headingToFLOLS;
|
|
|
|
if (!carrierRef->getFLOLSPositionHeading(flolsPosition, headingToFLOLS)) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Unable to compiute FLOLS position for carrier = "
|
|
|
|
<< carrier );
|
|
|
|
return Failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto flolsElevationFt = flolsPosition.getElevationFt();
|
|
|
|
double gs = SGMiscd::deg2rad(carrierRef->getFLOLFSGlidepathAngleDeg());
|
|
|
|
const double od = fgGetDouble("/sim/presets/offset-distance-nm");
|
|
|
|
|
2020-04-03 19:37:37 +00:00
|
|
|
// start position
|
2020-04-05 22:13:32 +00:00
|
|
|
SGGeod startPos;
|
|
|
|
if (abeam) {
|
|
|
|
// If we're starting from the abeam position, we are opposite the FLOLS, downwind on a left hand circuit
|
|
|
|
startPos = SGGeodesy::direct(flolsPosition, headingToFLOLS - 90, od * SG_NM_TO_METER);
|
|
|
|
} else {
|
|
|
|
startPos = SGGeodesy::direct(flolsPosition, headingToFLOLS + 180, od * SG_NM_TO_METER);
|
|
|
|
}
|
2020-03-18 16:34:02 +00:00
|
|
|
|
2020-04-03 19:37:37 +00:00
|
|
|
double alt = fgGetDouble("/sim/presets/altitude-ft");
|
|
|
|
|
|
|
|
if (alt < 0.0f) {
|
|
|
|
// No altitude set, so base on glideslope
|
|
|
|
const double offsetFt = od * SG_NM_TO_METER * SG_METER_TO_FEET;
|
|
|
|
startPos.setElevationFt(fabs(offsetFt*tan(gs)) + flolsElevationFt);
|
|
|
|
} else {
|
|
|
|
startPos.setElevationFt(alt);
|
|
|
|
}
|
2020-03-18 16:34:02 +00:00
|
|
|
|
|
|
|
fgSetDouble("/sim/presets/longitude-deg", startPos.getLongitudeDeg());
|
|
|
|
fgSetDouble("/sim/presets/latitude-deg", startPos.getLatitudeDeg());
|
|
|
|
fgSetDouble("/sim/presets/altitude-ft", startPos.getElevationFt());
|
2020-04-05 22:13:32 +00:00
|
|
|
fgSetDouble("/sim/presets/heading-deg", abeam ? (headingToFLOLS - 180) : headingToFLOLS);
|
2020-03-18 16:34:02 +00:00
|
|
|
fgSetDouble("/position/longitude-deg", startPos.getLongitudeDeg());
|
|
|
|
fgSetDouble("/position/latitude-deg", startPos.getLatitudeDeg());
|
|
|
|
fgSetDouble("/position/altitude-ft", startPos.getElevationFt());
|
|
|
|
fgSetDouble("/orientation/heading-deg", headingToFLOLS);
|
|
|
|
fgSetBool("/sim/presets/onground", false);
|
|
|
|
|
|
|
|
return ExactPosition;
|
|
|
|
}
|
|
|
|
|
2015-11-10 22:54:57 +00:00
|
|
|
// Set current_options lon/lat given a fix ident and GUID
|
2015-10-25 00:40:41 +00:00
|
|
|
static bool fgSetPosFromFix( const string& id, PositionedID guid )
|
2012-09-18 19:50:28 +00:00
|
|
|
{
|
2020-03-18 16:34:02 +00:00
|
|
|
FGPositionedRef fix;
|
2015-10-25 00:40:41 +00:00
|
|
|
if (guid != 0) {
|
|
|
|
fix = FGPositioned::loadById<FGPositioned>(guid);
|
|
|
|
} else {
|
|
|
|
FGPositioned::TypeFilter fixFilter(FGPositioned::FIX);
|
|
|
|
fix = FGPositioned::findFirstWithIdent(id, &fixFilter);
|
|
|
|
}
|
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
if (!fix) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate fix = " << id );
|
|
|
|
return false;
|
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
fgApplyStartOffset(fix->geod(), fgGetDouble("/sim/presets/heading-deg"));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the initial position based on presets (or defaults)
|
|
|
|
bool initPosition()
|
|
|
|
{
|
2012-12-09 19:41:31 +00:00
|
|
|
global_finalizeTime = SGTimeStamp(); // reset to invalid
|
2012-12-15 15:25:45 +00:00
|
|
|
if (!global_callbackRegistered) {
|
|
|
|
globals->get_event_mgr()->addTask("finalizePosition", &finalizePosition, 0.1);
|
|
|
|
global_callbackRegistered = true;
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2020-03-18 16:34:02 +00:00
|
|
|
double gs = SGMiscd::deg2rad(fgGetDouble("/sim/presets/glideslope-deg"));
|
2012-09-18 19:50:28 +00:00
|
|
|
double od = fgGetDouble("/sim/presets/offset-distance-nm");
|
|
|
|
double alt = fgGetDouble("/sim/presets/altitude-ft");
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
bool set_pos = false;
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2020-10-27 18:05:23 +00:00
|
|
|
// clear this value, so we don't preserve an old value and confuse
|
|
|
|
// the ATC manager. We will set it again if it's valid
|
|
|
|
fgSetString("/sim/atc/runway", "");
|
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
// If glideslope is specified, then calculate offset-distance or
|
|
|
|
// altitude relative to glide slope if either of those was not
|
|
|
|
// specified.
|
|
|
|
if ( fabs( gs ) > 0.01 ) {
|
|
|
|
fgSetDistOrAltFromGlideSlope();
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
// If we have an explicit, in-range lon/lat, don't change it, just use it.
|
|
|
|
// If not, check for an airport-id and use that.
|
|
|
|
// If not, default to the middle of the KSFO field.
|
|
|
|
// The default values for lon/lat are deliberately out of range
|
|
|
|
// so that the airport-id can take effect; valid lon/lat will
|
|
|
|
// override airport-id, however.
|
|
|
|
double lon_deg = fgGetDouble("/sim/presets/longitude-deg");
|
|
|
|
double lat_deg = fgGetDouble("/sim/presets/latitude-deg");
|
|
|
|
if ( lon_deg >= -180.0 && lon_deg <= 180.0
|
|
|
|
&& lat_deg >= -90.0 && lat_deg <= 90.0 )
|
|
|
|
{
|
|
|
|
set_pos = true;
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
string apt = fgGetString("/sim/presets/airport-id");
|
2017-03-19 11:54:49 +00:00
|
|
|
const bool apt_req = fgGetBool("/sim/presets/airport-requested");
|
2012-09-18 19:50:28 +00:00
|
|
|
string rwy_no = fgGetString("/sim/presets/runway");
|
|
|
|
bool rwy_req = fgGetBool("/sim/presets/runway-requested");
|
|
|
|
string vor = fgGetString("/sim/presets/vor-id");
|
|
|
|
double vor_freq = fgGetDouble("/sim/presets/vor-freq");
|
|
|
|
string ndb = fgGetString("/sim/presets/ndb-id");
|
|
|
|
double ndb_freq = fgGetDouble("/sim/presets/ndb-freq");
|
|
|
|
string carrier = fgGetString("/sim/presets/carrier");
|
|
|
|
string parkpos = fgGetString("/sim/presets/parkpos");
|
|
|
|
string fix = fgGetString("/sim/presets/fix");
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2015-10-25 00:40:41 +00:00
|
|
|
// the launcher sets this to precisely identify a navaid
|
2017-09-09 18:03:53 +00:00
|
|
|
PositionedID navaidId = fgGetInt("/sim/presets/navaid-id");
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
SGPropertyNode *hdg_preset = fgGetNode("/sim/presets/heading-deg", true);
|
|
|
|
double hdg = hdg_preset->getDoubleValue();
|
2020-04-12 20:45:20 +00:00
|
|
|
double original_hdg = hdg_preset->getDoubleValue();
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
// save some start parameters, so that we can later say what the
|
|
|
|
// user really requested. TODO generalize that and move it to options.cxx
|
|
|
|
static bool start_options_saved = false;
|
|
|
|
if (!start_options_saved) {
|
|
|
|
start_options_saved = true;
|
|
|
|
SGPropertyNode *opt = fgGetNode("/sim/startup/options", true);
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
opt->setDoubleValue("latitude-deg", lat_deg);
|
|
|
|
opt->setDoubleValue("longitude-deg", lon_deg);
|
|
|
|
opt->setDoubleValue("heading-deg", hdg);
|
|
|
|
opt->setStringValue("airport", apt.c_str());
|
|
|
|
opt->setStringValue("runway", rwy_no.c_str());
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-09-09 18:03:53 +00:00
|
|
|
if (hdg > 9990.0) {
|
2012-09-18 19:50:28 +00:00
|
|
|
hdg = fgGetDouble("/environment/config/boundary/entry/wind-from-heading-deg", 270);
|
2017-09-09 18:03:53 +00:00
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-09-09 18:03:53 +00:00
|
|
|
if ( !set_pos && !carrier.empty() ) {
|
|
|
|
// an aircraft carrier is requested
|
|
|
|
const auto result = setInitialPosFromCarrier( carrier );
|
|
|
|
if (result != Failure) {
|
|
|
|
// we at least found the carrier
|
|
|
|
set_pos = true;
|
2017-09-08 21:31:22 +00:00
|
|
|
}
|
2017-09-09 18:03:53 +00:00
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-03-19 11:54:49 +00:00
|
|
|
if (apt_req && !rwy_req) {
|
2017-09-09 18:03:53 +00:00
|
|
|
// ensure that if the users asks for a specific airport, but not a runway,
|
|
|
|
// presumably because they want automatic selection, we do not look
|
|
|
|
// for the default runway (from $FGDATA/location-preset.xml) which is
|
|
|
|
// likely missing.
|
|
|
|
rwy_no.clear();
|
2017-03-19 11:54:49 +00:00
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
if ( !set_pos && !apt.empty() && !parkpos.empty() ) {
|
|
|
|
// An airport + parking position is requested
|
2016-07-16 10:45:30 +00:00
|
|
|
// since this depends on parking, which is part of dynamics, and hence
|
|
|
|
// also depends on ATC (the ground controller), we need to defer this
|
|
|
|
// until position finalisation
|
2020-05-19 17:00:34 +00:00
|
|
|
// the rest of the work happens in finalizePosFromParkpos
|
|
|
|
if ( airportParkingSetVicinity( apt ) ) {
|
2012-09-18 19:50:28 +00:00
|
|
|
set_pos = true;
|
|
|
|
}
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
if ( !set_pos && !apt.empty() && !rwy_no.empty() ) {
|
|
|
|
// An airport + runway is requested
|
|
|
|
if ( fgSetPosFromAirportIDandRwy( apt, rwy_no, rwy_req ) ) {
|
|
|
|
set_pos = true;
|
|
|
|
}
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
if ( !set_pos && !apt.empty() ) {
|
|
|
|
// An airport is requested (find runway closest to hdg)
|
|
|
|
if ( setPosFromAirportIDandHdg( apt, hdg ) ) {
|
|
|
|
set_pos = true;
|
|
|
|
}
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2020-10-27 18:05:23 +00:00
|
|
|
// if an airport ID was requested, set closest-airport-id
|
|
|
|
// and tower based upon it.
|
|
|
|
if (!apt.empty() && set_pos) {
|
|
|
|
// set tower position
|
|
|
|
fgSetString("/sim/airport/closest-airport-id", apt.c_str());
|
|
|
|
fgSetString("/sim/tower/airport-id", apt.c_str());
|
|
|
|
}
|
|
|
|
|
2020-04-12 20:45:20 +00:00
|
|
|
if (original_hdg < 9990.0) {
|
|
|
|
// The user-set heading may be overridden by the setPosFromAirportID above.
|
|
|
|
hdg_preset->setDoubleValue(original_hdg);
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
if ( !set_pos && !vor.empty() ) {
|
|
|
|
// a VOR is requested
|
2015-11-10 22:54:57 +00:00
|
|
|
if ( fgSetPosFromNAV( vor, vor_freq, FGPositioned::VOR, navaidId ) ) {
|
2012-09-18 19:50:28 +00:00
|
|
|
set_pos = true;
|
|
|
|
}
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
if ( !set_pos && !ndb.empty() ) {
|
|
|
|
// an NDB is requested
|
2015-11-10 22:54:57 +00:00
|
|
|
if ( fgSetPosFromNAV( ndb, ndb_freq, FGPositioned::NDB, navaidId ) ) {
|
2012-09-18 19:50:28 +00:00
|
|
|
set_pos = true;
|
|
|
|
}
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
if ( !set_pos && !fix.empty() ) {
|
|
|
|
// a Fix is requested
|
2015-10-25 00:40:41 +00:00
|
|
|
if ( fgSetPosFromFix( fix, navaidId ) ) {
|
2012-09-18 19:50:28 +00:00
|
|
|
set_pos = true;
|
|
|
|
}
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
if ( !set_pos ) {
|
2019-05-27 10:42:53 +00:00
|
|
|
const std::string defaultAirportId = fgGetString("/sim/presets/airport-id");
|
|
|
|
const FGAirport* airport = fgFindAirportID(defaultAirportId);
|
2016-04-24 08:42:58 +00:00
|
|
|
if( airport ) {
|
|
|
|
const SGGeod & airportGeod = airport->geod();
|
|
|
|
fgSetDouble("/sim/presets/longitude-deg", airportGeod.getLongitudeDeg());
|
|
|
|
fgSetDouble("/sim/presets/latitude-deg", airportGeod.getLatitudeDeg());
|
|
|
|
} else {
|
2017-03-27 14:37:54 +00:00
|
|
|
// So, the default airport is unknown? We are in serious trouble.
|
2016-04-24 08:42:58 +00:00
|
|
|
// Let's hope KSFO still exists somehow
|
|
|
|
fgSetDouble("/sim/presets/longitude-deg", -122.374843);
|
|
|
|
fgSetDouble("/sim/presets/latitude-deg", 37.619002);
|
2019-05-27 10:42:53 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "Sorry, the default airport ('" << defaultAirportId
|
|
|
|
<< "') seems to be unknown.");
|
2016-04-24 08:42:58 +00:00
|
|
|
}
|
2012-09-18 19:50:28 +00:00
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
fgSetDouble( "/position/longitude-deg",
|
|
|
|
fgGetDouble("/sim/presets/longitude-deg") );
|
|
|
|
fgSetDouble( "/position/latitude-deg",
|
|
|
|
fgGetDouble("/sim/presets/latitude-deg") );
|
|
|
|
fgSetDouble( "/orientation/heading-deg", hdg_preset->getDoubleValue());
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
// determine if this should be an on-ground or in-air start
|
|
|
|
if ((fabs(gs) > 0.01 || fabs(od) > 0.1 || alt > 0.1) && carrier.empty()) {
|
|
|
|
fgSetBool("/sim/presets/onground", false);
|
|
|
|
} else {
|
|
|
|
fgSetBool("/sim/presets/onground", true);
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-12-15 15:25:45 +00:00
|
|
|
fgSetBool("/sim/position-finalized", false);
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2017-09-09 18:03:53 +00:00
|
|
|
// 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"));
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
return true;
|
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-12-15 15:25:45 +00:00
|
|
|
bool finalizeMetar()
|
|
|
|
{
|
|
|
|
double hdg = fgGetDouble( "/environment/metar/base-wind-dir-deg", 9999.0 );
|
2012-12-16 15:05:21 +00:00
|
|
|
string apt = fgGetString("/sim/presets/airport-id");
|
2012-12-15 15:25:45 +00:00
|
|
|
double strthdg = fgGetDouble( "/sim/startup/options/heading-deg", 9999.0 );
|
|
|
|
string parkpos = fgGetString( "/sim/presets/parkpos" );
|
|
|
|
bool onground = fgGetBool( "/sim/presets/onground", false );
|
2017-03-19 11:54:49 +00:00
|
|
|
const bool rwy_req = fgGetBool("/sim/presets/runway-requested");
|
2012-12-15 15:25:45 +00:00
|
|
|
// this logic is taken from former startup.nas
|
2017-03-19 11:54:49 +00:00
|
|
|
bool needMetar = (hdg < 360.0) &&
|
|
|
|
!apt.empty() &&
|
|
|
|
(strthdg > 360.0) &&
|
|
|
|
!rwy_req &&
|
|
|
|
onground &&
|
|
|
|
parkpos.empty();
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-12-15 15:25:45 +00:00
|
|
|
if (needMetar) {
|
|
|
|
// timeout so we don't spin forever if the network is down
|
2014-02-24 19:42:52 +00:00
|
|
|
if (global_finalizeTime.elapsedMSec() > fgGetInt("/sim/startup/metar-fetch-timeout-msec", 6000)) {
|
2012-12-15 15:25:45 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_WARN, "finalizePosition: timed out waiting for METAR fetch");
|
|
|
|
return true;
|
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2014-02-24 19:42:52 +00:00
|
|
|
if (fgGetBool( "/environment/metar/failure" )) {
|
|
|
|
SG_LOG(SG_ENVIRONMENT, SG_INFO, "metar download failed, not waiting");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-12-15 15:25:45 +00:00
|
|
|
if (!fgGetBool( "/environment/metar/valid" )) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-12-15 15:25:45 +00:00
|
|
|
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
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-12-15 15:25:45 +00:00
|
|
|
return true;
|
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-12-15 15:25:45 +00:00
|
|
|
void finalizePosition()
|
2012-12-09 19:41:31 +00:00
|
|
|
{
|
|
|
|
// first call to finalize after an initPosition call
|
|
|
|
if (global_finalizeTime.get_usec() == 0) {
|
|
|
|
global_finalizeTime = SGTimeStamp::now();
|
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-12-15 15:25:45 +00:00
|
|
|
bool done = true;
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-12-09 19:41:31 +00:00
|
|
|
/* 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
|
2017-03-27 14:37:54 +00:00
|
|
|
* carrier, recalculate the 'initial' position here
|
2012-12-09 19:41:31 +00:00
|
|
|
*/
|
2020-04-18 21:00:04 +00:00
|
|
|
const std::string carrier = fgGetString("/sim/presets/carrier");
|
|
|
|
const std::string carrierpos = fgGetString("/sim/presets/carrier-position");
|
|
|
|
const std::string parkpos = fgGetString("/sim/presets/parkpos");
|
|
|
|
const std::string runway = fgGetString("/sim/presets/runway");
|
|
|
|
const std::string apt = fgGetString("/sim/presets/airport-id");
|
2016-07-16 10:45:30 +00:00
|
|
|
|
2012-12-09 19:41:31 +00:00
|
|
|
if (!carrier.empty())
|
|
|
|
{
|
2020-04-17 19:25:18 +00:00
|
|
|
/* /sim/presets/carrier-position can take a number of different values
|
|
|
|
- name of a parking/cataputt
|
|
|
|
- FLOLS - to position on final approach
|
|
|
|
- abeam - on an abeam position from the FLOLS, heading downwind
|
|
|
|
- <empty> indicating a start position of a catapult
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Convert to lower case to simplify comparison
|
2020-04-18 21:00:04 +00:00
|
|
|
std::string cpos = simgear::strutils::lowercase(carrierpos);
|
2020-04-17 19:25:18 +00:00
|
|
|
|
2020-04-18 21:00:04 +00:00
|
|
|
const bool inair = (cpos == "flols") || (cpos == "abeam");
|
|
|
|
const bool abeam = (cpos == "abeam");
|
2020-03-18 16:34:02 +00:00
|
|
|
InitPosResult carrierResult;
|
2020-04-17 19:25:18 +00:00
|
|
|
if (inair) {
|
2020-04-05 22:13:32 +00:00
|
|
|
carrierResult = setFinalPosFromCarrierFLOLS(carrier, abeam);
|
2020-03-18 16:34:02 +00:00
|
|
|
} else {
|
2020-04-18 21:00:04 +00:00
|
|
|
// We don't simply use cpos as it is lower case, and the
|
2020-04-17 19:25:18 +00:00
|
|
|
// search against parking/catapult positions is case-sensitive.
|
2020-04-18 21:00:04 +00:00
|
|
|
carrierResult = setFinalPosFromCarrier(carrier, carrierpos);
|
2020-03-18 16:34:02 +00:00
|
|
|
}
|
|
|
|
if (carrierResult == ExactPosition) {
|
2017-09-08 21:31:22 +00:00
|
|
|
done = true;
|
2020-03-18 16:34:02 +00:00
|
|
|
} else if (carrierResult == Failure) {
|
2017-09-08 21:31:22 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "secondary carrier init failed");
|
|
|
|
done = true;
|
|
|
|
} else {
|
|
|
|
done = false;
|
|
|
|
// 60 second timeout on waiting for the carrier to load
|
|
|
|
if (global_finalizeTime.elapsedMSec() > 60000) {
|
2020-03-18 16:34:02 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "Timeout waiting for carrier scenery to load, will start on the water.");
|
2017-09-08 21:31:22 +00:00
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
}
|
2020-04-03 19:37:37 +00:00
|
|
|
|
2016-07-16 10:45:30 +00:00
|
|
|
} else if (!apt.empty() && !parkpos.empty()) {
|
|
|
|
// parking position depends on ATC / dynamics code to assign spaces,
|
|
|
|
// so we wait until this point to initialise
|
2020-05-19 17:00:34 +00:00
|
|
|
bool ok = finalizePositionForParkpos(apt, parkpos);
|
|
|
|
if (!ok) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_WARN, "finalizePositionForParkPos failed, reverting to best runway");
|
|
|
|
|
|
|
|
// clear this so finalizeMetar works as expected
|
|
|
|
fgSetString("/sim/presets/parkpos", "");
|
|
|
|
finalizeMetar();
|
|
|
|
}
|
2012-12-15 15:25:45 +00:00
|
|
|
} else {
|
|
|
|
done = finalizeMetar();
|
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-12-15 15:25:45 +00:00
|
|
|
fgSetBool("/sim/position-finalized", done);
|
|
|
|
if (done) {
|
|
|
|
globals->get_event_mgr()->removeTask("finalizePosition");
|
|
|
|
global_callbackRegistered = false;
|
2012-12-09 19:41:31 +00:00
|
|
|
}
|
|
|
|
}
|
2017-03-27 14:37:54 +00:00
|
|
|
|
2012-09-18 19:50:28 +00:00
|
|
|
} // of namespace flightgear
|