Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/flightgear into next
This commit is contained in:
commit
097603452e
31 changed files with 842 additions and 262 deletions
docs-mini
src
AIModel
Aircraft
Autopilot
Environment
FDM/JSBSim
Instrumentation/HUD
Main
Navaids
Network
Scenery
Scripting
test_suite
simgear_tests
unit_tests
77
docs-mini/README-structure.md
Normal file
77
docs-mini/README-structure.md
Normal file
|
@ -0,0 +1,77 @@
|
|||
# Strucutral Overview
|
||||
|
||||
## Foundation Components
|
||||
|
||||
|
||||
There is a group os basic objects used widely throughout the codebase, which
|
||||
are useful building blocks in all major simulation elements.
|
||||
|
||||
### Properties
|
||||
|
||||
Properties form a hierarchy of named, typed values. The hierarchy can be extended
|
||||
and modified at runtime by aircraft, simulation components and external protocols.
|
||||
|
||||
### Listeners
|
||||
|
||||
Listeners are a callback interface `SGPropertyChangeListener` which can implemented
|
||||
to receive notification when a property is modified, or its children added/removed.
|
||||
Most commonly listeners are used to look for a user input or simulation event
|
||||
changing some value (for example, a switch being turned from on to off), but it's
|
||||
possible to monitor an entire sub-tree of properties.
|
||||
|
||||
It's important than listeners do not block or perform excessive amounts of work, since
|
||||
the code modifying the property may not expect this. If non-trivial work needs to be
|
||||
performed in response to a property changing, the listener should set a dirty flag
|
||||
and a subsystem (etc) should check this in its `update()` method.
|
||||
|
||||
### Tied properties
|
||||
|
||||
Tied properties allow a property to be defined by a C++ memory location (such as a class
|
||||
member variable) or by callback functions implementation the get/set operations. This
|
||||
is very convenient for the implementating code. However, by default it has the
|
||||
problematic side-effect that listeners do not correctly, since they have no way to be
|
||||
notified thst the underlying value (or result of invoking the getter) has changed.
|
||||
|
||||
It is therefore critical, when using tied properties, to _manually_ invoke `fireValueChanged`
|
||||
on dependent proeprties, when updating the internal state producing them. Care should be
|
||||
taken to only fire the change, when the result of the getters would actually change; it is
|
||||
_not_ acceptable to simply call `fireValueChanged` each iteration, since any connected
|
||||
listener will be run at the simulation rate.
|
||||
|
||||
### Conditions
|
||||
|
||||
_Conditions_ are predicates which can be evaluated on demand. They can contain
|
||||
logical operations such as 'and' and 'or', and comparisoms: for example checking that
|
||||
a property is equal to a constant value, or that a property is less than another
|
||||
property.
|
||||
|
||||
Conditions are widely used to drive other state from properties; for example
|
||||
enabling or disabling user-interface in a dialog, or activating an autopilot
|
||||
element.
|
||||
|
||||
### Bindings
|
||||
|
||||
_Bindings_ are a command invocation with associated arguments. In the simplest case
|
||||
this might be invoking the `property-set` command with an argument of 42. At its
|
||||
most complex, the command might invoke a Nasal script, supplied as part of the
|
||||
binding, and substituing a runtime value into the Nasal logic.
|
||||
|
||||
Bindings are typically used in response to user actions, such as a menu item, key
|
||||
press or interacting with a cockpit control, to update some part of the simulator
|
||||
state.
|
||||
|
||||
## Structural Components
|
||||
|
||||
Most code within FlightGear is organized into subsystems. Subsystems are isolated from each other;
|
||||
a well designed subsytem can be added, remove or restarting independently. Many subystems do not
|
||||
full conform to these goals, especially regarding runtime adding and removal, but an increasing
|
||||
number do support it.
|
||||
|
||||
=== Subsystems ===
|
||||
|
||||
=== Commands ===
|
||||
|
||||
|
||||
## Finding data files
|
||||
|
||||
## Subsysterm Lifecycle
|
|
@ -19,9 +19,7 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <config.h>
|
||||
|
||||
#include <simgear/math/sg_random.hxx>
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
|
@ -33,6 +31,7 @@
|
|||
|
||||
#include <Main/util.hxx>
|
||||
#include <Environment/gravity.hxx>
|
||||
#include <Environment/atmosphere.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
using namespace simgear;
|
||||
|
@ -695,6 +694,10 @@ void FGAIBallistic::Run(double dt) {
|
|||
// for a conventional shell/bullet (no boat-tail).
|
||||
double Cdm;
|
||||
|
||||
const double Mach = FGAtmo::machFromKnotsAtAltitudeFt(speed, altitude_ft);
|
||||
const double rhoKgM3 = FGAtmo::densityAtAltitudeFt(altitude_ft);
|
||||
const double rho = rhoKgM3 / SG_SLUGFT3_TO_KGPM3;
|
||||
|
||||
if (Mach < 0.7)
|
||||
Cdm = 0.0125 * Mach + _cd;
|
||||
else if (Mach < 1.2)
|
||||
|
|
|
@ -193,12 +193,6 @@ FGAIBase::FGAIBase(object_type ot, bool enableHot) : replay_time(fgGetNode("sim/
|
|||
|
||||
serviceable = false;
|
||||
|
||||
rho = 1;
|
||||
T = 280;
|
||||
p = 1e5;
|
||||
a = 340;
|
||||
Mach = 0;
|
||||
|
||||
// explicitly disable HOT for (most) AI models
|
||||
if (!enableHot)
|
||||
aip.getSceneGraph()->setNodeMask(~SG_NODEMASK_TERRAIN_BIT);
|
||||
|
@ -309,9 +303,6 @@ void FGAIBase::update(double dt) {
|
|||
if (_otype == object_type::otStatic)
|
||||
return;
|
||||
|
||||
if (_otype == object_type::otBallistic)
|
||||
CalculateMach();
|
||||
|
||||
ft_per_deg_lat = 366468.96 - 3717.12 * cos(pos.getLatitudeRad());
|
||||
ft_per_deg_lon = 365228.16 * cos(pos.getLatitudeRad());
|
||||
|
||||
|
@ -1193,38 +1184,6 @@ int FGAIBase::_getFallbackModelIndex() const {
|
|||
return _fallback_model_index;
|
||||
}
|
||||
|
||||
void FGAIBase::CalculateMach() {
|
||||
// Calculate rho at altitude, using standard atmosphere
|
||||
// For the temperature T and the pressure p,
|
||||
double altitude = altitude_ft;
|
||||
|
||||
if (altitude < 36152) { // curve fits for the troposphere
|
||||
T = 59 - 0.00356 * altitude;
|
||||
p = 2116 * pow( ((T + 459.7) / 518.6) , 5.256);
|
||||
} else if ( 36152 < altitude && altitude < 82345 ) { // lower stratosphere
|
||||
T = -70;
|
||||
p = 473.1 * pow( e , 1.73 - (0.000048 * altitude) );
|
||||
} else { // upper stratosphere
|
||||
T = -205.05 + (0.00164 * altitude);
|
||||
p = 51.97 * pow( ((T + 459.7) / 389.98) , -11.388);
|
||||
}
|
||||
|
||||
rho = p / (1718 * (T + 459.7));
|
||||
|
||||
// calculate the speed of sound at altitude
|
||||
// a = sqrt ( g * R * (T + 459.7))
|
||||
// where:
|
||||
// a = speed of sound [ft/s]
|
||||
// g = specific heat ratio, which is usually equal to 1.4
|
||||
// R = specific gas constant, which equals 1716 ft-lb/slug/R
|
||||
a = sqrt ( 1.4 * 1716 * (T + 459.7));
|
||||
|
||||
// calculate Mach number
|
||||
Mach = speed/a;
|
||||
|
||||
// cout << "Speed(ft/s) "<< speed <<" Altitude(ft) "<< altitude << " Mach " << Mach << endl;
|
||||
}
|
||||
|
||||
int FGAIBase::_newAIModelID() {
|
||||
static int id = 0;
|
||||
|
||||
|
|
|
@ -292,7 +292,6 @@ protected:
|
|||
|
||||
ModelSearchOrder _searchOrder = ModelSearchOrder::DATA_ONLY;
|
||||
void Transform();
|
||||
void CalculateMach();
|
||||
double UpdateRadar(FGAIManager* manager);
|
||||
|
||||
void removeModel();
|
||||
|
@ -377,13 +376,6 @@ public:
|
|||
const char* _getSubmodel() const;
|
||||
int _getFallbackModelIndex() const;
|
||||
|
||||
// These are used in the Mach number calculations
|
||||
double rho;
|
||||
double T; // temperature, degs farenheit
|
||||
double p; // pressure lbs/sq ft
|
||||
double a; // speed of sound at altitude (ft/s)
|
||||
double Mach; // Mach number
|
||||
|
||||
static const double e;
|
||||
static const double lbs_to_slugs;
|
||||
|
||||
|
|
|
@ -123,4 +123,5 @@ void FGReplay::resetStatisticsProperties()
|
|||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<FGReplay> registrantFGReplay;
|
||||
SGSubsystemMgr::Registrant<FGReplay> registrantFGReplay(
|
||||
SGSubsystemMgr::POST_FDM);
|
||||
|
|
|
@ -1,26 +1,8 @@
|
|||
// route_mgr.cxx - manage a route (i.e. a collection of waypoints)
|
||||
//
|
||||
// Written by Curtis Olson, started January 2004.
|
||||
// Norman Vine
|
||||
// Melchior FRANZ
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// $Id$
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2004 Curtis L. Olson http://www.flightgear.org/~curt
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
|
||||
#include <config.h>
|
||||
|
@ -372,6 +354,7 @@ void FGRouteMgr::init() {
|
|||
|
||||
_edited = fgGetNode(RM "signals/edited", true);
|
||||
_flightplanChanged = fgGetNode(RM "signals/flightplan-changed", true);
|
||||
_isRoute = fgGetNode(RM "is-route", true);
|
||||
|
||||
_currentWpt = fgGetNode(RM "current-wp", true);
|
||||
_currentWpt->setAttribute(SGPropertyNode::LISTENER_SAFE, true);
|
||||
|
@ -411,6 +394,8 @@ void FGRouteMgr::postinit()
|
|||
loadRoute(path);
|
||||
}
|
||||
|
||||
_isRoute->setBoolValue(_plan->isRoute());
|
||||
|
||||
// this code only matters for the --wp option now - perhaps the option
|
||||
// should be deprecated in favour of an explicit flight-plan file?
|
||||
// then the global initial waypoint list could die.
|
||||
|
@ -486,7 +471,7 @@ void FGRouteMgr::setFlightPlan(const FlightPlanRef& plan)
|
|||
|
||||
_plan = plan;
|
||||
_plan->addDelegate(this);
|
||||
|
||||
_isRoute->setBoolValue(_plan->isRoute());
|
||||
_flightplanChanged->fireValueChanged();
|
||||
|
||||
// fire all the callbacks!
|
||||
|
|
|
@ -1,24 +1,8 @@
|
|||
// route_mgr.hxx - manage a route (i.e. a collection of waypoints)
|
||||
//
|
||||
// Written by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// $Id$
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2004 Curtis L. Olson http://www.flightgear.org/~curt
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
@ -144,7 +128,7 @@ private:
|
|||
|
||||
SGPropertyNode_ptr _pathNode;
|
||||
SGPropertyNode_ptr _currentWpt;
|
||||
|
||||
SGPropertyNode_ptr _isRoute;
|
||||
|
||||
/**
|
||||
* Signal property to notify people that the route was edited
|
||||
|
|
|
@ -151,6 +151,47 @@ double FGAtmo::a_vs_p(const double press, const double qnh) {
|
|||
return T0 * ( pow(qnh/P0,nn) - pow(press/P0,nn) ) / lam0;
|
||||
}
|
||||
|
||||
|
||||
double FGAtmo::ISATemperatureKAtAltitudeFt(const double alt, const double Tsl)
|
||||
{
|
||||
double pressure, temp;
|
||||
std::tie(pressure, temp) = PT_vs_hpt(alt * SG_FEET_TO_METER, atmodel::ISA::P0, Tsl);
|
||||
return temp;
|
||||
}
|
||||
|
||||
double FGAtmo::CSMetersPerSecondAtAltitudeFt(const double alt, const double Tsl)
|
||||
{
|
||||
const double oatK = ISATemperatureKAtAltitudeFt(alt, Tsl);
|
||||
// calculate the speed of sound at altitude
|
||||
const double g = 1.4; // specific heat ratio of air
|
||||
const double R = 287.053; // specific gas constant (J/kgK)
|
||||
return sqrt (g * R * oatK);
|
||||
}
|
||||
|
||||
double FGAtmo::densityAtAltitudeFt(const double alt, const double Tsl)
|
||||
{
|
||||
double pressure, temp;
|
||||
std::tie(pressure, temp) = PT_vs_hpt(alt * SG_FEET_TO_METER, atmodel::ISA::P0, Tsl);
|
||||
const double R = 287.053; // specific gas constant (J/kgK)
|
||||
return pressure / (temp * R);
|
||||
}
|
||||
|
||||
double FGAtmo::machFromKnotsAtAltitudeFt(const double knots,
|
||||
const double altFt,
|
||||
const double Tsl)
|
||||
{
|
||||
const auto cs = CSMetersPerSecondAtAltitudeFt(altFt, Tsl);
|
||||
return (knots * SG_KT_TO_MPS) / cs;
|
||||
}
|
||||
|
||||
double FGAtmo::knotsFromMachAtAltitudeFt(const double mach,
|
||||
const double altFt,
|
||||
const double Tsl)
|
||||
{
|
||||
const auto cs = CSMetersPerSecondAtAltitudeFt(altFt, Tsl);
|
||||
return mach * cs * SG_MPS_TO_KT;
|
||||
}
|
||||
|
||||
// force retabulation
|
||||
void FGAtmoCache::tabulate() {
|
||||
using namespace atmodel;
|
||||
|
|
|
@ -117,6 +117,30 @@ public:
|
|||
* or millibars
|
||||
*/
|
||||
static double fieldPressure(const double field_elev, const double qnh);
|
||||
|
||||
/**
|
||||
Compute the outisde temperature at an altitude, according to the standard atmosphere
|
||||
model. Optionally allow offseting the temperature at seal level, but default to the ISA standard
|
||||
for that value as well
|
||||
*/
|
||||
static double ISATemperatureKAtAltitudeFt(const double alt,
|
||||
const double Tsl = atmodel::ISA::T0);
|
||||
|
||||
/**
|
||||
Compute the speed of sound at an altitude
|
||||
*/
|
||||
static double CSMetersPerSecondAtAltitudeFt(const double alt,
|
||||
const double Tsl = atmodel::ISA::T0);
|
||||
|
||||
static double densityAtAltitudeFt(const double alt, const double Tsl = atmodel::ISA::T0);
|
||||
|
||||
static double machFromKnotsAtAltitudeFt(const double knots,
|
||||
const double altFt,
|
||||
const double Tsl = atmodel::ISA::T0);
|
||||
|
||||
static double knotsFromMachAtAltitudeFt(const double mach,
|
||||
const double altFt,
|
||||
const double Tsl = atmodel::ISA::T0);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1246,6 +1246,7 @@ void FGJSBsim::init_gear(void )
|
|||
{
|
||||
FGGroundReactions* gr=fdmex->GetGroundReactions();
|
||||
int Ngear=GroundReactions->GetNumGearUnits();
|
||||
double max = 1.0;
|
||||
for (int i=0;i<Ngear;i++) {
|
||||
FGLGear *gear = gr->GetGearUnit(i);
|
||||
SGPropertyNode * node = fgGetNode("gear/gear", i, true);
|
||||
|
@ -1254,16 +1255,23 @@ void FGJSBsim::init_gear(void )
|
|||
node->setDoubleValue("yoffset-in", gear->GetBodyLocation()(2) * 12);
|
||||
node->setDoubleValue("zoffset-in", gear->GetBodyLocation()(3) * 12);
|
||||
|
||||
node->setDoubleValue("xoffset-m", gear->GetBodyLocation()(1) * 0.08333333);
|
||||
node->setDoubleValue("yoffset-m", gear->GetBodyLocation()(2) * 0.08333333);
|
||||
node->setDoubleValue("zoffset-m", gear->GetBodyLocation()(3) * 0.08333333);
|
||||
node->setDoubleValue("xoffset-m", gear->GetBodyLocation()(1) * SG_FEET_TO_METER);
|
||||
node->setDoubleValue("yoffset-m", gear->GetBodyLocation()(2) * SG_FEET_TO_METER);
|
||||
node->setDoubleValue("zoffset-m", gear->GetBodyLocation()(3) * SG_FEET_TO_METER);
|
||||
|
||||
node->setBoolValue("wow", gear->GetWOW());
|
||||
node->setDoubleValue("rollspeed-ms", gear->GetWheelRollVel()*0.3043);
|
||||
node->setDoubleValue("rollspeed-ms", gear->GetWheelRollVel() * SG_FEET_TO_METER);
|
||||
node->setBoolValue("has-brake", gear->GetBrakeGroup() > 0);
|
||||
node->setDoubleValue("position-norm", gear->GetGearUnitPos());
|
||||
// node->setDoubleValue("tire-pressure-norm", gear->GetTirePressure());
|
||||
node->setDoubleValue("compression-norm", gear->GetCompLen());
|
||||
max = node->getDoubleValue("max-compression-ft");
|
||||
if (max < 0.00001) {
|
||||
max = node->getDoubleValue("max-compression-m");
|
||||
if (max < 0.00001) max = 1.0;
|
||||
else max /= SG_FEET_TO_METER;
|
||||
}
|
||||
node->setDoubleValue("compression-norm", gear->GetCompLen() / max);
|
||||
node->setDoubleValue("compression-m", gear->GetCompLen() * SG_FEET_TO_METER);
|
||||
node->setDoubleValue("compression-ft", gear->GetCompLen());
|
||||
if ( gear->GetSteerable() )
|
||||
node->setDoubleValue("steering-norm", gear->GetSteerNorm());
|
||||
|
@ -1274,14 +1282,22 @@ void FGJSBsim::update_gear(void)
|
|||
{
|
||||
FGGroundReactions* gr=fdmex->GetGroundReactions();
|
||||
int Ngear=GroundReactions->GetNumGearUnits();
|
||||
double max = 1.0;
|
||||
for (int i=0;i<Ngear;i++) {
|
||||
FGLGear *gear = gr->GetGearUnit(i);
|
||||
SGPropertyNode * node = fgGetNode("gear/gear", i, true);
|
||||
node->getChild("wow", 0, true)->setBoolValue( gear->GetWOW());
|
||||
node->getChild("rollspeed-ms", 0, true)->setDoubleValue(gear->GetWheelRollVel()*0.3043);
|
||||
node->getChild("rollspeed-ms", 0, true)->setDoubleValue(gear->GetWheelRollVel() * SG_FEET_TO_METER);
|
||||
node->getChild("position-norm", 0, true)->setDoubleValue(gear->GetGearUnitPos());
|
||||
// gear->SetTirePressure(node->getDoubleValue("tire-pressure-norm"));
|
||||
node->setDoubleValue("compression-norm", gear->GetCompLen());
|
||||
max = node->getDoubleValue("max-compression-ft");
|
||||
if (max < 0.00001) {
|
||||
max = node->getDoubleValue("max-compression-m");
|
||||
if (max < 0.00001) max = 1.0;
|
||||
else max /= SG_FEET_TO_METER;
|
||||
}
|
||||
node->setDoubleValue("compression-norm", gear->GetCompLen() / max);
|
||||
node->setDoubleValue("compression-m", gear->GetCompLen() * SG_FEET_TO_METER);
|
||||
node->setDoubleValue("compression-ft", gear->GetCompLen());
|
||||
if ( gear->GetSteerable() )
|
||||
node->setDoubleValue("steering-norm", gear->GetSteerNorm());
|
||||
|
|
|
@ -649,7 +649,8 @@ void HUDText::draw()
|
|||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<HUD> registrantHUD;
|
||||
SGSubsystemMgr::Registrant<HUD> registrantHUD(
|
||||
SGSubsystemMgr::DISPLAY);
|
||||
|
||||
|
||||
void TextList::align(const char *s, int align, float *x, float *y,
|
||||
|
|
|
@ -1051,7 +1051,7 @@ void fgCreateSubsystems(bool duringReset) {
|
|||
// autopilot.)
|
||||
mgr->add<FGSystemMgr>();
|
||||
mgr->add<FGInstrumentMgr>();
|
||||
mgr->add("xml-autopilot", FGXMLAutopilotGroup::createInstance("autopilot"));
|
||||
mgr->add("xml-autopilot", FGXMLAutopilotGroup::createInstance("autopilot"), SGSubsystemMgr::FDM);
|
||||
}
|
||||
|
||||
// SGSubsystemMgr::POST_FDM
|
||||
|
@ -1106,7 +1106,7 @@ void fgCreateSubsystems(bool duringReset) {
|
|||
mgr->add<CanvasMgr>();
|
||||
|
||||
auto canvasGui = new GUIMgr;
|
||||
mgr->add("CanvasGUI", canvasGui);
|
||||
mgr->add("CanvasGUI", canvasGui, SGSubsystemMgr::DISPLAY);
|
||||
auto guiCamera = flightgear::getGUICamera(flightgear::CameraGroup::getDefault());
|
||||
canvasGui->setGUIViewAndCamera(globals->get_renderer()->getView(), guiCamera);
|
||||
|
||||
|
@ -1117,7 +1117,7 @@ void fgCreateSubsystems(bool duringReset) {
|
|||
// ordering here is important : Nasal (via events), then models, then views
|
||||
if (!duringReset) {
|
||||
mgr->add<FGLight>();
|
||||
mgr->add("events", globals->get_event_mgr());
|
||||
mgr->add("events", globals->get_event_mgr(), SGSubsystemMgr::DISPLAY);
|
||||
}
|
||||
|
||||
mgr->add<FGAircraftModel>();
|
||||
|
@ -1435,20 +1435,22 @@ void fgStartNewReset()
|
|||
|
||||
sgUserDataInit( globals->get_props() );
|
||||
|
||||
unsigned int numDBPagerThreads = fgGetNode("/sim/rendering/database-pager/threads", true)->getIntValue(2);
|
||||
|
||||
if (composite_viewer) {
|
||||
composite_viewer_view->getDatabasePager()->setUpThreads(2, 1);
|
||||
composite_viewer_view->setDatabasePager(FGScenery::getPagerSingleton());
|
||||
composite_viewer_view->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true, false);
|
||||
composite_viewer_view->getDatabasePager()->setUpThreads(numDBPagerThreads, 1);
|
||||
composite_viewer_view->getDatabasePager()->setAcceptNewDatabaseRequests(true);
|
||||
flightgear::CameraGroup::buildDefaultGroup(composite_viewer_view);
|
||||
composite_viewer_view->setFrameStamp(composite_viewer->getFrameStamp());
|
||||
composite_viewer_view->setDatabasePager(FGScenery::getPagerSingleton());
|
||||
composite_viewer_view->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true, false);
|
||||
osg::GraphicsContext::createNewContextID();
|
||||
render->setView(composite_viewer_view);
|
||||
render->preinit();
|
||||
composite_viewer->startThreading();
|
||||
}
|
||||
else {
|
||||
viewer->getDatabasePager()->setUpThreads(2, 1);
|
||||
viewer->getDatabasePager()->setUpThreads(numDBPagerThreads, 1);
|
||||
viewer->getDatabasePager()->setAcceptNewDatabaseRequests(true);
|
||||
// must do this before preinit for Rembrandthe
|
||||
flightgear::CameraGroup::buildDefaultGroup(viewer.get());
|
||||
|
|
|
@ -1301,16 +1301,7 @@ bool FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData)
|
|||
continue;
|
||||
}
|
||||
|
||||
LegRef l = new Leg{this, wp};
|
||||
// sync leg restrictions with waypoint ones
|
||||
if (wp->speedRestriction() != RESTRICT_NONE) {
|
||||
l->setSpeed(wp->speedRestriction(), wp->speed());
|
||||
}
|
||||
|
||||
if (wp->altitudeRestriction() != RESTRICT_NONE) {
|
||||
l->setAltitude(wp->altitudeRestriction(), wp->altitudeFt());
|
||||
}
|
||||
|
||||
LegRef l = new Leg{this, wp};
|
||||
if (wpNode->hasChild("hold-count")) {
|
||||
l->setHoldCount(wpNode->getIntValue("hold-count"));
|
||||
}
|
||||
|
@ -1383,7 +1374,7 @@ WayptRef FlightPlan::parseVersion1XMLWaypt(SGPropertyNode* aWP)
|
|||
|
||||
double altFt = aWP->getDoubleValue("altitude-ft", -9999.9);
|
||||
if (altFt > -9990.0) {
|
||||
w->setAltitude(altFt, RESTRICT_AT);
|
||||
w->setAltitude(altFt, RESTRICT_AT, ALTITUDE_FEET);
|
||||
}
|
||||
|
||||
return w;
|
||||
|
@ -1564,8 +1555,11 @@ FlightPlan::Leg* FlightPlan::Leg::cloneFor(FlightPlan* owner) const
|
|||
// clone local data
|
||||
c->_speed = _speed;
|
||||
c->_speedRestrict = _speedRestrict;
|
||||
c->_altitudeFt = _altitudeFt;
|
||||
c->_speedUnits = _speedUnits;
|
||||
c->_altitude = _altitude;
|
||||
c->_altRestrict = _altRestrict;
|
||||
c->_altitudeUnits = _altitudeUnits;
|
||||
c->_holdCount = c->_holdCount;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
@ -1583,36 +1577,37 @@ unsigned int FlightPlan::Leg::index() const
|
|||
return _parent->findLegIndex(this);
|
||||
}
|
||||
|
||||
int FlightPlan::Leg::altitudeFt() const
|
||||
double FlightPlan::Leg::altitude(RouteUnits aUnits) const
|
||||
{
|
||||
if (_altRestrict != RESTRICT_NONE) {
|
||||
return _altitudeFt;
|
||||
return convertAltitudeUnits(_altitudeUnits, aUnits, _altitude);
|
||||
}
|
||||
|
||||
return _waypt->altitudeFt();
|
||||
return _waypt->altitude(aUnits);
|
||||
}
|
||||
|
||||
int FlightPlan::Leg::speed() const
|
||||
int FlightPlan::Leg::altitudeFt() const
|
||||
{
|
||||
return static_cast<int>(altitude(ALTITUDE_FEET));
|
||||
}
|
||||
|
||||
double FlightPlan::Leg::speed(RouteUnits units) const
|
||||
{
|
||||
if (_speedRestrict != RESTRICT_NONE) {
|
||||
return _speed;
|
||||
return convertSpeedUnits(_speedUnits, units, altitudeFt(), _speed);
|
||||
}
|
||||
|
||||
return _waypt->speed();
|
||||
return _waypt->speed(units);
|
||||
}
|
||||
|
||||
int FlightPlan::Leg::speedKts() const
|
||||
{
|
||||
return speed();
|
||||
return static_cast<int>(speed(SPEED_KNOTS));
|
||||
}
|
||||
|
||||
double FlightPlan::Leg::speedMach() const
|
||||
{
|
||||
if (!isMachRestrict(_speedRestrict)) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return -(_speed / 100.0);
|
||||
return speed(SPEED_MACH);
|
||||
}
|
||||
|
||||
RouteRestriction FlightPlan::Leg::altitudeRestriction() const
|
||||
|
@ -1633,20 +1628,29 @@ RouteRestriction FlightPlan::Leg::speedRestriction() const
|
|||
return _waypt->speedRestriction();
|
||||
}
|
||||
|
||||
void FlightPlan::Leg::setSpeed(RouteRestriction ty, double speed)
|
||||
void FlightPlan::Leg::setSpeed(RouteRestriction ty, double speed, RouteUnits aUnit)
|
||||
{
|
||||
_speedRestrict = ty;
|
||||
if (isMachRestrict(ty)) {
|
||||
_speed = (speed * -100);
|
||||
} else {
|
||||
_speed = speed;
|
||||
if (aUnit == DEFAULT_UNITS) {
|
||||
if (isMachRestrict(ty)) {
|
||||
aUnit = SPEED_MACH;
|
||||
} else {
|
||||
// TODO: check for system in metric?
|
||||
aUnit = SPEED_KNOTS;
|
||||
}
|
||||
}
|
||||
_speedUnits = aUnit;
|
||||
_speed = speed;
|
||||
}
|
||||
|
||||
void FlightPlan::Leg::setAltitude(RouteRestriction ty, int altFt)
|
||||
void FlightPlan::Leg::setAltitude(RouteRestriction ty, double alt, RouteUnits aUnit)
|
||||
{
|
||||
_altRestrict = ty;
|
||||
_altitudeFt = altFt;
|
||||
if (aUnit == DEFAULT_UNITS) {
|
||||
aUnit = ALTITUDE_FEET;
|
||||
}
|
||||
_altitudeUnits = aUnit;
|
||||
_altitude = alt;
|
||||
}
|
||||
|
||||
double FlightPlan::Leg::courseDeg() const
|
||||
|
@ -1722,16 +1726,24 @@ void FlightPlan::Leg::writeToProperties(SGPropertyNode* aProp) const
|
|||
{
|
||||
if (_speedRestrict != RESTRICT_NONE) {
|
||||
aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict));
|
||||
if (_speedRestrict == SPEED_RESTRICT_MACH) {
|
||||
aProp->setDoubleValue("speed", speedMach());
|
||||
if (_speedUnits == SPEED_MACH) {
|
||||
aProp->setDoubleValue("speed-mach", _speed);
|
||||
} else if (_speedUnits == SPEED_KPH) {
|
||||
aProp->setDoubleValue("speed-kph", _speed);
|
||||
} else {
|
||||
aProp->setDoubleValue("speed", _speed);
|
||||
aProp->setDoubleValue("speed", _speed);
|
||||
}
|
||||
}
|
||||
|
||||
if (_altRestrict != RESTRICT_NONE) {
|
||||
aProp->setStringValue("alt-restrict", restrictionToString(_altRestrict));
|
||||
aProp->setDoubleValue("altitude-ft", _altitudeFt);
|
||||
if (_altitudeUnits == ALTITUDE_FLIGHTLEVEL) {
|
||||
aProp->setDoubleValue("flight-level", _altitude);
|
||||
} else if (_altitudeUnits == ALTITUDE_METER) {
|
||||
aProp->setDoubleValue("altitude-m", _altitude);
|
||||
} else {
|
||||
aProp->setDoubleValue("altitude-ft", _altitude);
|
||||
}
|
||||
}
|
||||
|
||||
if (_holdCount > 0) {
|
||||
|
|
|
@ -92,7 +92,7 @@ public:
|
|||
FlightPlanRef clone(const std::string& newIdent = {}, bool convertToFlightPlan = false) const;
|
||||
|
||||
/**
|
||||
is this flight-pan a route (for planning) or an active flight-plan (which can be flow?)
|
||||
is this flight-pan a route (for planning) or an active flight-plan (which can be flown?)
|
||||
Routes can contain Via, and cannot be active: FlightPlans contain Legs for procedures and
|
||||
airways, i.e what the GPS/FMC actally flies.
|
||||
*/
|
||||
|
@ -131,7 +131,8 @@ public:
|
|||
unsigned int index() const;
|
||||
|
||||
int altitudeFt() const;
|
||||
int speed() const;
|
||||
double speed(RouteUnits units = DEFAULT_UNITS) const;
|
||||
double altitude(RouteUnits units = DEFAULT_UNITS) const;
|
||||
|
||||
int speedKts() const;
|
||||
double speedMach() const;
|
||||
|
@ -139,8 +140,8 @@ public:
|
|||
RouteRestriction altitudeRestriction() const;
|
||||
RouteRestriction speedRestriction() const;
|
||||
|
||||
void setSpeed(RouteRestriction ty, double speed);
|
||||
void setAltitude(RouteRestriction ty, int altFt);
|
||||
void setSpeed(RouteRestriction ty, double speed, RouteUnits units = DEFAULT_UNITS);
|
||||
void setAltitude(RouteRestriction ty, double alt, RouteUnits units = DEFAULT_UNITS);
|
||||
|
||||
double courseDeg() const;
|
||||
double distanceNm() const;
|
||||
|
@ -164,8 +165,10 @@ public:
|
|||
const FlightPlan* _parent;
|
||||
RouteRestriction _speedRestrict = RESTRICT_NONE,
|
||||
_altRestrict = RESTRICT_NONE;
|
||||
int _speed = 0;
|
||||
int _altitudeFt = 0;
|
||||
double _speed = 0.0;
|
||||
double _altitude = 0.0;
|
||||
RouteUnits _speedUnits = SPEED_KNOTS;
|
||||
RouteUnits _altitudeUnits = ALTITUDE_FEET;
|
||||
|
||||
// if > 0, we will hold at the waypoint using
|
||||
// the published hold side/course
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <Navaids/waypoint.hxx>
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Navaids/route.hxx>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
@ -203,15 +204,7 @@ void NavdataVisitor::endElement(const char* name)
|
|||
} else if (tag == "Altitude") {
|
||||
_altitude = atof(_text.c_str());
|
||||
} else if (tag == "AltitudeRestriction") {
|
||||
if (_text == "at") {
|
||||
_altRestrict = RESTRICT_AT;
|
||||
} else if (_text == "above") {
|
||||
_altRestrict = RESTRICT_ABOVE;
|
||||
} else if (_text == "below") {
|
||||
_altRestrict = RESTRICT_BELOW;
|
||||
} else {
|
||||
throw sg_format_exception("Unrecognized altitude restriction", _text);
|
||||
}
|
||||
_altRestrict = restrictionFromString(_text);
|
||||
} else if (tag == "Hld_Rad_or_Inbd") {
|
||||
if (_text == "Inbd") {
|
||||
_holdRadial = -1.0;
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include <Navaids/LevelDXML.hxx>
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Navaids/airways.hxx>
|
||||
#include <Environment/atmosphere.hxx> // for Mach conversions
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
@ -123,7 +124,67 @@ WayptRef viaFromString(const SGGeod& basePosition, const std::string& target)
|
|||
return new Via(nullptr, airway, nav);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
static double convertSpeedToKnots(RouteUnits aUnits, double aAltitudeFt, double aValue)
|
||||
{
|
||||
switch (aUnits) {
|
||||
case SPEED_KNOTS: return aValue;
|
||||
case SPEED_KPH: return aValue * SG_KMH_TO_MPS * SG_MPS_TO_KT;
|
||||
case SPEED_MACH: return FGAtmo::knotsFromMachAtAltitudeFt(aValue, aAltitudeFt);
|
||||
|
||||
default:
|
||||
throw sg_format_exception("Can't convert unit to Knots", "convertSpeedToKnots");
|
||||
}
|
||||
}
|
||||
|
||||
static double convertSpeedFromKnots(RouteUnits aUnits, double aAltitudeFt, double aValue)
|
||||
{
|
||||
if (aUnits == DEFAULT_UNITS) {
|
||||
// TODO : use KPH is simulator is in metric
|
||||
aUnits = SPEED_KNOTS;
|
||||
}
|
||||
|
||||
switch (aUnits) {
|
||||
case SPEED_KNOTS: return aValue;
|
||||
case SPEED_KPH: return aValue * SG_KT_TO_MPS * SG_MPS_TO_KMH;
|
||||
case SPEED_MACH: return FGAtmo::machFromKnotsAtAltitudeFt(aValue, aAltitudeFt);
|
||||
|
||||
default:
|
||||
throw sg_format_exception("Can't convert to unit", "convertSpeedFromKnots");
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
double convertSpeedUnits(RouteUnits aSrc, RouteUnits aDest, double aAltitudeFt, double aValue)
|
||||
{
|
||||
const double valueKnots = convertSpeedToKnots(aSrc, aAltitudeFt, aValue);
|
||||
return convertSpeedFromKnots(aDest, aAltitudeFt, valueKnots);
|
||||
}
|
||||
|
||||
double convertAltitudeUnits(RouteUnits aSrc, RouteUnits aDest, double aValue)
|
||||
{
|
||||
if (aDest == DEFAULT_UNITS) {
|
||||
// TODO : use meters if sim is in metric
|
||||
aDest = ALTITUDE_FEET;
|
||||
}
|
||||
|
||||
double altFt = 0.0;
|
||||
switch (aSrc) {
|
||||
case ALTITUDE_FEET: altFt = aValue; break;
|
||||
case ALTITUDE_METER: altFt = aValue * SG_METER_TO_FEET; break;
|
||||
case ALTITUDE_FLIGHTLEVEL: altFt = aValue * 100; break;
|
||||
default:
|
||||
throw sg_format_exception("Unsupported source altitude units", "convertAltitudeUnits");
|
||||
}
|
||||
|
||||
switch (aDest) {
|
||||
case ALTITUDE_FEET: return altFt;
|
||||
case ALTITUDE_METER: return altFt * SG_FEET_TO_METER;
|
||||
case ALTITUDE_FLIGHTLEVEL: return round(altFt / 100);
|
||||
default:
|
||||
throw sg_format_exception("Unsupported destination altitude units", "convertAltitudeUnits");
|
||||
}
|
||||
}
|
||||
|
||||
Waypt::Waypt(RouteBase* aOwner) :
|
||||
_owner(aOwner),
|
||||
|
@ -184,28 +245,80 @@ bool Waypt::matches(const SGGeod& aPos) const
|
|||
return (d < 100.0); // 100 metres seems plenty
|
||||
}
|
||||
|
||||
void Waypt::setAltitude(double aAlt, RouteRestriction aRestrict)
|
||||
void Waypt::setAltitude(double aAlt, RouteRestriction aRestrict, RouteUnits aUnit)
|
||||
{
|
||||
_altitudeFt = aAlt;
|
||||
if (aUnit == DEFAULT_UNITS) {
|
||||
aUnit = ALTITUDE_FEET;
|
||||
}
|
||||
|
||||
_altitude = aAlt;
|
||||
_altitudeUnits = aUnit;
|
||||
_altRestrict = aRestrict;
|
||||
}
|
||||
|
||||
void Waypt::setSpeed(double aSpeed, RouteRestriction aRestrict)
|
||||
void Waypt::setConstraintAltitude(double aAlt)
|
||||
{
|
||||
_constraintAltitude = aAlt;
|
||||
}
|
||||
|
||||
void Waypt::setSpeed(double aSpeed, RouteRestriction aRestrict, RouteUnits aUnit)
|
||||
{
|
||||
if (aUnit == DEFAULT_UNITS) {
|
||||
if ((aRestrict == SPEED_RESTRICT_MACH) || (aRestrict == SPEED_RESTRICT_MACH)) {
|
||||
aUnit = SPEED_MACH;
|
||||
} else {
|
||||
aUnit = SPEED_KNOTS;
|
||||
}
|
||||
}
|
||||
|
||||
_speed = aSpeed;
|
||||
_speedUnits = aUnit;
|
||||
_speedRestrict = aRestrict;
|
||||
}
|
||||
|
||||
double Waypt::speedKts() const
|
||||
{
|
||||
assert(_speedRestrict != SPEED_RESTRICT_MACH);
|
||||
return speed();
|
||||
return speed(SPEED_KNOTS);
|
||||
}
|
||||
|
||||
double Waypt::speedMach() const
|
||||
{
|
||||
assert(_speedRestrict == SPEED_RESTRICT_MACH);
|
||||
return speed();
|
||||
return speed(SPEED_MACH);
|
||||
}
|
||||
|
||||
double Waypt::altitudeFt() const
|
||||
{
|
||||
return altitude(ALTITUDE_FEET);
|
||||
}
|
||||
|
||||
double Waypt::speed(RouteUnits aUnits) const
|
||||
{
|
||||
if (aUnits == _speedUnits) {
|
||||
return _speed;
|
||||
}
|
||||
|
||||
return convertSpeedUnits(_speedUnits, aUnits, altitudeFt(), _speed);
|
||||
}
|
||||
|
||||
double Waypt::altitude(RouteUnits aUnits) const
|
||||
{
|
||||
if (aUnits == _altitudeUnits) {
|
||||
return _altitude;
|
||||
}
|
||||
|
||||
return convertAltitudeUnits(_altitudeUnits, aUnits, _altitude);
|
||||
}
|
||||
|
||||
double Waypt::constraintAltitude(RouteUnits aUnits) const
|
||||
{
|
||||
if (!_constraintAltitude.has_value())
|
||||
return 0.0;
|
||||
|
||||
if (aUnits == _altitudeUnits) {
|
||||
return _constraintAltitude.value_or(0.0);
|
||||
}
|
||||
|
||||
return convertAltitudeUnits(_altitudeUnits, aUnits, _constraintAltitude.value_or(0.0));
|
||||
}
|
||||
|
||||
double Waypt::magvarDeg() const
|
||||
|
@ -234,13 +347,14 @@ std::string Waypt::icaoDescription() const
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// persistence
|
||||
|
||||
static RouteRestriction restrictionFromString(const char* aStr)
|
||||
RouteRestriction restrictionFromString(const std::string& aStr)
|
||||
{
|
||||
std::string l = simgear::strutils::lowercase(aStr);
|
||||
|
||||
if (l == "at") return RESTRICT_AT;
|
||||
if (l == "above") return RESTRICT_ABOVE;
|
||||
if (l == "below") return RESTRICT_BELOW;
|
||||
if (l == "between") return RESTRICT_BETWEEN;
|
||||
if (l == "none") return RESTRICT_NONE;
|
||||
if (l == "mach") return SPEED_RESTRICT_MACH;
|
||||
|
||||
|
@ -256,6 +370,7 @@ const char* restrictionToString(RouteRestriction aRestrict)
|
|||
case RESTRICT_BELOW: return "below";
|
||||
case RESTRICT_ABOVE: return "above";
|
||||
case RESTRICT_NONE: return "none";
|
||||
case RESTRICT_BETWEEN: return "between";
|
||||
case SPEED_RESTRICT_MACH: return "mach";
|
||||
|
||||
default:
|
||||
|
@ -382,16 +497,22 @@ WayptRef Waypt::createFromString(RouteBase* aOwner, const std::string& s, const
|
|||
|
||||
auto target = simgear::strutils::uppercase(s);
|
||||
// extract altitude
|
||||
double altFt = 0.0;
|
||||
double alt = 0.0;
|
||||
RouteRestriction altSetting = RESTRICT_NONE;
|
||||
RouteUnits altitudeUnits = ALTITUDE_FEET;
|
||||
|
||||
size_t pos = target.find('@');
|
||||
if (pos != string::npos) {
|
||||
altFt = std::stof(target.substr(pos + 1));
|
||||
target = target.substr(0, pos);
|
||||
if (fgGetString("/sim/startup/units") == "meter") {
|
||||
altFt *= SG_METER_TO_FEET;
|
||||
auto altStr = simgear::strutils::uppercase(target.substr(pos + 1));
|
||||
if (simgear::strutils::starts_with(altStr, "FL")) {
|
||||
altitudeUnits = ALTITUDE_FLIGHTLEVEL;
|
||||
altStr = altStr.substr(2); // trim leading 'FL'
|
||||
} else if (fgGetString("/sim/startup/units") == "meter") {
|
||||
altitudeUnits = ALTITUDE_METER;
|
||||
}
|
||||
|
||||
alt = std::stof(altStr);
|
||||
target = target.substr(0, pos);
|
||||
altSetting = RESTRICT_AT;
|
||||
}
|
||||
|
||||
|
@ -448,7 +569,7 @@ WayptRef Waypt::createFromString(RouteBase* aOwner, const std::string& s, const
|
|||
}
|
||||
|
||||
if (altSetting != RESTRICT_NONE) {
|
||||
wpt->setAltitude(altFt, altSetting);
|
||||
wpt->setAltitude(alt, altSetting, altitudeUnits);
|
||||
}
|
||||
return wpt;
|
||||
}
|
||||
|
@ -462,42 +583,70 @@ void Waypt::saveAsNode(SGPropertyNode* n) const
|
|||
|
||||
bool Waypt::initFromProperties(SGPropertyNode_ptr aProp)
|
||||
{
|
||||
if (aProp->hasChild("generated")) {
|
||||
setFlag(WPT_GENERATED, aProp->getBoolValue("generated"));
|
||||
}
|
||||
if (aProp->hasChild("generated")) {
|
||||
setFlag(WPT_GENERATED, aProp->getBoolValue("generated"));
|
||||
}
|
||||
|
||||
if (aProp->hasChild("overflight")) {
|
||||
setFlag(WPT_OVERFLIGHT, aProp->getBoolValue("overflight"));
|
||||
}
|
||||
if (aProp->hasChild("overflight")) {
|
||||
setFlag(WPT_OVERFLIGHT, aProp->getBoolValue("overflight"));
|
||||
}
|
||||
|
||||
if (aProp->hasChild("arrival")) {
|
||||
setFlag(WPT_ARRIVAL, aProp->getBoolValue("arrival"));
|
||||
}
|
||||
if (aProp->hasChild("arrival")) {
|
||||
setFlag(WPT_ARRIVAL, aProp->getBoolValue("arrival"));
|
||||
}
|
||||
|
||||
if (aProp->hasChild("approach")) {
|
||||
setFlag(WPT_APPROACH, aProp->getBoolValue("approach"));
|
||||
}
|
||||
if (aProp->hasChild("approach")) {
|
||||
setFlag(WPT_APPROACH, aProp->getBoolValue("approach"));
|
||||
}
|
||||
|
||||
if (aProp->hasChild("departure")) {
|
||||
setFlag(WPT_DEPARTURE, aProp->getBoolValue("departure"));
|
||||
}
|
||||
if (aProp->hasChild("departure")) {
|
||||
setFlag(WPT_DEPARTURE, aProp->getBoolValue("departure"));
|
||||
}
|
||||
|
||||
if (aProp->hasChild("miss")) {
|
||||
setFlag(WPT_MISS, aProp->getBoolValue("miss"));
|
||||
}
|
||||
if (aProp->hasChild("miss")) {
|
||||
setFlag(WPT_MISS, aProp->getBoolValue("miss"));
|
||||
}
|
||||
|
||||
if (aProp->hasChild("airway")) {
|
||||
setFlag(WPT_VIA, true);
|
||||
}
|
||||
if (aProp->hasChild("airway")) {
|
||||
setFlag(WPT_VIA, true);
|
||||
}
|
||||
|
||||
if (aProp->hasChild("alt-restrict")) {
|
||||
_altRestrict = restrictionFromString(aProp->getStringValue("alt-restrict").c_str());
|
||||
_altitudeFt = aProp->getDoubleValue("altitude-ft");
|
||||
if (aProp->hasChild("alt-restrict")) {
|
||||
_altRestrict = restrictionFromString(aProp->getStringValue("alt-restrict"));
|
||||
if (aProp->hasChild("altitude-ft")) {
|
||||
_altitude = aProp->getDoubleValue("altitude-ft");
|
||||
_altitudeUnits = ALTITUDE_FEET;
|
||||
} else if (aProp->hasChild("altitude-m")) {
|
||||
_altitude = aProp->getDoubleValue("altitude-m");
|
||||
_altitudeUnits = ALTITUDE_METER;
|
||||
} else if (aProp->hasChild("flight-level")) {
|
||||
_altitude = aProp->getIntValue("flight-level");
|
||||
_altitudeUnits = ALTITUDE_FLIGHTLEVEL;
|
||||
}
|
||||
|
||||
if (aProp->hasChild("constraint-altitude")) {
|
||||
_constraintAltitude = aProp->getDoubleValue("constraint-altitude");
|
||||
}
|
||||
}
|
||||
|
||||
if (aProp->hasChild("speed-restrict")) {
|
||||
_speedRestrict = restrictionFromString(aProp->getStringValue("speed-restrict").c_str());
|
||||
_speed = aProp->getDoubleValue("speed");
|
||||
_speedRestrict = restrictionFromString(aProp->getStringValue("speed-restrict"));
|
||||
RouteUnits units = SPEED_KNOTS;
|
||||
if (_speedRestrict == SPEED_RESTRICT_MACH) {
|
||||
units = SPEED_MACH;
|
||||
}
|
||||
|
||||
if (aProp->hasChild("speed-mach")) {
|
||||
units = SPEED_MACH;
|
||||
_speed = aProp->getDoubleValue("speed-mach");
|
||||
} else if (aProp->hasChild("speed-kph")) {
|
||||
units = SPEED_KPH;
|
||||
_speed = aProp->getDoubleValue("speed-kph");
|
||||
} else {
|
||||
_speed = aProp->getDoubleValue("speed");
|
||||
}
|
||||
|
||||
_speedUnits = units;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -537,9 +686,20 @@ void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const
|
|||
|
||||
if (_altRestrict != RESTRICT_NONE) {
|
||||
aProp->setStringValue("alt-restrict", restrictionToString(_altRestrict));
|
||||
aProp->setDoubleValue("altitude-ft", _altitudeFt);
|
||||
if (_altitudeUnits == ALTITUDE_METER) {
|
||||
aProp->setDoubleValue("altitude-m", _altitude);
|
||||
} else if (_altitudeUnits == ALTITUDE_FLIGHTLEVEL) {
|
||||
aProp->setDoubleValue("flight-level", _altitude);
|
||||
|
||||
} else {
|
||||
aProp->setDoubleValue("altitude-ft", _altitude);
|
||||
}
|
||||
}
|
||||
|
||||
if (_constraintAltitude.has_value()) {
|
||||
aProp->setDoubleValue("constraint-altitude", _constraintAltitude.value_or(0.0));
|
||||
}
|
||||
|
||||
if (_speedRestrict != RESTRICT_NONE) {
|
||||
aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict));
|
||||
aProp->setDoubleValue("speed", _speed);
|
||||
|
|
|
@ -4,32 +4,16 @@
|
|||
* owns a collection (list, tree, graph) of route elements - such as airways,
|
||||
* procedures or a flight plan.
|
||||
*/
|
||||
// Copyright (C) 2009 James Turner
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Written by James Turner, started 2009.
|
||||
//
|
||||
// Copyright (C) 2009 Curtis L. Olson
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef FG_ROUTE_HXX
|
||||
#define FG_ROUTE_HXX
|
||||
#pragma once
|
||||
|
||||
// std
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <iosfwd>
|
||||
#include <optional>
|
||||
|
||||
// Simgear
|
||||
#include <simgear/structure/SGReferenced.hxx>
|
||||
|
@ -88,14 +72,30 @@ typedef enum {
|
|||
RESTRICT_AT,
|
||||
RESTRICT_ABOVE,
|
||||
RESTRICT_BELOW,
|
||||
RESTRICT_BETWEEN,
|
||||
SPEED_RESTRICT_MACH, ///< encode an 'AT' restriction in Mach, not IAS
|
||||
RESTRICT_DELETE, ///< ignore underlying restriction (on a leg)
|
||||
RESTRICT_COMPUTED, ///< data is computed, not a real restriction
|
||||
SPEED_COMPUTED_MACH ///< variant on above to encode a Mach value
|
||||
} RouteRestriction;
|
||||
|
||||
typedef enum {
|
||||
DEFAULT_UNITS = 0,
|
||||
ALTITUDE_FEET,
|
||||
ALTITUDE_METER,
|
||||
ALTITUDE_FLIGHTLEVEL,
|
||||
SPEED_KNOTS,
|
||||
SPEED_MACH,
|
||||
SPEED_KPH
|
||||
} RouteUnits;
|
||||
|
||||
RouteRestriction restrictionFromString(const std::string& t);
|
||||
|
||||
bool isMachRestrict(RouteRestriction rr);
|
||||
|
||||
double convertSpeedUnits(RouteUnits aSrc, RouteUnits aDest, double aAltitudeFt, double aValue);
|
||||
double convertAltitudeUnits(RouteUnits aSrc, RouteUnits aDest, double aValue);
|
||||
|
||||
/**
|
||||
* Abstract base class for waypoints (and things that are treated similarly
|
||||
* by navigation systems). More precisely this is route path elements,
|
||||
|
@ -117,11 +117,13 @@ public:
|
|||
virtual FGPositioned* source() const
|
||||
{ return nullptr; }
|
||||
|
||||
virtual double altitudeFt() const
|
||||
{ return _altitudeFt; }
|
||||
double altitudeFt() const;
|
||||
|
||||
virtual double speed() const
|
||||
{ return _speed; }
|
||||
double speed(RouteUnits aUnits = DEFAULT_UNITS) const;
|
||||
|
||||
double altitude(RouteUnits aUnits = DEFAULT_UNITS) const;
|
||||
|
||||
double constraintAltitude(RouteUnits aUnits = DEFAULT_UNITS) const;
|
||||
|
||||
// wrapper - asserts if restriction type is _MACH
|
||||
double speedKts() const;
|
||||
|
@ -135,8 +137,10 @@ public:
|
|||
virtual RouteRestriction speedRestriction() const
|
||||
{ return _speedRestrict; }
|
||||
|
||||
void setAltitude(double aAlt, RouteRestriction aRestrict);
|
||||
void setSpeed(double aSpeed, RouteRestriction aRestrict);
|
||||
void setAltitude(double aAlt, RouteRestriction aRestrict, RouteUnits aUnits = DEFAULT_UNITS);
|
||||
void setSpeed(double aSpeed, RouteRestriction aRestrict, RouteUnits aUnits = DEFAULT_UNITS);
|
||||
|
||||
void setConstraintAltitude(double aAlt);
|
||||
|
||||
/**
|
||||
* Identifier assoicated with the waypoint. Human-readable, but
|
||||
|
@ -230,8 +234,16 @@ protected:
|
|||
typedef Waypt*(FactoryFunction)(RouteBase* aOwner);
|
||||
static void registerFactory(const std::string aNodeType, FactoryFunction* aFactory);
|
||||
|
||||
double _altitudeFt = 0.0;
|
||||
double _speed = 0.0; // knots IAS or mach
|
||||
double _altitude = 0.0;
|
||||
/// some restriction types specify two altitudes, in which case this is the second value, corresponding to
|
||||
/// AltitudeCons in the level-D XML procedures format.
|
||||
std::optional<double> _constraintAltitude;
|
||||
|
||||
RouteUnits _altitudeUnits = ALTITUDE_FEET;
|
||||
|
||||
double _speed = 0.0;
|
||||
RouteUnits _speedUnits = SPEED_KNOTS;
|
||||
|
||||
RouteRestriction _altRestrict = RESTRICT_NONE;
|
||||
RouteRestriction _speedRestrict = RESTRICT_NONE;
|
||||
private:
|
||||
|
@ -269,5 +281,3 @@ private:
|
|||
};
|
||||
|
||||
} // of namespace flightgear
|
||||
|
||||
#endif // of FG_ROUTE_HXX
|
||||
|
|
|
@ -96,7 +96,7 @@ NavaidWaypoint::NavaidWaypoint(RouteBase* aOwner) :
|
|||
|
||||
SGGeod NavaidWaypoint::position() const
|
||||
{
|
||||
return SGGeod::fromGeodFt(_navaid->geod(), _altitudeFt);
|
||||
return SGGeod::fromGeodFt(_navaid->geod(), altitudeFt());
|
||||
}
|
||||
|
||||
std::string NavaidWaypoint::ident() const
|
||||
|
@ -170,7 +170,7 @@ void OffsetNavaidWaypoint::init()
|
|||
SGGeod offset;
|
||||
double az2;
|
||||
SGGeodesy::direct(_navaid->geod(), _radial, _distanceNm * SG_NM_TO_METER, offset, az2);
|
||||
_geod = SGGeod::fromGeodFt(offset, _altitudeFt);
|
||||
_geod = SGGeod::fromGeodFt(offset, altitude(ALTITUDE_FEET));
|
||||
}
|
||||
|
||||
bool OffsetNavaidWaypoint::initFromProperties(SGPropertyNode_ptr aProp)
|
||||
|
|
|
@ -745,4 +745,5 @@ bool FGCom::isInRange(const double &freq) const
|
|||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<FGCom> registrantFGCom;
|
||||
SGSubsystemMgr::Registrant<FGCom> registrantFGCom(
|
||||
SGSubsystemMgr::SOUND);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/scene/model/modellib.hxx>
|
||||
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
|
||||
#include <simgear/scene/tgdb/VPBTechnique.hxx>
|
||||
#include <simgear/scene/tsync/terrasync.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/scene/material/matlib.hxx>
|
||||
|
@ -497,6 +498,11 @@ void FGTileMgr::update_queues(bool& isDownloadingScenery)
|
|||
|
||||
tile_cache.clear_entry(drop_index);
|
||||
|
||||
if (_use_vpb) {
|
||||
// Clear out any VPB data - e.g. roads
|
||||
simgear::VPBTechnique::unloadFeatures(old->get_tile_bucket());
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Object> subgraph = old->getNode();
|
||||
old->removeFromSceneGraph();
|
||||
delete old;
|
||||
|
|
|
@ -464,9 +464,27 @@ static RouteRestriction routeRestrictionFromArg(naRef arg)
|
|||
if (u == "mach") return SPEED_RESTRICT_MACH;
|
||||
if (u == "computed-mach") return SPEED_COMPUTED_MACH;
|
||||
if (u == "delete") return RESTRICT_DELETE;
|
||||
if (u == "between") return RESTRICT_BETWEEN;
|
||||
return RESTRICT_NONE;
|
||||
};
|
||||
|
||||
static RouteUnits routeUnitsFromArg(naRef arg)
|
||||
{
|
||||
if (naIsNil(arg) || !naIsString(arg)) {
|
||||
return DEFAULT_UNITS;
|
||||
}
|
||||
|
||||
const auto u = simgear::strutils::lowercase(naStr_data(arg));
|
||||
if ((u == "knots") || (u == "kt")) return SPEED_KNOTS;
|
||||
if (u == "kph") return SPEED_KPH;
|
||||
if (u == "mach") return SPEED_MACH;
|
||||
if ((u == "ft") || (u == "\'") || (u == "feet")) return ALTITUDE_FEET;
|
||||
if ((u == "m") || (u == "meter") || (u == "meters")) return ALTITUDE_METER;
|
||||
if ((u == "fl") || (u == "flight-level")) return ALTITUDE_FLIGHTLEVEL;
|
||||
|
||||
throw sg_format_exception("routeUnitsFromArg: unknown units", u);
|
||||
}
|
||||
|
||||
naRef routeRestrictionToNasal(naContext c, RouteRestriction rr)
|
||||
{
|
||||
switch (rr) {
|
||||
|
@ -478,6 +496,7 @@ naRef routeRestrictionToNasal(naContext c, RouteRestriction rr)
|
|||
case RESTRICT_COMPUTED: return stringToNasal(c, "computed");
|
||||
case SPEED_COMPUTED_MACH: return stringToNasal(c, "computed-mach");
|
||||
case RESTRICT_DELETE: return stringToNasal(c, "delete");
|
||||
case RESTRICT_BETWEEN: return stringToNasal(c, "between");
|
||||
}
|
||||
|
||||
return naNil();
|
||||
|
@ -1984,6 +2003,8 @@ static naRef f_leg_setSpeed(naContext c, naRef me, int argc, naRef* args)
|
|||
|
||||
double speed = 0.0;
|
||||
RouteRestriction rr = RESTRICT_AT;
|
||||
RouteUnits units = DEFAULT_UNITS;
|
||||
|
||||
if (argc > 0) {
|
||||
if (naIsNil(args[0])) {
|
||||
// clear the restriction to NONE
|
||||
|
@ -1994,9 +2015,13 @@ static naRef f_leg_setSpeed(naContext c, naRef me, int argc, naRef* args)
|
|||
} else {
|
||||
naRuntimeError(c, "bad arguments to setSpeed");
|
||||
}
|
||||
|
||||
if (argc > 2) {
|
||||
units = routeUnitsFromArg(args[2]);
|
||||
}
|
||||
}
|
||||
|
||||
leg->setSpeed(rr, speed);
|
||||
leg->setSpeed(rr, speed, units);
|
||||
} else {
|
||||
naRuntimeError(c, "bad arguments to setSpeed");
|
||||
}
|
||||
|
@ -2013,6 +2038,8 @@ static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args)
|
|||
|
||||
double altitude = 0.0;
|
||||
RouteRestriction rr = RESTRICT_AT;
|
||||
RouteUnits units = DEFAULT_UNITS;
|
||||
|
||||
if (argc > 0) {
|
||||
if (naIsNil(args[0])) {
|
||||
// clear the restriction to NONE
|
||||
|
@ -2023,9 +2050,37 @@ static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args)
|
|||
} else {
|
||||
naRuntimeError(c, "bad arguments to leg.setAltitude");
|
||||
}
|
||||
|
||||
if (argc > 2) {
|
||||
units = routeUnitsFromArg(args[2]);
|
||||
}
|
||||
} else if (naIsVector(args[0])) {
|
||||
const auto altTuple = args[0];
|
||||
// we need a second restriction type arg, and the tuple should be of length 2
|
||||
if ((argc < 2) || (naVec_size(altTuple) != 2)) {
|
||||
naRuntimeError(c, "missing/bad arguments to leg.setAltitude");
|
||||
}
|
||||
|
||||
rr = routeRestrictionFromArg(args[1]);
|
||||
if (rr != RESTRICT_BETWEEN) {
|
||||
naRuntimeError(c, "leg.setAltitude: passed a 2-tuple, but restriction type is not 'between'");
|
||||
}
|
||||
|
||||
double constraintAltitude;
|
||||
const auto ok = convertToNum(naVec_get(altTuple, 0), constraintAltitude)
|
||||
&& convertToNum(naVec_get(altTuple, 1), altitude);
|
||||
if (!ok) {
|
||||
naRuntimeError(c, "leg.setAltitude: tuple members not convertible to numeric altitudes");
|
||||
}
|
||||
|
||||
if (argc > 2) {
|
||||
units = routeUnitsFromArg(args[2]);
|
||||
}
|
||||
|
||||
// TODO: store constraint altitude
|
||||
}
|
||||
|
||||
leg->setAltitude(rr, altitude);
|
||||
leg->setAltitude(rr, altitude, units);
|
||||
} else {
|
||||
naRuntimeError(c, "bad arguments to setleg.setAltitude");
|
||||
}
|
||||
|
@ -2033,6 +2088,43 @@ static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args)
|
|||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_leg_altitude(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
FlightPlan::Leg* leg = fpLegGhost(me);
|
||||
if (!leg) {
|
||||
naRuntimeError(c, "leg.altitude called on non-flightplan-leg object");
|
||||
}
|
||||
|
||||
RouteUnits units = DEFAULT_UNITS;
|
||||
if (argc > 0) {
|
||||
units = routeUnitsFromArg(args[9]);
|
||||
}
|
||||
|
||||
if (leg->altitudeRestriction() == RESTRICT_BETWEEN) {
|
||||
naRef result = naNewVector(c);
|
||||
naVec_append(result, naNum(leg->waypoint()->constraintAltitude(units)));
|
||||
naVec_append(result, naNum(leg->altitude(units)));
|
||||
return result;
|
||||
}
|
||||
|
||||
return naNum(leg->altitude(units));
|
||||
}
|
||||
|
||||
static naRef f_leg_speed(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
FlightPlan::Leg* leg = fpLegGhost(me);
|
||||
if (!leg) {
|
||||
naRuntimeError(c, "leg.speed called on non-flightplan-leg object");
|
||||
}
|
||||
|
||||
RouteUnits units = DEFAULT_UNITS;
|
||||
if (argc > 0) {
|
||||
units = routeUnitsFromArg(args[9]);
|
||||
}
|
||||
|
||||
return naNum(leg->speed(units));
|
||||
}
|
||||
|
||||
static naRef f_leg_courseAndDistanceFrom(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
FlightPlan::Leg* leg = fpLegGhost(me);
|
||||
|
@ -2301,6 +2393,8 @@ naRef initNasalFlightPlan(naRef globals, naContext c)
|
|||
naSave(c, fpLegPrototype);
|
||||
hashset(c, fpLegPrototype, "setSpeed", naNewFunc(c, naNewCCode(c, f_leg_setSpeed)));
|
||||
hashset(c, fpLegPrototype, "setAltitude", naNewFunc(c, naNewCCode(c, f_leg_setAltitude)));
|
||||
hashset(c, fpLegPrototype, "altitude", naNewFunc(c, naNewCCode(c, f_leg_altitude)));
|
||||
hashset(c, fpLegPrototype, "speed", naNewFunc(c, naNewCCode(c, f_leg_speed)));
|
||||
hashset(c, fpLegPrototype, "path", naNewFunc(c, naNewCCode(c, f_leg_path)));
|
||||
hashset(c, fpLegPrototype, "courseAndDistanceFrom", naNewFunc(c, naNewCCode(c, f_leg_courseAndDistanceFrom)));
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ foreach( simgear_test_category
|
|||
props
|
||||
canvas
|
||||
structure
|
||||
timing
|
||||
)
|
||||
|
||||
add_subdirectory(${simgear_test_category})
|
||||
|
|
12
test_suite/simgear_tests/timing/CMakeLists.txt
Normal file
12
test_suite/simgear_tests/timing/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_SGTime.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_SGTime.hxx
|
||||
PARENT_SCOPE
|
||||
)
|
11
test_suite/simgear_tests/timing/TestSuite.cxx
Normal file
11
test_suite/simgear_tests/timing/TestSuite.cxx
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2022 James Turner <james@flightgear.org>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
|
||||
#include "test_SGTime.hxx"
|
||||
|
||||
|
||||
// Set up the tests.
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SimgearTimingTests, "Simgear unit tests");
|
51
test_suite/simgear_tests/timing/test_SGTime.cxx
Normal file
51
test_suite/simgear_tests/timing/test_SGTime.cxx
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2022 James Turner <james@flightgear.org>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "test_SGTime.hxx"
|
||||
#include "test_suite/dataStore.hxx"
|
||||
|
||||
#include <simgear/math/SGGeod.hxx>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Set up function for each test.
|
||||
void SimgearTimingTests::setUp()
|
||||
{
|
||||
}
|
||||
|
||||
// Clean up after each test.
|
||||
void SimgearTimingTests::tearDown()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SimgearTimingTests::testBadZoneDetectPosition()
|
||||
{
|
||||
DataStore &data = DataStore::get();
|
||||
const SGPath rootPath{data.getFGRoot()};
|
||||
if (rootPath.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<SGTime> t(new SGTime{rootPath});
|
||||
|
||||
const auto goodPos = SGGeod::fromDeg(-69.5, 12.0);
|
||||
CPPUNIT_ASSERT(t->updateLocal(goodPos, rootPath / "Timezone"));
|
||||
|
||||
// discovered while flying with the Shuttle, but actually
|
||||
// can happen at sea level.
|
||||
// https://sourceforge.net/p/flightgear/codetickets/2780/
|
||||
|
||||
// this was fixed by updating the timezone data from
|
||||
// https://github.com/BertoldVdb/ZoneDetect into FGData/Timezone/timezone16.bin
|
||||
const auto pos = SGGeod::fromDeg(-69.0, 12.0);
|
||||
CPPUNIT_ASSERT(t->updateLocal(pos, rootPath / "Timezone"));
|
||||
}
|
33
test_suite/simgear_tests/timing/test_SGTime.hxx
Normal file
33
test_suite/simgear_tests/timing/test_SGTime.hxx
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2022 James Turner <james@flightgear.org>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
#include <simgear/timing/sg_time.hxx>
|
||||
|
||||
// The unit tests of the simgear property tree.
|
||||
class SimgearTimingTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(SimgearTimingTests);
|
||||
CPPUNIT_TEST(testBadZoneDetectPosition);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testBadZoneDetectPosition();
|
||||
|
||||
private:
|
||||
|
||||
};
|
|
@ -702,6 +702,77 @@ void FlightplanTests::testLoadSaveMachRestriction()
|
|||
CPPUNIT_ASSERT_EQUAL(8, f2->legAtIndex(2)->holdCount());
|
||||
}
|
||||
|
||||
void FlightplanTests::testLoadSaveBetweenRestriction()
|
||||
{
|
||||
const std::string fpXML = R"(<?xml version="1.0" encoding="UTF-8"?>
|
||||
<PropertyList>
|
||||
<version type="int">2</version>
|
||||
<departure>
|
||||
<airport type="string">SAWG</airport>
|
||||
<runway type="string">25</runway>
|
||||
</departure>
|
||||
<destination>
|
||||
<airport type="string">SUMU</airport>
|
||||
</destination>
|
||||
<route>
|
||||
<wp n="0">
|
||||
<type type="string">navaid</type>
|
||||
<ident type="string">PUGLI</ident>
|
||||
<lon type="double">-60.552200</lon>
|
||||
<lat type="double">-40.490000</lat>
|
||||
</wp>
|
||||
<wp n="1">
|
||||
<type type="string">basic</type>
|
||||
<alt-restrict type="string">between</alt-restrict>
|
||||
<altitude-ft type="double">36000</altitude-ft>
|
||||
<constraint-altitude type="double">32000</constraint-altitude>
|
||||
<ident type="string">SV002</ident>
|
||||
<lon type="double">-115.50531</lon>
|
||||
<lat type="double">37.89523</lat>
|
||||
</wp>
|
||||
<wp n="2">
|
||||
<type type="string">navaid</type>
|
||||
<ident type="string">SIGUL</ident>
|
||||
<lon type="double">-60.552200</lon>
|
||||
<lat type="double">-40.490000</lat>
|
||||
</wp>
|
||||
</route>
|
||||
</PropertyList>
|
||||
)";
|
||||
|
||||
std::istringstream stream(fpXML);
|
||||
FlightPlanRef f = FlightPlan::create();
|
||||
bool ok = f->load(stream);
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
auto leg = f->legAtIndex(1);
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_BETWEEN, leg->altitudeRestriction());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(36000, leg->altitudeFt(), 0.01);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(32000, leg->waypoint()->constraintAltitude(ALTITUDE_FEET), 0.01);
|
||||
}
|
||||
|
||||
void FlightplanTests::testRestrictionUnits()
|
||||
{
|
||||
FlightPlanRef fp1 = makeTestFP("LIRF"s, "34L"s, "LEBL"s, "25R"s,
|
||||
"ESINO GITRI BALEN MUREN TOSNU"s);
|
||||
auto leg = fp1->legAtIndex(2);
|
||||
leg->setAltitude(RESTRICT_ABOVE, 120, ALTITUDE_FLIGHTLEVEL);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(leg->altitude(), 12000.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(leg->altitude(ALTITUDE_METER), 12000.0 * SG_FEET_TO_METER, 0.1);
|
||||
|
||||
leg->setSpeed(RESTRICT_AT, 300);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(leg->speedKts(), 300, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(leg->speed(), 300, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(leg->speed(SPEED_KPH), 300 * SG_KT_TO_MPS * SG_MPS_TO_KMH, 0.1);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(leg->speedMach(), 0.473, 0.1);
|
||||
|
||||
// setting a mach restriction assumes Mach units by default
|
||||
leg->setSpeed(SPEED_RESTRICT_MACH, 0.7);
|
||||
leg->setAltitude(RESTRICT_AT, 38000);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(leg->speedKts(), 401, 1);
|
||||
}
|
||||
|
||||
void FlightplanTests::testBasicDiscontinuity()
|
||||
{
|
||||
FlightPlanRef fp1 = makeTestFP("LIRF"s, "34L"s, "LEBL"s, "25R"s,
|
||||
|
|
|
@ -58,9 +58,11 @@ class FlightplanTests : public CppUnit::TestFixture
|
|||
CPPUNIT_TEST(testViaInsertIntoFP);
|
||||
CPPUNIT_TEST(testViaInsertIntoRoute);
|
||||
CPPUNIT_TEST(loadFGFPAsRoute);
|
||||
CPPUNIT_TEST(testLoadSaveBetweenRestriction);
|
||||
CPPUNIT_TEST(testRestrictionUnits);
|
||||
|
||||
// CPPUNIT_TEST(testParseICAORoute);
|
||||
// CPPUNIT_TEST(testParseICANLowLevelRoute);
|
||||
// CPPUNIT_TEST(testParseICAORoute);
|
||||
// CPPUNIT_TEST(testParseICANLowLevelRoute);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
|
@ -100,6 +102,8 @@ public:
|
|||
void testViaInsertIntoFP();
|
||||
void testViaInsertIntoRoute();
|
||||
void loadFGFPAsRoute();
|
||||
void testLoadSaveBetweenRestriction();
|
||||
void testRestrictionUnits();
|
||||
};
|
||||
|
||||
#endif // FG_FLIGHTPLAN_UNIT_TESTS_HXX
|
||||
|
|
|
@ -197,6 +197,19 @@ void FPNasalTests::testRestrictions()
|
|||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_DELETE, fp1->legAtIndex(3)->speedRestriction());
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
var leg = fp.getWP(3);
|
||||
leg.setSpeed(0.8, 'at', "Mach");
|
||||
leg.setAltitude(300, 'above', 'FL');
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_AT, fp1->legAtIndex(3)->speedRestriction());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.8, fp1->legAtIndex(3)->speedMach(), 0.01);
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_ABOVE, fp1->legAtIndex(3)->altitudeRestriction());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(30000, fp1->legAtIndex(3)->altitudeFt(), 1.0);
|
||||
}
|
||||
|
||||
void FPNasalTests::testSegfaultWaypointGhost()
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
|
||||
|
||||
if (ENABLE_SWIFT)
|
||||
set(SWIFT_TESTS_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftService.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftAircraftManager.cxx
|
||||
)
|
||||
|
||||
set(SWIFT_TESTS_HEADERS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftService.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftAircraftManager.hxx
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftService.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftAircraftManager.cxx
|
||||
${SWIFT_TESTS_SOURCES}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftService.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftAircraftManager.hxx
|
||||
${SWIFT_TESTS_HEADERS}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
|
|
|
@ -3,9 +3,15 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if defined(ENABLE_SWIFT)
|
||||
|
||||
#include "test_swiftAircraftManager.hxx"
|
||||
#include "test_swiftService.hxx"
|
||||
|
||||
// Set up the unit tests.
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SwiftAircraftManagerTest, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SwiftServiceTest, "Unit tests");
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue