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
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#include <config.h>
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <simgear/math/sg_random.hxx>
|
#include <simgear/math/sg_random.hxx>
|
||||||
#include <simgear/math/sg_geodesy.hxx>
|
#include <simgear/math/sg_geodesy.hxx>
|
||||||
|
@ -33,6 +31,7 @@
|
||||||
|
|
||||||
#include <Main/util.hxx>
|
#include <Main/util.hxx>
|
||||||
#include <Environment/gravity.hxx>
|
#include <Environment/gravity.hxx>
|
||||||
|
#include <Environment/atmosphere.hxx>
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
|
|
||||||
using namespace simgear;
|
using namespace simgear;
|
||||||
|
@ -695,6 +694,10 @@ void FGAIBallistic::Run(double dt) {
|
||||||
// for a conventional shell/bullet (no boat-tail).
|
// for a conventional shell/bullet (no boat-tail).
|
||||||
double Cdm;
|
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)
|
if (Mach < 0.7)
|
||||||
Cdm = 0.0125 * Mach + _cd;
|
Cdm = 0.0125 * Mach + _cd;
|
||||||
else if (Mach < 1.2)
|
else if (Mach < 1.2)
|
||||||
|
|
|
@ -193,12 +193,6 @@ FGAIBase::FGAIBase(object_type ot, bool enableHot) : replay_time(fgGetNode("sim/
|
||||||
|
|
||||||
serviceable = false;
|
serviceable = false;
|
||||||
|
|
||||||
rho = 1;
|
|
||||||
T = 280;
|
|
||||||
p = 1e5;
|
|
||||||
a = 340;
|
|
||||||
Mach = 0;
|
|
||||||
|
|
||||||
// explicitly disable HOT for (most) AI models
|
// explicitly disable HOT for (most) AI models
|
||||||
if (!enableHot)
|
if (!enableHot)
|
||||||
aip.getSceneGraph()->setNodeMask(~SG_NODEMASK_TERRAIN_BIT);
|
aip.getSceneGraph()->setNodeMask(~SG_NODEMASK_TERRAIN_BIT);
|
||||||
|
@ -309,9 +303,6 @@ void FGAIBase::update(double dt) {
|
||||||
if (_otype == object_type::otStatic)
|
if (_otype == object_type::otStatic)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_otype == object_type::otBallistic)
|
|
||||||
CalculateMach();
|
|
||||||
|
|
||||||
ft_per_deg_lat = 366468.96 - 3717.12 * cos(pos.getLatitudeRad());
|
ft_per_deg_lat = 366468.96 - 3717.12 * cos(pos.getLatitudeRad());
|
||||||
ft_per_deg_lon = 365228.16 * cos(pos.getLatitudeRad());
|
ft_per_deg_lon = 365228.16 * cos(pos.getLatitudeRad());
|
||||||
|
|
||||||
|
@ -1193,38 +1184,6 @@ int FGAIBase::_getFallbackModelIndex() const {
|
||||||
return _fallback_model_index;
|
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() {
|
int FGAIBase::_newAIModelID() {
|
||||||
static int id = 0;
|
static int id = 0;
|
||||||
|
|
||||||
|
|
|
@ -292,7 +292,6 @@ protected:
|
||||||
|
|
||||||
ModelSearchOrder _searchOrder = ModelSearchOrder::DATA_ONLY;
|
ModelSearchOrder _searchOrder = ModelSearchOrder::DATA_ONLY;
|
||||||
void Transform();
|
void Transform();
|
||||||
void CalculateMach();
|
|
||||||
double UpdateRadar(FGAIManager* manager);
|
double UpdateRadar(FGAIManager* manager);
|
||||||
|
|
||||||
void removeModel();
|
void removeModel();
|
||||||
|
@ -377,13 +376,6 @@ public:
|
||||||
const char* _getSubmodel() const;
|
const char* _getSubmodel() const;
|
||||||
int _getFallbackModelIndex() 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 e;
|
||||||
static const double lbs_to_slugs;
|
static const double lbs_to_slugs;
|
||||||
|
|
||||||
|
|
|
@ -123,4 +123,5 @@ void FGReplay::resetStatisticsProperties()
|
||||||
|
|
||||||
|
|
||||||
// Register the subsystem.
|
// 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)
|
// route_mgr.cxx - manage a route (i.e. a collection of waypoints)
|
||||||
//
|
/*
|
||||||
// Written by Curtis Olson, started January 2004.
|
* SPDX-FileCopyrightText: (C) 2004 Curtis L. Olson http://www.flightgear.org/~curt
|
||||||
// Norman Vine
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
// 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$
|
|
||||||
|
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
@ -372,7 +354,8 @@ void FGRouteMgr::init() {
|
||||||
|
|
||||||
_edited = fgGetNode(RM "signals/edited", true);
|
_edited = fgGetNode(RM "signals/edited", true);
|
||||||
_flightplanChanged = fgGetNode(RM "signals/flightplan-changed", true);
|
_flightplanChanged = fgGetNode(RM "signals/flightplan-changed", true);
|
||||||
|
_isRoute = fgGetNode(RM "is-route", true);
|
||||||
|
|
||||||
_currentWpt = fgGetNode(RM "current-wp", true);
|
_currentWpt = fgGetNode(RM "current-wp", true);
|
||||||
_currentWpt->setAttribute(SGPropertyNode::LISTENER_SAFE, true);
|
_currentWpt->setAttribute(SGPropertyNode::LISTENER_SAFE, true);
|
||||||
_currentWpt->tie(SGRawValueMethods<FGRouteMgr, int>
|
_currentWpt->tie(SGRawValueMethods<FGRouteMgr, int>
|
||||||
|
@ -411,6 +394,8 @@ void FGRouteMgr::postinit()
|
||||||
loadRoute(path);
|
loadRoute(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isRoute->setBoolValue(_plan->isRoute());
|
||||||
|
|
||||||
// this code only matters for the --wp option now - perhaps the option
|
// this code only matters for the --wp option now - perhaps the option
|
||||||
// should be deprecated in favour of an explicit flight-plan file?
|
// should be deprecated in favour of an explicit flight-plan file?
|
||||||
// then the global initial waypoint list could die.
|
// then the global initial waypoint list could die.
|
||||||
|
@ -486,7 +471,7 @@ void FGRouteMgr::setFlightPlan(const FlightPlanRef& plan)
|
||||||
|
|
||||||
_plan = plan;
|
_plan = plan;
|
||||||
_plan->addDelegate(this);
|
_plan->addDelegate(this);
|
||||||
|
_isRoute->setBoolValue(_plan->isRoute());
|
||||||
_flightplanChanged->fireValueChanged();
|
_flightplanChanged->fireValueChanged();
|
||||||
|
|
||||||
// fire all the callbacks!
|
// fire all the callbacks!
|
||||||
|
|
|
@ -1,24 +1,8 @@
|
||||||
// route_mgr.hxx - manage a route (i.e. a collection of waypoints)
|
// route_mgr.hxx - manage a route (i.e. a collection of waypoints)
|
||||||
//
|
/*
|
||||||
// Written by Curtis Olson, started January 2004.
|
* SPDX-FileCopyrightText: (C) 2004 Curtis L. Olson http://www.flightgear.org/~curt
|
||||||
//
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
// 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$
|
|
||||||
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -144,7 +128,7 @@ private:
|
||||||
|
|
||||||
SGPropertyNode_ptr _pathNode;
|
SGPropertyNode_ptr _pathNode;
|
||||||
SGPropertyNode_ptr _currentWpt;
|
SGPropertyNode_ptr _currentWpt;
|
||||||
|
SGPropertyNode_ptr _isRoute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signal property to notify people that the route was edited
|
* 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;
|
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
|
// force retabulation
|
||||||
void FGAtmoCache::tabulate() {
|
void FGAtmoCache::tabulate() {
|
||||||
using namespace atmodel;
|
using namespace atmodel;
|
||||||
|
|
|
@ -117,6 +117,30 @@ public:
|
||||||
* or millibars
|
* or millibars
|
||||||
*/
|
*/
|
||||||
static double fieldPressure(const double field_elev, const double qnh);
|
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();
|
FGGroundReactions* gr=fdmex->GetGroundReactions();
|
||||||
int Ngear=GroundReactions->GetNumGearUnits();
|
int Ngear=GroundReactions->GetNumGearUnits();
|
||||||
|
double max = 1.0;
|
||||||
for (int i=0;i<Ngear;i++) {
|
for (int i=0;i<Ngear;i++) {
|
||||||
FGLGear *gear = gr->GetGearUnit(i);
|
FGLGear *gear = gr->GetGearUnit(i);
|
||||||
SGPropertyNode * node = fgGetNode("gear/gear", i, true);
|
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("yoffset-in", gear->GetBodyLocation()(2) * 12);
|
||||||
node->setDoubleValue("zoffset-in", gear->GetBodyLocation()(3) * 12);
|
node->setDoubleValue("zoffset-in", gear->GetBodyLocation()(3) * 12);
|
||||||
|
|
||||||
node->setDoubleValue("xoffset-m", gear->GetBodyLocation()(1) * 0.08333333);
|
node->setDoubleValue("xoffset-m", gear->GetBodyLocation()(1) * SG_FEET_TO_METER);
|
||||||
node->setDoubleValue("yoffset-m", gear->GetBodyLocation()(2) * 0.08333333);
|
node->setDoubleValue("yoffset-m", gear->GetBodyLocation()(2) * SG_FEET_TO_METER);
|
||||||
node->setDoubleValue("zoffset-m", gear->GetBodyLocation()(3) * 0.08333333);
|
node->setDoubleValue("zoffset-m", gear->GetBodyLocation()(3) * SG_FEET_TO_METER);
|
||||||
|
|
||||||
node->setBoolValue("wow", gear->GetWOW());
|
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->setBoolValue("has-brake", gear->GetBrakeGroup() > 0);
|
||||||
node->setDoubleValue("position-norm", gear->GetGearUnitPos());
|
node->setDoubleValue("position-norm", gear->GetGearUnitPos());
|
||||||
// node->setDoubleValue("tire-pressure-norm", gear->GetTirePressure());
|
// 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());
|
node->setDoubleValue("compression-ft", gear->GetCompLen());
|
||||||
if ( gear->GetSteerable() )
|
if ( gear->GetSteerable() )
|
||||||
node->setDoubleValue("steering-norm", gear->GetSteerNorm());
|
node->setDoubleValue("steering-norm", gear->GetSteerNorm());
|
||||||
|
@ -1274,14 +1282,22 @@ void FGJSBsim::update_gear(void)
|
||||||
{
|
{
|
||||||
FGGroundReactions* gr=fdmex->GetGroundReactions();
|
FGGroundReactions* gr=fdmex->GetGroundReactions();
|
||||||
int Ngear=GroundReactions->GetNumGearUnits();
|
int Ngear=GroundReactions->GetNumGearUnits();
|
||||||
|
double max = 1.0;
|
||||||
for (int i=0;i<Ngear;i++) {
|
for (int i=0;i<Ngear;i++) {
|
||||||
FGLGear *gear = gr->GetGearUnit(i);
|
FGLGear *gear = gr->GetGearUnit(i);
|
||||||
SGPropertyNode * node = fgGetNode("gear/gear", i, true);
|
SGPropertyNode * node = fgGetNode("gear/gear", i, true);
|
||||||
node->getChild("wow", 0, true)->setBoolValue( gear->GetWOW());
|
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());
|
node->getChild("position-norm", 0, true)->setDoubleValue(gear->GetGearUnitPos());
|
||||||
// gear->SetTirePressure(node->getDoubleValue("tire-pressure-norm"));
|
// 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());
|
node->setDoubleValue("compression-ft", gear->GetCompLen());
|
||||||
if ( gear->GetSteerable() )
|
if ( gear->GetSteerable() )
|
||||||
node->setDoubleValue("steering-norm", gear->GetSteerNorm());
|
node->setDoubleValue("steering-norm", gear->GetSteerNorm());
|
||||||
|
|
|
@ -649,7 +649,8 @@ void HUDText::draw()
|
||||||
|
|
||||||
|
|
||||||
// Register the subsystem.
|
// 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,
|
void TextList::align(const char *s, int align, float *x, float *y,
|
||||||
|
|
|
@ -1051,7 +1051,7 @@ void fgCreateSubsystems(bool duringReset) {
|
||||||
// autopilot.)
|
// autopilot.)
|
||||||
mgr->add<FGSystemMgr>();
|
mgr->add<FGSystemMgr>();
|
||||||
mgr->add<FGInstrumentMgr>();
|
mgr->add<FGInstrumentMgr>();
|
||||||
mgr->add("xml-autopilot", FGXMLAutopilotGroup::createInstance("autopilot"));
|
mgr->add("xml-autopilot", FGXMLAutopilotGroup::createInstance("autopilot"), SGSubsystemMgr::FDM);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SGSubsystemMgr::POST_FDM
|
// SGSubsystemMgr::POST_FDM
|
||||||
|
@ -1106,7 +1106,7 @@ void fgCreateSubsystems(bool duringReset) {
|
||||||
mgr->add<CanvasMgr>();
|
mgr->add<CanvasMgr>();
|
||||||
|
|
||||||
auto canvasGui = new GUIMgr;
|
auto canvasGui = new GUIMgr;
|
||||||
mgr->add("CanvasGUI", canvasGui);
|
mgr->add("CanvasGUI", canvasGui, SGSubsystemMgr::DISPLAY);
|
||||||
auto guiCamera = flightgear::getGUICamera(flightgear::CameraGroup::getDefault());
|
auto guiCamera = flightgear::getGUICamera(flightgear::CameraGroup::getDefault());
|
||||||
canvasGui->setGUIViewAndCamera(globals->get_renderer()->getView(), guiCamera);
|
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
|
// ordering here is important : Nasal (via events), then models, then views
|
||||||
if (!duringReset) {
|
if (!duringReset) {
|
||||||
mgr->add<FGLight>();
|
mgr->add<FGLight>();
|
||||||
mgr->add("events", globals->get_event_mgr());
|
mgr->add("events", globals->get_event_mgr(), SGSubsystemMgr::DISPLAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
mgr->add<FGAircraftModel>();
|
mgr->add<FGAircraftModel>();
|
||||||
|
@ -1435,20 +1435,22 @@ void fgStartNewReset()
|
||||||
|
|
||||||
sgUserDataInit( globals->get_props() );
|
sgUserDataInit( globals->get_props() );
|
||||||
|
|
||||||
|
unsigned int numDBPagerThreads = fgGetNode("/sim/rendering/database-pager/threads", true)->getIntValue(2);
|
||||||
|
|
||||||
if (composite_viewer) {
|
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);
|
composite_viewer_view->getDatabasePager()->setAcceptNewDatabaseRequests(true);
|
||||||
flightgear::CameraGroup::buildDefaultGroup(composite_viewer_view);
|
flightgear::CameraGroup::buildDefaultGroup(composite_viewer_view);
|
||||||
composite_viewer_view->setFrameStamp(composite_viewer->getFrameStamp());
|
composite_viewer_view->setFrameStamp(composite_viewer->getFrameStamp());
|
||||||
composite_viewer_view->setDatabasePager(FGScenery::getPagerSingleton());
|
|
||||||
composite_viewer_view->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true, false);
|
|
||||||
osg::GraphicsContext::createNewContextID();
|
osg::GraphicsContext::createNewContextID();
|
||||||
render->setView(composite_viewer_view);
|
render->setView(composite_viewer_view);
|
||||||
render->preinit();
|
render->preinit();
|
||||||
composite_viewer->startThreading();
|
composite_viewer->startThreading();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
viewer->getDatabasePager()->setUpThreads(2, 1);
|
viewer->getDatabasePager()->setUpThreads(numDBPagerThreads, 1);
|
||||||
viewer->getDatabasePager()->setAcceptNewDatabaseRequests(true);
|
viewer->getDatabasePager()->setAcceptNewDatabaseRequests(true);
|
||||||
// must do this before preinit for Rembrandthe
|
// must do this before preinit for Rembrandthe
|
||||||
flightgear::CameraGroup::buildDefaultGroup(viewer.get());
|
flightgear::CameraGroup::buildDefaultGroup(viewer.get());
|
||||||
|
|
|
@ -1301,16 +1301,7 @@ bool FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LegRef l = new Leg{this, wp};
|
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wpNode->hasChild("hold-count")) {
|
if (wpNode->hasChild("hold-count")) {
|
||||||
l->setHoldCount(wpNode->getIntValue("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);
|
double altFt = aWP->getDoubleValue("altitude-ft", -9999.9);
|
||||||
if (altFt > -9990.0) {
|
if (altFt > -9990.0) {
|
||||||
w->setAltitude(altFt, RESTRICT_AT);
|
w->setAltitude(altFt, RESTRICT_AT, ALTITUDE_FEET);
|
||||||
}
|
}
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
|
@ -1564,9 +1555,12 @@ FlightPlan::Leg* FlightPlan::Leg::cloneFor(FlightPlan* owner) const
|
||||||
// clone local data
|
// clone local data
|
||||||
c->_speed = _speed;
|
c->_speed = _speed;
|
||||||
c->_speedRestrict = _speedRestrict;
|
c->_speedRestrict = _speedRestrict;
|
||||||
c->_altitudeFt = _altitudeFt;
|
c->_speedUnits = _speedUnits;
|
||||||
|
c->_altitude = _altitude;
|
||||||
c->_altRestrict = _altRestrict;
|
c->_altRestrict = _altRestrict;
|
||||||
|
c->_altitudeUnits = _altitudeUnits;
|
||||||
|
c->_holdCount = c->_holdCount;
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1583,36 +1577,37 @@ unsigned int FlightPlan::Leg::index() const
|
||||||
return _parent->findLegIndex(this);
|
return _parent->findLegIndex(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FlightPlan::Leg::altitudeFt() const
|
double FlightPlan::Leg::altitude(RouteUnits aUnits) const
|
||||||
{
|
{
|
||||||
if (_altRestrict != RESTRICT_NONE) {
|
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) {
|
if (_speedRestrict != RESTRICT_NONE) {
|
||||||
return _speed;
|
return convertSpeedUnits(_speedUnits, units, altitudeFt(), _speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _waypt->speed();
|
return _waypt->speed(units);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FlightPlan::Leg::speedKts() const
|
int FlightPlan::Leg::speedKts() const
|
||||||
{
|
{
|
||||||
return speed();
|
return static_cast<int>(speed(SPEED_KNOTS));
|
||||||
}
|
}
|
||||||
|
|
||||||
double FlightPlan::Leg::speedMach() const
|
double FlightPlan::Leg::speedMach() const
|
||||||
{
|
{
|
||||||
if (!isMachRestrict(_speedRestrict)) {
|
return speed(SPEED_MACH);
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -(_speed / 100.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RouteRestriction FlightPlan::Leg::altitudeRestriction() const
|
RouteRestriction FlightPlan::Leg::altitudeRestriction() const
|
||||||
|
@ -1632,21 +1627,30 @@ RouteRestriction FlightPlan::Leg::speedRestriction() const
|
||||||
|
|
||||||
return _waypt->speedRestriction();
|
return _waypt->speedRestriction();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlightPlan::Leg::setSpeed(RouteRestriction ty, double speed)
|
void FlightPlan::Leg::setSpeed(RouteRestriction ty, double speed, RouteUnits aUnit)
|
||||||
{
|
{
|
||||||
_speedRestrict = ty;
|
_speedRestrict = ty;
|
||||||
if (isMachRestrict(ty)) {
|
if (aUnit == DEFAULT_UNITS) {
|
||||||
_speed = (speed * -100);
|
if (isMachRestrict(ty)) {
|
||||||
} else {
|
aUnit = SPEED_MACH;
|
||||||
_speed = speed;
|
} 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;
|
_altRestrict = ty;
|
||||||
_altitudeFt = altFt;
|
if (aUnit == DEFAULT_UNITS) {
|
||||||
|
aUnit = ALTITUDE_FEET;
|
||||||
|
}
|
||||||
|
_altitudeUnits = aUnit;
|
||||||
|
_altitude = alt;
|
||||||
}
|
}
|
||||||
|
|
||||||
double FlightPlan::Leg::courseDeg() const
|
double FlightPlan::Leg::courseDeg() const
|
||||||
|
@ -1722,16 +1726,24 @@ void FlightPlan::Leg::writeToProperties(SGPropertyNode* aProp) const
|
||||||
{
|
{
|
||||||
if (_speedRestrict != RESTRICT_NONE) {
|
if (_speedRestrict != RESTRICT_NONE) {
|
||||||
aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict));
|
aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict));
|
||||||
if (_speedRestrict == SPEED_RESTRICT_MACH) {
|
if (_speedUnits == SPEED_MACH) {
|
||||||
aProp->setDoubleValue("speed", speedMach());
|
aProp->setDoubleValue("speed-mach", _speed);
|
||||||
|
} else if (_speedUnits == SPEED_KPH) {
|
||||||
|
aProp->setDoubleValue("speed-kph", _speed);
|
||||||
} else {
|
} else {
|
||||||
aProp->setDoubleValue("speed", _speed);
|
aProp->setDoubleValue("speed", _speed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_altRestrict != RESTRICT_NONE) {
|
if (_altRestrict != RESTRICT_NONE) {
|
||||||
aProp->setStringValue("alt-restrict", restrictionToString(_altRestrict));
|
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) {
|
if (_holdCount > 0) {
|
||||||
|
|
|
@ -92,7 +92,7 @@ public:
|
||||||
FlightPlanRef clone(const std::string& newIdent = {}, bool convertToFlightPlan = false) const;
|
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
|
Routes can contain Via, and cannot be active: FlightPlans contain Legs for procedures and
|
||||||
airways, i.e what the GPS/FMC actally flies.
|
airways, i.e what the GPS/FMC actally flies.
|
||||||
*/
|
*/
|
||||||
|
@ -131,16 +131,17 @@ public:
|
||||||
unsigned int index() const;
|
unsigned int index() const;
|
||||||
|
|
||||||
int altitudeFt() const;
|
int altitudeFt() const;
|
||||||
int speed() const;
|
double speed(RouteUnits units = DEFAULT_UNITS) const;
|
||||||
|
double altitude(RouteUnits units = DEFAULT_UNITS) const;
|
||||||
|
|
||||||
int speedKts() const;
|
int speedKts() const;
|
||||||
double speedMach() const;
|
double speedMach() const;
|
||||||
|
|
||||||
RouteRestriction altitudeRestriction() const;
|
RouteRestriction altitudeRestriction() const;
|
||||||
RouteRestriction speedRestriction() const;
|
RouteRestriction speedRestriction() const;
|
||||||
|
|
||||||
void setSpeed(RouteRestriction ty, double speed);
|
void setSpeed(RouteRestriction ty, double speed, RouteUnits units = DEFAULT_UNITS);
|
||||||
void setAltitude(RouteRestriction ty, int altFt);
|
void setAltitude(RouteRestriction ty, double alt, RouteUnits units = DEFAULT_UNITS);
|
||||||
|
|
||||||
double courseDeg() const;
|
double courseDeg() const;
|
||||||
double distanceNm() const;
|
double distanceNm() const;
|
||||||
|
@ -164,8 +165,10 @@ public:
|
||||||
const FlightPlan* _parent;
|
const FlightPlan* _parent;
|
||||||
RouteRestriction _speedRestrict = RESTRICT_NONE,
|
RouteRestriction _speedRestrict = RESTRICT_NONE,
|
||||||
_altRestrict = RESTRICT_NONE;
|
_altRestrict = RESTRICT_NONE;
|
||||||
int _speed = 0;
|
double _speed = 0.0;
|
||||||
int _altitudeFt = 0;
|
double _altitude = 0.0;
|
||||||
|
RouteUnits _speedUnits = SPEED_KNOTS;
|
||||||
|
RouteUnits _altitudeUnits = ALTITUDE_FEET;
|
||||||
|
|
||||||
// if > 0, we will hold at the waypoint using
|
// if > 0, we will hold at the waypoint using
|
||||||
// the published hold side/course
|
// the published hold side/course
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <Navaids/waypoint.hxx>
|
#include <Navaids/waypoint.hxx>
|
||||||
#include <Airports/airport.hxx>
|
#include <Airports/airport.hxx>
|
||||||
|
#include <Navaids/route.hxx>
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
@ -203,15 +204,7 @@ void NavdataVisitor::endElement(const char* name)
|
||||||
} else if (tag == "Altitude") {
|
} else if (tag == "Altitude") {
|
||||||
_altitude = atof(_text.c_str());
|
_altitude = atof(_text.c_str());
|
||||||
} else if (tag == "AltitudeRestriction") {
|
} else if (tag == "AltitudeRestriction") {
|
||||||
if (_text == "at") {
|
_altRestrict = restrictionFromString(_text);
|
||||||
_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);
|
|
||||||
}
|
|
||||||
} else if (tag == "Hld_Rad_or_Inbd") {
|
} else if (tag == "Hld_Rad_or_Inbd") {
|
||||||
if (_text == "Inbd") {
|
if (_text == "Inbd") {
|
||||||
_holdRadial = -1.0;
|
_holdRadial = -1.0;
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include <Navaids/LevelDXML.hxx>
|
#include <Navaids/LevelDXML.hxx>
|
||||||
#include <Airports/airport.hxx>
|
#include <Airports/airport.hxx>
|
||||||
#include <Navaids/airways.hxx>
|
#include <Navaids/airways.hxx>
|
||||||
|
#include <Environment/atmosphere.hxx> // for Mach conversions
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
@ -123,7 +124,67 @@ WayptRef viaFromString(const SGGeod& basePosition, const std::string& target)
|
||||||
return new Via(nullptr, airway, nav);
|
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) :
|
Waypt::Waypt(RouteBase* aOwner) :
|
||||||
_owner(aOwner),
|
_owner(aOwner),
|
||||||
|
@ -184,28 +245,80 @@ bool Waypt::matches(const SGGeod& aPos) const
|
||||||
return (d < 100.0); // 100 metres seems plenty
|
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;
|
_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;
|
_speed = aSpeed;
|
||||||
|
_speedUnits = aUnit;
|
||||||
_speedRestrict = aRestrict;
|
_speedRestrict = aRestrict;
|
||||||
}
|
}
|
||||||
|
|
||||||
double Waypt::speedKts() const
|
double Waypt::speedKts() const
|
||||||
{
|
{
|
||||||
assert(_speedRestrict != SPEED_RESTRICT_MACH);
|
return speed(SPEED_KNOTS);
|
||||||
return speed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double Waypt::speedMach() const
|
double Waypt::speedMach() const
|
||||||
{
|
{
|
||||||
assert(_speedRestrict == SPEED_RESTRICT_MACH);
|
return speed(SPEED_MACH);
|
||||||
return speed();
|
}
|
||||||
|
|
||||||
|
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
|
double Waypt::magvarDeg() const
|
||||||
|
@ -234,13 +347,14 @@ std::string Waypt::icaoDescription() const
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// persistence
|
// persistence
|
||||||
|
|
||||||
static RouteRestriction restrictionFromString(const char* aStr)
|
RouteRestriction restrictionFromString(const std::string& aStr)
|
||||||
{
|
{
|
||||||
std::string l = simgear::strutils::lowercase(aStr);
|
std::string l = simgear::strutils::lowercase(aStr);
|
||||||
|
|
||||||
if (l == "at") return RESTRICT_AT;
|
if (l == "at") return RESTRICT_AT;
|
||||||
if (l == "above") return RESTRICT_ABOVE;
|
if (l == "above") return RESTRICT_ABOVE;
|
||||||
if (l == "below") return RESTRICT_BELOW;
|
if (l == "below") return RESTRICT_BELOW;
|
||||||
|
if (l == "between") return RESTRICT_BETWEEN;
|
||||||
if (l == "none") return RESTRICT_NONE;
|
if (l == "none") return RESTRICT_NONE;
|
||||||
if (l == "mach") return SPEED_RESTRICT_MACH;
|
if (l == "mach") return SPEED_RESTRICT_MACH;
|
||||||
|
|
||||||
|
@ -256,6 +370,7 @@ const char* restrictionToString(RouteRestriction aRestrict)
|
||||||
case RESTRICT_BELOW: return "below";
|
case RESTRICT_BELOW: return "below";
|
||||||
case RESTRICT_ABOVE: return "above";
|
case RESTRICT_ABOVE: return "above";
|
||||||
case RESTRICT_NONE: return "none";
|
case RESTRICT_NONE: return "none";
|
||||||
|
case RESTRICT_BETWEEN: return "between";
|
||||||
case SPEED_RESTRICT_MACH: return "mach";
|
case SPEED_RESTRICT_MACH: return "mach";
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -382,16 +497,22 @@ WayptRef Waypt::createFromString(RouteBase* aOwner, const std::string& s, const
|
||||||
|
|
||||||
auto target = simgear::strutils::uppercase(s);
|
auto target = simgear::strutils::uppercase(s);
|
||||||
// extract altitude
|
// extract altitude
|
||||||
double altFt = 0.0;
|
double alt = 0.0;
|
||||||
RouteRestriction altSetting = RESTRICT_NONE;
|
RouteRestriction altSetting = RESTRICT_NONE;
|
||||||
|
RouteUnits altitudeUnits = ALTITUDE_FEET;
|
||||||
|
|
||||||
size_t pos = target.find('@');
|
size_t pos = target.find('@');
|
||||||
if (pos != string::npos) {
|
if (pos != string::npos) {
|
||||||
altFt = std::stof(target.substr(pos + 1));
|
auto altStr = simgear::strutils::uppercase(target.substr(pos + 1));
|
||||||
target = target.substr(0, pos);
|
if (simgear::strutils::starts_with(altStr, "FL")) {
|
||||||
if (fgGetString("/sim/startup/units") == "meter") {
|
altitudeUnits = ALTITUDE_FLIGHTLEVEL;
|
||||||
altFt *= SG_METER_TO_FEET;
|
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;
|
altSetting = RESTRICT_AT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,7 +569,7 @@ WayptRef Waypt::createFromString(RouteBase* aOwner, const std::string& s, const
|
||||||
}
|
}
|
||||||
|
|
||||||
if (altSetting != RESTRICT_NONE) {
|
if (altSetting != RESTRICT_NONE) {
|
||||||
wpt->setAltitude(altFt, altSetting);
|
wpt->setAltitude(alt, altSetting, altitudeUnits);
|
||||||
}
|
}
|
||||||
return wpt;
|
return wpt;
|
||||||
}
|
}
|
||||||
|
@ -462,42 +583,70 @@ void Waypt::saveAsNode(SGPropertyNode* n) const
|
||||||
|
|
||||||
bool Waypt::initFromProperties(SGPropertyNode_ptr aProp)
|
bool Waypt::initFromProperties(SGPropertyNode_ptr aProp)
|
||||||
{
|
{
|
||||||
if (aProp->hasChild("generated")) {
|
if (aProp->hasChild("generated")) {
|
||||||
setFlag(WPT_GENERATED, aProp->getBoolValue("generated"));
|
setFlag(WPT_GENERATED, aProp->getBoolValue("generated"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aProp->hasChild("overflight")) {
|
if (aProp->hasChild("overflight")) {
|
||||||
setFlag(WPT_OVERFLIGHT, aProp->getBoolValue("overflight"));
|
setFlag(WPT_OVERFLIGHT, aProp->getBoolValue("overflight"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aProp->hasChild("arrival")) {
|
if (aProp->hasChild("arrival")) {
|
||||||
setFlag(WPT_ARRIVAL, aProp->getBoolValue("arrival"));
|
setFlag(WPT_ARRIVAL, aProp->getBoolValue("arrival"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aProp->hasChild("approach")) {
|
if (aProp->hasChild("approach")) {
|
||||||
setFlag(WPT_APPROACH, aProp->getBoolValue("approach"));
|
setFlag(WPT_APPROACH, aProp->getBoolValue("approach"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aProp->hasChild("departure")) {
|
if (aProp->hasChild("departure")) {
|
||||||
setFlag(WPT_DEPARTURE, aProp->getBoolValue("departure"));
|
setFlag(WPT_DEPARTURE, aProp->getBoolValue("departure"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aProp->hasChild("miss")) {
|
if (aProp->hasChild("miss")) {
|
||||||
setFlag(WPT_MISS, aProp->getBoolValue("miss"));
|
setFlag(WPT_MISS, aProp->getBoolValue("miss"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aProp->hasChild("airway")) {
|
if (aProp->hasChild("airway")) {
|
||||||
setFlag(WPT_VIA, true);
|
setFlag(WPT_VIA, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aProp->hasChild("alt-restrict")) {
|
if (aProp->hasChild("alt-restrict")) {
|
||||||
_altRestrict = restrictionFromString(aProp->getStringValue("alt-restrict").c_str());
|
_altRestrict = restrictionFromString(aProp->getStringValue("alt-restrict"));
|
||||||
_altitudeFt = aProp->getDoubleValue("altitude-ft");
|
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")) {
|
if (aProp->hasChild("speed-restrict")) {
|
||||||
_speedRestrict = restrictionFromString(aProp->getStringValue("speed-restrict").c_str());
|
_speedRestrict = restrictionFromString(aProp->getStringValue("speed-restrict"));
|
||||||
_speed = aProp->getDoubleValue("speed");
|
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;
|
return true;
|
||||||
|
@ -537,8 +686,19 @@ void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const
|
||||||
|
|
||||||
if (_altRestrict != RESTRICT_NONE) {
|
if (_altRestrict != RESTRICT_NONE) {
|
||||||
aProp->setStringValue("alt-restrict", restrictionToString(_altRestrict));
|
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) {
|
if (_speedRestrict != RESTRICT_NONE) {
|
||||||
aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict));
|
aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict));
|
||||||
|
|
|
@ -4,32 +4,16 @@
|
||||||
* owns a collection (list, tree, graph) of route elements - such as airways,
|
* owns a collection (list, tree, graph) of route elements - such as airways,
|
||||||
* procedures or a flight plan.
|
* procedures or a flight plan.
|
||||||
*/
|
*/
|
||||||
|
// Copyright (C) 2009 James Turner
|
||||||
// Written by James Turner, started 2009.
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
//
|
|
||||||
// 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
|
#pragma once
|
||||||
#define FG_ROUTE_HXX
|
|
||||||
|
|
||||||
// std
|
// std
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
// Simgear
|
// Simgear
|
||||||
#include <simgear/structure/SGReferenced.hxx>
|
#include <simgear/structure/SGReferenced.hxx>
|
||||||
|
@ -88,14 +72,30 @@ typedef enum {
|
||||||
RESTRICT_AT,
|
RESTRICT_AT,
|
||||||
RESTRICT_ABOVE,
|
RESTRICT_ABOVE,
|
||||||
RESTRICT_BELOW,
|
RESTRICT_BELOW,
|
||||||
|
RESTRICT_BETWEEN,
|
||||||
SPEED_RESTRICT_MACH, ///< encode an 'AT' restriction in Mach, not IAS
|
SPEED_RESTRICT_MACH, ///< encode an 'AT' restriction in Mach, not IAS
|
||||||
RESTRICT_DELETE, ///< ignore underlying restriction (on a leg)
|
RESTRICT_DELETE, ///< ignore underlying restriction (on a leg)
|
||||||
RESTRICT_COMPUTED, ///< data is computed, not a real restriction
|
RESTRICT_COMPUTED, ///< data is computed, not a real restriction
|
||||||
SPEED_COMPUTED_MACH ///< variant on above to encode a Mach value
|
SPEED_COMPUTED_MACH ///< variant on above to encode a Mach value
|
||||||
} RouteRestriction;
|
} 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);
|
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
|
* Abstract base class for waypoints (and things that are treated similarly
|
||||||
* by navigation systems). More precisely this is route path elements,
|
* by navigation systems). More precisely this is route path elements,
|
||||||
|
@ -117,12 +117,14 @@ public:
|
||||||
virtual FGPositioned* source() const
|
virtual FGPositioned* source() const
|
||||||
{ return nullptr; }
|
{ return nullptr; }
|
||||||
|
|
||||||
virtual double altitudeFt() const
|
double altitudeFt() const;
|
||||||
{ return _altitudeFt; }
|
|
||||||
|
|
||||||
virtual double speed() const
|
double speed(RouteUnits aUnits = DEFAULT_UNITS) const;
|
||||||
{ return _speed; }
|
|
||||||
|
|
||||||
|
double altitude(RouteUnits aUnits = DEFAULT_UNITS) const;
|
||||||
|
|
||||||
|
double constraintAltitude(RouteUnits aUnits = DEFAULT_UNITS) const;
|
||||||
|
|
||||||
// wrapper - asserts if restriction type is _MACH
|
// wrapper - asserts if restriction type is _MACH
|
||||||
double speedKts() const;
|
double speedKts() const;
|
||||||
|
|
||||||
|
@ -135,9 +137,11 @@ public:
|
||||||
virtual RouteRestriction speedRestriction() const
|
virtual RouteRestriction speedRestriction() const
|
||||||
{ return _speedRestrict; }
|
{ return _speedRestrict; }
|
||||||
|
|
||||||
void setAltitude(double aAlt, RouteRestriction aRestrict);
|
void setAltitude(double aAlt, RouteRestriction aRestrict, RouteUnits aUnits = DEFAULT_UNITS);
|
||||||
void setSpeed(double aSpeed, RouteRestriction aRestrict);
|
void setSpeed(double aSpeed, RouteRestriction aRestrict, RouteUnits aUnits = DEFAULT_UNITS);
|
||||||
|
|
||||||
|
void setConstraintAltitude(double aAlt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifier assoicated with the waypoint. Human-readable, but
|
* Identifier assoicated with the waypoint. Human-readable, but
|
||||||
* possibly quite terse, and definitiely not unique.
|
* possibly quite terse, and definitiely not unique.
|
||||||
|
@ -230,8 +234,16 @@ protected:
|
||||||
typedef Waypt*(FactoryFunction)(RouteBase* aOwner);
|
typedef Waypt*(FactoryFunction)(RouteBase* aOwner);
|
||||||
static void registerFactory(const std::string aNodeType, FactoryFunction* aFactory);
|
static void registerFactory(const std::string aNodeType, FactoryFunction* aFactory);
|
||||||
|
|
||||||
double _altitudeFt = 0.0;
|
double _altitude = 0.0;
|
||||||
double _speed = 0.0; // knots IAS or mach
|
/// 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 _altRestrict = RESTRICT_NONE;
|
||||||
RouteRestriction _speedRestrict = RESTRICT_NONE;
|
RouteRestriction _speedRestrict = RESTRICT_NONE;
|
||||||
private:
|
private:
|
||||||
|
@ -269,5 +281,3 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
} // of namespace flightgear
|
} // of namespace flightgear
|
||||||
|
|
||||||
#endif // of FG_ROUTE_HXX
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ NavaidWaypoint::NavaidWaypoint(RouteBase* aOwner) :
|
||||||
|
|
||||||
SGGeod NavaidWaypoint::position() const
|
SGGeod NavaidWaypoint::position() const
|
||||||
{
|
{
|
||||||
return SGGeod::fromGeodFt(_navaid->geod(), _altitudeFt);
|
return SGGeod::fromGeodFt(_navaid->geod(), altitudeFt());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string NavaidWaypoint::ident() const
|
std::string NavaidWaypoint::ident() const
|
||||||
|
@ -170,7 +170,7 @@ void OffsetNavaidWaypoint::init()
|
||||||
SGGeod offset;
|
SGGeod offset;
|
||||||
double az2;
|
double az2;
|
||||||
SGGeodesy::direct(_navaid->geod(), _radial, _distanceNm * SG_NM_TO_METER, offset, 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)
|
bool OffsetNavaidWaypoint::initFromProperties(SGPropertyNode_ptr aProp)
|
||||||
|
|
|
@ -745,4 +745,5 @@ bool FGCom::isInRange(const double &freq) const
|
||||||
|
|
||||||
|
|
||||||
// Register the subsystem.
|
// Register the subsystem.
|
||||||
SGSubsystemMgr::Registrant<FGCom> registrantFGCom;
|
SGSubsystemMgr::Registrant<FGCom> registrantFGCom(
|
||||||
|
SGSubsystemMgr::SOUND);
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
#include <simgear/scene/model/modellib.hxx>
|
#include <simgear/scene/model/modellib.hxx>
|
||||||
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
|
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
|
||||||
|
#include <simgear/scene/tgdb/VPBTechnique.hxx>
|
||||||
#include <simgear/scene/tsync/terrasync.hxx>
|
#include <simgear/scene/tsync/terrasync.hxx>
|
||||||
#include <simgear/misc/strutils.hxx>
|
#include <simgear/misc/strutils.hxx>
|
||||||
#include <simgear/scene/material/matlib.hxx>
|
#include <simgear/scene/material/matlib.hxx>
|
||||||
|
@ -497,6 +498,11 @@ void FGTileMgr::update_queues(bool& isDownloadingScenery)
|
||||||
|
|
||||||
tile_cache.clear_entry(drop_index);
|
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();
|
osg::ref_ptr<osg::Object> subgraph = old->getNode();
|
||||||
old->removeFromSceneGraph();
|
old->removeFromSceneGraph();
|
||||||
delete old;
|
delete old;
|
||||||
|
|
|
@ -464,9 +464,27 @@ static RouteRestriction routeRestrictionFromArg(naRef arg)
|
||||||
if (u == "mach") return SPEED_RESTRICT_MACH;
|
if (u == "mach") return SPEED_RESTRICT_MACH;
|
||||||
if (u == "computed-mach") return SPEED_COMPUTED_MACH;
|
if (u == "computed-mach") return SPEED_COMPUTED_MACH;
|
||||||
if (u == "delete") return RESTRICT_DELETE;
|
if (u == "delete") return RESTRICT_DELETE;
|
||||||
|
if (u == "between") return RESTRICT_BETWEEN;
|
||||||
return RESTRICT_NONE;
|
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)
|
naRef routeRestrictionToNasal(naContext c, RouteRestriction rr)
|
||||||
{
|
{
|
||||||
switch (rr) {
|
switch (rr) {
|
||||||
|
@ -478,6 +496,7 @@ naRef routeRestrictionToNasal(naContext c, RouteRestriction rr)
|
||||||
case RESTRICT_COMPUTED: return stringToNasal(c, "computed");
|
case RESTRICT_COMPUTED: return stringToNasal(c, "computed");
|
||||||
case SPEED_COMPUTED_MACH: return stringToNasal(c, "computed-mach");
|
case SPEED_COMPUTED_MACH: return stringToNasal(c, "computed-mach");
|
||||||
case RESTRICT_DELETE: return stringToNasal(c, "delete");
|
case RESTRICT_DELETE: return stringToNasal(c, "delete");
|
||||||
|
case RESTRICT_BETWEEN: return stringToNasal(c, "between");
|
||||||
}
|
}
|
||||||
|
|
||||||
return naNil();
|
return naNil();
|
||||||
|
@ -1984,6 +2003,8 @@ static naRef f_leg_setSpeed(naContext c, naRef me, int argc, naRef* args)
|
||||||
|
|
||||||
double speed = 0.0;
|
double speed = 0.0;
|
||||||
RouteRestriction rr = RESTRICT_AT;
|
RouteRestriction rr = RESTRICT_AT;
|
||||||
|
RouteUnits units = DEFAULT_UNITS;
|
||||||
|
|
||||||
if (argc > 0) {
|
if (argc > 0) {
|
||||||
if (naIsNil(args[0])) {
|
if (naIsNil(args[0])) {
|
||||||
// clear the restriction to NONE
|
// clear the restriction to NONE
|
||||||
|
@ -1994,9 +2015,13 @@ static naRef f_leg_setSpeed(naContext c, naRef me, int argc, naRef* args)
|
||||||
} else {
|
} else {
|
||||||
naRuntimeError(c, "bad arguments to setSpeed");
|
naRuntimeError(c, "bad arguments to setSpeed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (argc > 2) {
|
||||||
|
units = routeUnitsFromArg(args[2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
leg->setSpeed(rr, speed);
|
leg->setSpeed(rr, speed, units);
|
||||||
} else {
|
} else {
|
||||||
naRuntimeError(c, "bad arguments to setSpeed");
|
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;
|
double altitude = 0.0;
|
||||||
RouteRestriction rr = RESTRICT_AT;
|
RouteRestriction rr = RESTRICT_AT;
|
||||||
|
RouteUnits units = DEFAULT_UNITS;
|
||||||
|
|
||||||
if (argc > 0) {
|
if (argc > 0) {
|
||||||
if (naIsNil(args[0])) {
|
if (naIsNil(args[0])) {
|
||||||
// clear the restriction to NONE
|
// clear the restriction to NONE
|
||||||
|
@ -2023,9 +2050,37 @@ static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args)
|
||||||
} else {
|
} else {
|
||||||
naRuntimeError(c, "bad arguments to leg.setAltitude");
|
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 {
|
} else {
|
||||||
naRuntimeError(c, "bad arguments to setleg.setAltitude");
|
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();
|
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)
|
static naRef f_leg_courseAndDistanceFrom(naContext c, naRef me, int argc, naRef* args)
|
||||||
{
|
{
|
||||||
FlightPlan::Leg* leg = fpLegGhost(me);
|
FlightPlan::Leg* leg = fpLegGhost(me);
|
||||||
|
@ -2301,6 +2393,8 @@ naRef initNasalFlightPlan(naRef globals, naContext c)
|
||||||
naSave(c, fpLegPrototype);
|
naSave(c, fpLegPrototype);
|
||||||
hashset(c, fpLegPrototype, "setSpeed", naNewFunc(c, naNewCCode(c, f_leg_setSpeed)));
|
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, "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, "path", naNewFunc(c, naNewCCode(c, f_leg_path)));
|
||||||
hashset(c, fpLegPrototype, "courseAndDistanceFrom", naNewFunc(c, naNewCCode(c, f_leg_courseAndDistanceFrom)));
|
hashset(c, fpLegPrototype, "courseAndDistanceFrom", naNewFunc(c, naNewCCode(c, f_leg_courseAndDistanceFrom)));
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ foreach( simgear_test_category
|
||||||
props
|
props
|
||||||
canvas
|
canvas
|
||||||
structure
|
structure
|
||||||
|
timing
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(${simgear_test_category})
|
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());
|
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()
|
void FlightplanTests::testBasicDiscontinuity()
|
||||||
{
|
{
|
||||||
FlightPlanRef fp1 = makeTestFP("LIRF"s, "34L"s, "LEBL"s, "25R"s,
|
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(testViaInsertIntoFP);
|
||||||
CPPUNIT_TEST(testViaInsertIntoRoute);
|
CPPUNIT_TEST(testViaInsertIntoRoute);
|
||||||
CPPUNIT_TEST(loadFGFPAsRoute);
|
CPPUNIT_TEST(loadFGFPAsRoute);
|
||||||
|
CPPUNIT_TEST(testLoadSaveBetweenRestriction);
|
||||||
// CPPUNIT_TEST(testParseICAORoute);
|
CPPUNIT_TEST(testRestrictionUnits);
|
||||||
// CPPUNIT_TEST(testParseICANLowLevelRoute);
|
|
||||||
|
// CPPUNIT_TEST(testParseICAORoute);
|
||||||
|
// CPPUNIT_TEST(testParseICANLowLevelRoute);
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -100,6 +102,8 @@ public:
|
||||||
void testViaInsertIntoFP();
|
void testViaInsertIntoFP();
|
||||||
void testViaInsertIntoRoute();
|
void testViaInsertIntoRoute();
|
||||||
void loadFGFPAsRoute();
|
void loadFGFPAsRoute();
|
||||||
|
void testLoadSaveBetweenRestriction();
|
||||||
|
void testRestrictionUnits();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FG_FLIGHTPLAN_UNIT_TESTS_HXX
|
#endif // FG_FLIGHTPLAN_UNIT_TESTS_HXX
|
||||||
|
|
|
@ -197,6 +197,19 @@ void FPNasalTests::testRestrictions()
|
||||||
CPPUNIT_ASSERT(ok);
|
CPPUNIT_ASSERT(ok);
|
||||||
|
|
||||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_DELETE, fp1->legAtIndex(3)->speedRestriction());
|
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()
|
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
|
set(TESTSUITE_SOURCES
|
||||||
${TESTSUITE_SOURCES}
|
${TESTSUITE_SOURCES}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftService.cxx
|
${SWIFT_TESTS_SOURCES}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftAircraftManager.cxx
|
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|
||||||
set(TESTSUITE_HEADERS
|
set(TESTSUITE_HEADERS
|
||||||
${TESTSUITE_HEADERS}
|
${TESTSUITE_HEADERS}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftService.hxx
|
${SWIFT_TESTS_HEADERS}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftAircraftManager.hxx
|
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,15 @@
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#if defined(ENABLE_SWIFT)
|
||||||
|
|
||||||
#include "test_swiftAircraftManager.hxx"
|
#include "test_swiftAircraftManager.hxx"
|
||||||
#include "test_swiftService.hxx"
|
#include "test_swiftService.hxx"
|
||||||
|
|
||||||
// Set up the unit tests.
|
// Set up the unit tests.
|
||||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SwiftAircraftManagerTest, "Unit tests");
|
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SwiftAircraftManagerTest, "Unit tests");
|
||||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SwiftServiceTest, "Unit tests");
|
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SwiftServiceTest, "Unit tests");
|
||||||
|
|
||||||
|
#endif
|
Loading…
Add table
Reference in a new issue