EXTRA_DIST = \
acinclude.m4 \
autogen.sh \
- FlightGear.dsp \
- FlightGear.dsw \
projects \
README \
README.OpenAL \
Thanks
dist-hook:
- (cd $(top_srcdir); $(HOME)/Projects/FlightGear/admin/am2dsp.pl)
(cd $(top_srcdir); tar --exclude docs-mini/CVS --exclude hints/CVS \
-cf - docs-mini ) | (cd $(distdir); tar xvf -)
rm -rf `find $(distdir)/projects -name CVS`
--exclude='*/Docs/source' \
--exclude='*/Models/MNUAV' \
--exclude='*/Models/Airspace' \
- -cjvf source/FlightGear-data-$(VERSION).tar.bz2 \
+ -cjvf FlightGear-data-$(VERSION).tar.bz2 \
data/AI \
data/Aircraft/Generic \
data/Aircraft/Instruments \
data/preferences.xml \
data/Protocol \
data/README \
+ data/Scenery/Airports \
data/Scenery/Objects \
data/Scenery/Terrain \
data/Shaders \
-Version 1.99.5
-* October 30, 2008 (source code snapshot release)
+Version 2.0.0 - February 25, 2010
+
+ FlightGear 2.0.0. reflects the maturation of the OpenSceneGraph port
+ that started with the previous 1.9.0 release. In addition to many
+ internal code improvements, FlightGear 2.0.0. marks the introduction
+ of many new exciting improvements in the graphics and sound system,
+ as well as improved usability of key features, and improved behavior
+ of exsisting features. Highlights of this new version include:
+ Dramatic new 3D clouds, dramatic lighting conditions, improved
+ support for custom scenery, and many many new and detailed aircraft
+ models.
+
+ Sound
+ * Complete overhaul of the sound code
+ * doppler effects
+ * distance attenuation
+ * 3D positional sound sources
+ * assignment of sound sources to external objects (i.e. AI controlled
+ aircraft)
+ * User selection of the sound device
+
+ Visual Effects
+ * Use of Shaders for dynamic textures
+ * Use of Effects files
+ * Improved 3D clouds
+ * Color changes based on humidity and other weather effects allow for very
+ dramatic lighting conditions
+ * Dynamic water textures
+ * Text animation based on OSGText
+
+ Usability
+ * Allow screenshots in more common file formats
+ * User selectable sound device
+ * More intuitive selection of the weather settings through the GUI and/or
+ commandline
+
+ Infrastructure
+ * Airport geometry data can be read from the scenery, allowing for more
+ flexible regeneration of terrain tiles
+
+ Internals
+ * Improved efficiency of the property tree
+ * A more efficient ground cache
+ * Many improvements to the route management code
+ * Removed many compiler warnings
+ * More realistic atmosphere model
+
+ Behavior
+ * More realistic ILS behavior
+ * Autopilot improvements
+ * A generic autobrake function
+ * Winds over mountainous areas cause up- and downdrafts that can be used
+ for gliding
+ * More realistic behavior of the route manager
+ * Wild fires, which can be extinguished by firefighter aircraft operating
+ across the multplayer server
+ * Navaid frequencies and radials can be transmitted to Atlas
+
+ Utilities
+ * A python script to visualize Yasim configuration files in Blender
+
+ AI
+ * Allow traffic departing and arriving at the same airport
+ * Add Ground Vehicles - including automobiles, trucks, articulated trucks,
+ trains (including high speed trains)
+ * ATC interactions between AI aircraft and ground controllers
+ * Performance characteristics of AI aircraft can be specified in a
+ performance database
+ * Push-back vehicles are available for a selected number of aircraft
+ * Add escorts for AI carrier - frigates, guided missile cruiser, amphibious
+ warfare ships now make up the Vinson Battle Group
+ * Improved radar functionality - now detects AI escorts etc.
+ * AI objects are now solid (i.e. users can collide with them)
+ * Some preliminary support for SID/STAR procedures for AI aircraft
+
+Version 1.9.0
+* December 20, 2008 (source code snapshot release)
New in 0.9.10
You *must* have OpenSceneGraph (OSG) installed to build this version of
FlightGear.
-Notice that FlightGear 1.9.0 requires at least version 2.7.8. Using earlier
-versions of OSG will yield serious rendering bugs.
+Notice that from FlightGear version 1.9.0, OSG version 2.7.8 or later
+is required. Using earlier versions of OSG will yield serious
+rendering bugs. If you are using an 'older' distribution, this may mean
+a suitable version of OSG may not be availble through the usual package
+repositories.
You can get the latest version of OSG from:
make
sudo make install
+Also later release versions of OpenSceneGraph can be obtained by
+svn, or you can use the OSG development svn 'trunk', but be warned,
+OSG is always in heavy development, and at certain moments
+in time, it may not compile completely, so, as usual, it is
+recommended that you stay with released versions.
+
+Installation notes:
+
+In some unix/linux distributions, particularly 64-bit
+systems, OSG may install its shared libraries in other than
+/usr/lib, /usr/local/lib or $prefix/lib!
+
+This does not seem to effect binary installation, which is
+to $prefix/bin, nor header installation, which remains
+$prefix/include. Just the shared libraries, and perhaps
+only for 64-bit systems, or higher as, and when available.
+
+The default is /usr/local/lib64 or $prefix/lib64 in
+64-bit systems. This may cause problems with the auto-conf
+tools when configuring and compiling FlighGear, since
+even using the configure option --with-osg=$prefix
+will not 'fix' the problem.
+
+The are various ways to deal with this, which mainly depend
+on whether you just want one version of OSG 'globally'
+installed, or desire to be able to build, and run, FlightGear
+against 'different' versions of OSG.
+
+There is a parameter, -D LIB_POSTFIX= or -D LIB_POSTFIX=""
+which can be passed to cmake OSG to force the OSG library
+installation into the 'standard' "$prefix/lib".
+
+OSG cmake advises of a post installation step -
+ $ sudo make install_ld_conf
+which, if available, will add an openscenegraph.conf file
+to the /etc/ld.so.conf.d folder, with the line -
+${CMAKE_INSTALL_PREFIX}lib${LIB_POSTFIX}
+and run 'ldconfig' to add this to the 'cache'. But this
+option does not always seem available.
+
+Configuring SimGear and Flightgear notes:
+
+If you install OSG in other than the 'standard' directories,
+you must add --with-osg=$prefix when configuring
+SimGear and FlightGear. This will cause the auto-conf
+tools to look in $prefix/include for headers, and
+$prefix/lib for libraries, but _NOT_ in 'lib64'!
+
+If the OSG is installed to a unique $prefix directory, then
+it is also possible to make a 'link' entry lib -> lib64 to
+get over this, but obviously this is not available if
+$prefix/lib already exists, and contains entries.
+
+Running fgfs, and others, with OSG shared libraries:
+
+You must also deal with the executable loader being
+able to find the OSG shared libraries when running fgfs,
+and others, through perhaps using :-
+ $ export LD_LIBRARY_PATH=/path/to/osg/lib[64][:/other/paths]
+or more permanently using a '.conf' file in the
+/etc/ld.so.conf.d directory, and running
+ $ sudo ldconfig -v to update the 'cache'.
+
fi
AM_CONDITIONAL(ENABLE_SP_FDM, test "x$enable_sp_fdms" != "xno")
+# Specify whether we want to compile ATCDCL.
+# default to with_atcdcl=yes
+AC_ARG_ENABLE(atcdcl, [ --enable-atcdcl Compile and link the depricated atc/ai module], [enable_atcdcl="$enableval"] )
+if test "x$enable_atcdcl" != "xno"; then
+ AC_DEFINE([ENABLE_ATCDCL], 1, [Define to include old ATC/AI module])
+else
+ AC_DEFINE([ENABLE_ATCDCL], 0, [Define to include old ATC/AI module])
+fi
+AM_CONDITIONAL(ENABLE_ATCDCL, test "x$enable_atcdcl" != "xno")
+
+
dnl EXPERIMENTAL generic event driven input device
# defaults to no
fi
AC_CHECK_HEADER(pthread.h)
+
dnl Used by JSBSim to conditionally compile in fgfs interface code
AC_DEFINE([FGFS], 1, [Define so that JSBSim compiles in 'library' mode])
base_LIBS="$LIBS"
dnl Check for SDL or glut if enabled.
-AC_ARG_ENABLE(osgviewer, [ --enable-osgviewer Configure to use osgViewer(default)], [enable_osgviewer="$enableval"])
+AC_ARG_ENABLE(osgviewer, [ --enable-osgviewer Configure to use osgViewer(default)], [enable_osgviewer="$enableval"])
AC_ARG_ENABLE(sdl, [ --enable-sdl Configure to use SDL], [enable_sdl="$enableval"])
AC_ARG_ENABLE(glut, [ --enable-glut Configure to use GLUT], [enable_glut="$enableval"])
AM_CONDITIONAL(USE_SDL, test "x$enable_sdl" = "xyes")
exit
fi
-AC_MSG_CHECKING([for SimGear 1.9.0 or newer])
+AC_MSG_CHECKING([for SimGear 2.0.0 or newer])
AC_TRY_RUN([
#include <stdio.h>
#define STRINGIFY(X) XSTRINGIFY(X)
#define XSTRINGIFY(X) #X
-#define MIN_MAJOR 1
-#define MIN_MINOR 9
+#define MIN_MAJOR 2
+#define MIN_MINOR 0
#define MIN_MICRO 0
int main() {
AC_DEFINE([ENABLE_PLIB_JOYSTICK], 1, [Define to enable plib joystick support])
# Find the OSG libraries. Note special handling for OS X frameworks
-case "${host}" in
-*-apple-darwin*)
- if test "x$with_osg_framework" = "x"; then
- if test "x$enable_osgdebug" = "xyes"; then
- # debug version of osg libs
- AC_CHECK_LIB(OpenThreadsd,OpenThreadsGetVersion)
- openthreads_LIBS="$LIBS"
- LIBS=""
- AC_CHECK_LIB(osgd,osgGetVersion)
- AC_CHECK_LIB(osgUtild,osgUtilGetVersion)
- AC_CHECK_LIB(osgDBd,osgDBGetVersion)
- AC_CHECK_LIB(osgTextd,osgTextGetVersion)
- AC_CHECK_LIB(osgGAd,osgGAGetVersion)
- AC_CHECK_LIB(osgViewerd,osgViewerGetVersion)
- AC_CHECK_LIB(osgSimd,osgSimGetVersion)
- AC_CHECK_LIB(osgParticled,osgParticleGetVersion)
- OSG_LIBS="$LIBS -losgFXd $openthreads_LIBS"
- LIBS=""
- else
- # release version of osg libs
- AC_CHECK_LIB(OpenThreads,OpenThreadsGetVersion)
- openthreads_LIBS="$LIBS"
- LIBS=""
- AC_CHECK_LIB(osg,osgGetVersion)
- AC_CHECK_LIB(osgUtil,osgUtilGetVersion)
- AC_CHECK_LIB(osgDB,osgDBGetVersion)
- AC_CHECK_LIB(osgText,osgTextGetVersion)
- AC_CHECK_LIB(osgGA,osgGAGetVersion)
- AC_CHECK_LIB(osgViewer,osgViewerGetVersion)
- AC_CHECK_LIB(osgSim,osgSimGetVersion)
- AC_CHECK_LIB(osgParticle,osgParticleGetVersion)
- OSG_LIBS="$LIBS -losgFX $openthreads_LIBS"
- LIBS=""
- # echo $LIBS
- fi
- AC_SUBST(openthreads_LIBS)
- AC_SUBST(OSG_LIBS)
- else
- # Checking osg frameworks.
- AC_CHECK_FRAMEWORK(osgViewer, [#include <osgViewer/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgGA, [#include <osgGA/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgText, [#include <osgText/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgFX, [#include <osgFX/AnisotropicLighting>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgUtil, [#include <osgUtil/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgDB, [#include <osgDB/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgSim, [#include <osgSim/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgParticle, [#include <osgParticle/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osg, [#include <osg/Version>], $with_osg_framework)
- osg_FRAMEWORKS="$FRAMEWORKS"
- FRAMEWORKS=""
- AC_CHECK_FRAMEWORK(OpenThreads, [#include <OpenThreads/Version>], $with_osg_framework)
- openthreads_FRAMEWORK="$FRAMEWORKS"
- AC_SUBST(osg_FRAMEWORKS)
- AC_SUBST(openthreads_FRAMEWORK)
- fi
- ;;
-*)
+if test "x$with_osg_framework" = "x"; then
if test "x$enable_osgdebug" = "xyes"; then
- AC_CHECK_LIB(OpenThreadsd,OpenThreadsGetVersion)
- AC_CHECK_LIB(osgd,osgGetVersion)
- AC_CHECK_LIB(osgUtild,osgUtilGetVersion)
- AC_CHECK_LIB(osgDBd,osgDBGetVersion)
- AC_CHECK_LIB(osgTextd,osgTextGetVersion)
- AC_CHECK_LIB(osgGAd,osgGAGetVersion)
- AC_CHECK_LIB(osgViewerd,osgViewerGetVersion)
- AC_CHECK_LIB(osgSimd,osgSimGetVersion)
- AC_CHECK_LIB(osgParticled,osgParticleGetVersion)
- LIBS="$LIBS -losgFXd $opengl_LIBS"
+ AC_CHECK_LIB(OpenThreadsd,OpenThreadsGetVersion, , [AC_MSG_ERROR(OpenThreads library not found.)],)
+ AC_CHECK_LIB(osgd,osgGetVersion, , [AC_MSG_ERROR(OpenSceneGraph library not found.)],)
+ AC_CHECK_LIB(osgUtild,osgUtilGetVersion, , [AC_MSG_ERROR(OpenSceneGraph utility library not found.)],)
+ AC_CHECK_LIB(osgDBd,osgDBGetVersion, , [AC_MSG_ERROR(OpenSceneGraph database library not found.)],)
+ AC_CHECK_LIB(osgTextd,osgTextGetVersion, , [AC_MSG_ERROR(OpenSceneGraph Text library not found.)],)
+ AC_CHECK_LIB(osgGAd,osgGAGetVersion, , [AC_MSG_ERROR(OpenSceneGraph GUI Abstraction library not found.)],)
+ AC_CHECK_LIB(osgViewerd,osgViewerGetVersion, , [AC_MSG_ERROR(OpenSceneGraph Viewer library not found.)],)
+ AC_CHECK_LIB(osgSimd,osgSimGetVersion, , [AC_MSG_ERROR(OpenSceneGraph simulation library not found.)],)
+ AC_CHECK_LIB(osgParticled,osgParticleGetVersion, , [AC_MSG_ERROR(OpenSceneGraph Particle library not found.)],)
+ AC_CHECK_LIB(osgFXd, osgFXGetVersion, , [AC_MSG_ERROR(OpenSceneGraph FX library not found.)],)
else
- AC_CHECK_LIB(OpenThreads,OpenThreadsGetVersion)
- AC_CHECK_LIB(osg,osgGetVersion)
- AC_CHECK_LIB(osgUtil,osgUtilGetVersion)
- AC_CHECK_LIB(osgDB,osgDBGetVersion)
- AC_CHECK_LIB(osgText,osgTextGetVersion)
- AC_CHECK_LIB(osgGA,osgGAGetVersion)
- AC_CHECK_LIB(osgViewer,osgViewerGetVersion)
- AC_CHECK_LIB(osgSim,osgSimGetVersion)
- AC_CHECK_LIB(osgParticle,osgParticleGetVersion)
- LIBS="$LIBS -losgFX $opengl_LIBS"
+ AC_CHECK_LIB(OpenThreads,OpenThreadsGetVersion, , [AC_MSG_ERROR(OpenThreads library not found.)],)
+ AC_CHECK_LIB(osg,osgGetVersion, , [AC_MSG_ERROR(OpenSceneGraph library not found.)],)
+ AC_CHECK_LIB(osgUtil,osgUtilGetVersion, , [AC_MSG_ERROR(OpenSceneGraph utility library not found.)],)
+ AC_CHECK_LIB(osgDB,osgDBGetVersion, , [AC_MSG_ERROR(OpenSceneGraph database library not found.)],)
+ AC_CHECK_LIB(osgText,osgTextGetVersion, , [AC_MSG_ERROR(OpenSceneGraph Text library not found.)],)
+ AC_CHECK_LIB(osgGA,osgGAGetVersion, , [AC_MSG_ERROR(OpenSceneGraph GUI Abstraction library not found.)],)
+ AC_CHECK_LIB(osgViewer,osgViewerGetVersion, , [AC_MSG_ERROR(OpenSceneGraph Viewer library not found.)],)
+ AC_CHECK_LIB(osgSim,osgSimGetVersion, , [AC_MSG_ERROR(OpenSceneGraph simulation library not found.)],)
+ AC_CHECK_LIB(osgParticle,osgParticleGetVersion, , [AC_MSG_ERROR(OpenSceneGraph Particle library not found.)],)
+ AC_CHECK_LIB(osgFX, osgFXGetVersion, , [AC_MSG_ERROR(OpenSceneGraph FX library not found.)],)
fi
- ;;
-esac
+else
+ # Checking osg frameworks.
+ AC_CHECK_FRAMEWORK(osgViewer, [#include <osgViewer/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgGA, [#include <osgGA/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgText, [#include <osgText/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgFX, [#include <osgFX/AnisotropicLighting>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgUtil, [#include <osgUtil/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgDB, [#include <osgDB/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgSim, [#include <osgSim/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgParticle, [#include <osgParticle/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osg, [#include <osg/Version>], $with_osg_framework)
+ osg_FRAMEWORKS="$FRAMEWORKS"
+ FRAMEWORKS=""
+ AC_CHECK_FRAMEWORK(OpenThreads, [#include <OpenThreads/Version>], $with_osg_framework)
+ openthreads_FRAMEWORK="$FRAMEWORKS"
+ AC_SUBST(osg_FRAMEWORKS)
+ AC_SUBST(openthreads_FRAMEWORK)
+fi
AM_CONDITIONAL(HAVE_FRAMEWORK_OSG, test "x$ac_cv_framework_osg" != "x")
AC_CHECK_HEADER(osg/Version)
echo "Include special purpose flight models: no"
fi
+if test "x$enable_atcdcl" != "xno"; then
+ echo "Build depricated ATC/AI module: yes"
+else
+ echo "Build depricated ATC/AI module: no"
+fi
<max>infinity</max>
<min>-infinity<min>
<abs>false</abs>
+ <period>
+ <min>-180.0</min>
+ <max>-180.0</max>
+ </period>
</some-element>
The enclosing element <some-element> is the element defined in each filter, like <input>, <u_min>,
OutputValue
==============================================================================
-Each filter drives one to many output properties. No scaling of offsetting is implemented
+Each filter drives one to many output properties. No scaling or offsetting is implemented
for the output value, these should be done in the filter itself.
The output properties are defined in the <output/> element by adding <property/> elements
within the <output/> element. For just a single output property, the <property/> element
min or max value defaults to 0 (zero).
Note: <u_min> and <u_max> may also occour within a <config> element.
<min> and <max> may be used as a substitude for the corresponding u_xxx element.
+<period> Complex Define a periodical input or output value. The phase width is defined by the
+ child elements <min> and <max> which are of type InputValue
Example: Limit the pilot's body temperature to a constant minimum of 36 and a maximum defined in
/pilots/max-body-temperature-degc, initialized to 40.0
<u_max>100.0</u_max>
</config>
+This defines the input or output as a periodic value with a phase width of 360, like
+the compass rose. Any value reaching the filter's input or leaving the filter at the
+output will be transformed to fit into the given range by adding or substracting one phase
+width of 360. Values of -270, 90 or 450 applied to this periodical element will allways
+result in +90. A value of 630, 270 or -90 will be normalized to -90 in the given example.
+<period>
+ <min>-180.0</min>
+ <max>180.0</max>
+</period>
+
+
<enable> Complex Define a condition to enable or disable the filter. For disabled
filters, no output computations are performed. Only enabled
filters fill the output properties. The default for undefined
<File
RelativePath="..\..\src\GUI\SafeTexFont.hxx">
</File>
+ <File RelativePath="..\..\..\src\GUI\WaypointList.cxx"></File>
+ <File RelativePath="..\..\..\src\GUI\WaypointList.hxx"></File>
</Filter>
<Filter
Name="Lib_Input"
RelativePath="..\..\..\src\GUI\SafeTexFont.hxx"
>
</File>
+ <File RelativePath="..\..\..\src\GUI\WaypointList.cxx"></File>
+ <File RelativePath="..\..\..\src\GUI\WaypointList.hxx"></File>
</Filter>
<Filter
Name="Lib_Input"
static string tempReg;
-class AI_OutOfSight{};
-class FP_Inactive{};
-
FGAIAircraft::FGAIAircraft(FGAISchedule *ref) : FGAIBase(otAircraft) {
trafficRef = ref;
if (trafficRef) {
void FGAIAircraft::Run(double dt) {
FGAIAircraft::dt = dt;
-
- try {
- updatePrimaryTargetValues(); // target hdg, alt, speed
- }
- catch (AI_OutOfSight) {
- return;
+
+ bool outOfSight = false,
+ flightplanActive = true;
+ updatePrimaryTargetValues(flightplanActive, outOfSight); // target hdg, alt, speed
+ if (outOfSight) {
+ return;
}
- catch (FP_Inactive) {
- //return;
- groundTargetSpeed = 0;
+
+ if (!flightplanActive) {
+ groundTargetSpeed = 0;
}
handleATCRequests(); // ATC also has a word to say
case 4: //Take off tower controller
if (trafficRef->getDepartureAirport()->getDynamics()) {
controller = trafficRef->getDepartureAirport()->getDynamics()->getTowerController();
- //if (trafficRef->getDepartureAirport()->getId() == "EHAM") {
- //string trns = trafficRef->getCallSign() + " at runway " + fp->getRunway() +
- // ". Ready for departure. " + trafficRef->getFlightType() + " to " +
- // trafficRef->getArrivalAirport()->getId();
- //fgSetString("/sim/messages/atc", trns.c_str());
- // if (controller == 0) {
- //cerr << "Error in assigning controller at " << trafficRef->getDepartureAirport()->getId() << endl;
- //}
- //}
} else {
cerr << "Error: Could not find Dynamics at airport : " << trafficRef->getDepartureAirport()->getId() << endl;
}
/**
* Update target values (heading, alt, speed) depending on flight plan or control properties
*/
-void FGAIAircraft::updatePrimaryTargetValues() {
+void FGAIAircraft::updatePrimaryTargetValues(bool& flightplanActive, bool& aiOutOfSight) {
if (fp) // AI object has a flightplan
{
//TODO make this a function of AIBase
}
if (trafficRef) {
//cerr << trafficRef->getRegistration() << " Setting altitude to " << altitude_ft;
- if (! aiTrafficVisible()) {
+ aiOutOfSight = !aiTrafficVisible();
+ if (aiOutOfSight) {
setDie(true);
//cerr << trafficRef->getRegistration() << " is set to die " << endl;
- throw AI_OutOfSight();
+ aiOutOfSight = true;
+ return;
}
}
timeElapsed = now - fp->getStartTime();
- if (! fp->isActive(now)) {
- throw FP_Inactive();
- }
+ flightplanActive = fp->isActive(now);
} else {
// no flight plan, update target heading, speed, and altitude
// from control properties. These default to the initial
#include <ATC/trafficcontrol.hxx>
#include <string>
-using std::string;
class PerformanceData;
void doGroundAltitude();
bool loadNextLeg ();
- void setAcType(const string& ac) { acType = ac; };
- void setCompany(const string& comp) { company = comp;};
+ void setAcType(const std::string& ac) { acType = ac; };
+ void setCompany(const std::string& comp) { company = comp;};
void announcePositionToController(); //TODO have to be public?
void processATC(FGATCInstruction instruction);
virtual const char* getTypeString(void) const { return "aircraft"; }
- string GetTransponderCode() { return transponderCode; };
- void SetTransponderCode(string tc) { transponderCode = tc;};
+ std::string GetTransponderCode() { return transponderCode; };
+ void SetTransponderCode(const std::string& tc) { transponderCode = tc;};
// included as performance data needs them, who else?
inline PerformanceData* getPerformance() { return _performance; };
inline double getVerticalSpeed() const { return vs; };
inline double altitudeAGL() const { return props->getFloatValue("position/altitude-agl-ft");};
inline double airspeed() const { return props->getFloatValue("velocities/airspeed-kt");};
- string atGate();
+ std::string atGate();
protected:
void Run(double dt);
void controlHeading(FGAIFlightPlan::waypoint* curr);
void controlSpeed(FGAIFlightPlan::waypoint* curr,
FGAIFlightPlan::waypoint* next);
- void updatePrimaryTargetValues();
+
+ void updatePrimaryTargetValues(bool& flightplanActive, bool& aiOutOfSight);
+
void updateSecondaryTargetValues();
void updatePosition();
void updateHeading();
double sign(double x);
- string acType;
- string company;
- string transponderCode;
+ std::string acType;
+ std::string company;
+ std::string transponderCode;
int spinCounter;
double prevSpeed;
{
if (model.valid()) {
- fgSetString("/ai/models/model-added", props->getPath());
+ fgSetString("/ai/models/model-added", props->getPath().c_str());
} else if (!model_path.empty()) {
SG_LOG(SG_INPUT, SG_WARN, "AIBase: Could not load model " << model_path);
noinst_LIBRARIES = libATC.a
libATC_a_SOURCES = \
+ atcutils.cxx atcutils.hxx \
trafficcontrol.cxx trafficcontrol.hxx
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
--- /dev/null
+// commlist.cxx -- comm frequency lookup class
+//
+// Written by David Luff and Alexander Kappes, started Jan 2003.
+// Based on navlist.cxx by Curtis Olson, started April 2000.
+//
+// Copyright (C) 2000 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.
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/misc/sgstream.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/math/sg_random.h>
+#include <simgear/bucket/newbucket.hxx>
+#include <Airports/simple.hxx>
+
+#include "atcutils.hxx"
+
+#if !ENABLE_ATCDCL
+
+
+FGCommList *current_commlist;
+
+
+// Constructor
+FGCommList::FGCommList( void ) {
+ sg_srandom_time();
+}
+
+
+// Destructor
+FGCommList::~FGCommList( void ) {
+}
+
+
+// load the navaids and build the map
+bool FGCommList::init( const SGPath& path ) {
+/*
+ SGPath temp = path;
+ commlist_freq.erase(commlist_freq.begin(), commlist_freq.end());
+ commlist_bck.erase(commlist_bck.begin(), commlist_bck.end());
+ temp.append( "ATC/default.atis" );
+ LoadComms(temp);
+ temp = path;
+ temp.append( "ATC/default.tower" );
+ LoadComms(temp);
+ temp = path;
+ temp.append( "ATC/default.ground" );
+ LoadComms(temp);
+ temp = path;
+ temp.append( "ATC/default.approach" );
+ LoadComms(temp);
+ return true;*/
+}
+
+
+bool FGCommList::LoadComms(const SGPath& path) {
+/*
+ sg_gzifstream fin( path.str() );
+ if ( !fin.is_open() ) {
+ SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
+ exit(-1);
+ }
+
+ // read in each line of the file
+ fin >> skipcomment;
+
+ while ( !fin.eof() ) {
+ ATCData a;
+ fin >> a;
+ if(a.type == INVALID) {
+ SG_LOG(SG_GENERAL, SG_DEBUG, "WARNING - INVALID type found in " << path.str() << '\n');
+ } else {
+ // Push all stations onto frequency map
+ commlist_freq[a.freq].push_back(a);
+
+ // Push non-atis stations onto bucket map as well
+ // In fact, push all stations onto bucket map for now so FGATCMgr::GetFrequency() works.
+ //if(a.type != ATIS and/or AWOS?) {
+ // get bucket number
+ SGBucket bucket(a.geod);
+ int bucknum = bucket.gen_index();
+ commlist_bck[bucknum].push_back(a);
+ //}
+ }
+
+ fin >> skipcomment;
+ }
+
+ fin.close();
+ return true;
+*/
+}
+
+
+// query the database for the specified frequency, lon and lat are in
+// degrees, elev is in meters
+// If no atc_type is specified, it returns true if any non-invalid type is found
+// If atc_type is specifed, returns true only if the specified type is found
+bool FGCommList::FindByFreq(const SGGeod& aPos, double freq,
+ ATCData* ad, atc_type tp )
+{
+/*
+ comm_list_type stations;
+ stations = commlist_freq[kHz10(freq)];
+ comm_list_iterator current = stations.begin();
+ comm_list_iterator last = stations.end();
+
+ // double az1, az2, s;
+ SGVec3d aircraft = SGVec3d::fromGeod(aPos);
+ const double orig_max_d = 1e100;
+ double max_d = orig_max_d;
+ double d;
+ // TODO - at the moment this loop returns the first match found in range
+ // We want to return the closest match in the event of a frequency conflict
+ for ( ; current != last ; ++current ) {
+ d = distSqr(aircraft, current->cart);
+
+ //cout << " dist = " << sqrt(d)
+ // << " range = " << current->range * SG_NM_TO_METER << endl;
+
+ // TODO - match up to twice the published range so we can model
+ // reduced signal strength
+ // NOTE The below is squared since we match to distance3Dsquared (above) to avoid a sqrt.
+ if ( d < (current->range * SG_NM_TO_METER
+ * current->range * SG_NM_TO_METER ) ) {
+ //cout << "matched = " << current->ident << endl;
+ if((tp == INVALID) || (tp == (*current).type)) {
+ if(d < max_d) {
+ max_d = d;
+ *ad = *current;
+ }
+ }
+ }
+ }
+
+ if(max_d < orig_max_d) {
+ return true;
+ } else {
+ return false;
+ }
+*/
+}
+
+int FGCommList::FindByPos(const SGGeod& aPos, double range, comm_list_type* stations, atc_type tp)
+{
+/*
+ // number of relevant stations found within range
+ int found = 0;
+ stations->erase(stations->begin(), stations->end());
+
+ // get bucket number for plane position
+ SGBucket buck(aPos);
+
+ // get neigboring buckets
+ int bx = (int)( range*SG_NM_TO_METER / buck.get_width_m() / 2) + 1;
+ int by = (int)( range*SG_NM_TO_METER / buck.get_height_m() / 2 ) + 1;
+
+ // loop over bucket range
+ for ( int i=-bx; i<=bx; i++) {
+ for ( int j=-by; j<=by; j++) {
+ buck = sgBucketOffset(aPos.getLongitudeDeg(), aPos.getLatitudeDeg(), i, j);
+ long int bucket = buck.gen_index();
+ comm_map_const_iterator Fstations = commlist_bck.find(bucket);
+ if (Fstations == commlist_bck.end()) continue;
+ comm_list_const_iterator current = Fstations->second.begin();
+ comm_list_const_iterator last = Fstations->second.end();
+
+
+ // double az1, az2, s;
+ SGVec3d aircraft = SGVec3d::fromGeod(aPos);
+ double d;
+ for(; current != last; ++current) {
+ if((current->type == tp) || (tp == INVALID)) {
+ d = distSqr(aircraft, current->cart);
+ // NOTE The below is squared since we match to distance3Dsquared (above) to avoid a sqrt.
+ if ( d < (current->range * SG_NM_TO_METER
+ * current->range * SG_NM_TO_METER ) ) {
+ stations->push_back(*current);
+ ++found;
+ }
+ }
+ }
+ }
+ }
+ return found;
+*/
+}
+
+
+// Returns the distance in meters to the closest station of a given type,
+// with the details written into ATCData& ad. If no type is specifed simply
+// returns the distance to the closest station of any type.
+// Returns -9999 if no stations found within max_range in nautical miles (default 100 miles).
+// Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if
+// say 300 miles is specifed 10, then 100, then 1000 will be searched, breaking at first result
+// and giving up after 1000.
+double FGCommList::FindClosest(const SGGeod& aPos, ATCData& ad, atc_type tp, double max_range) {
+/*
+ int num_stations = 0;
+ int range = 10;
+ comm_list_type stations;
+ comm_list_iterator itr;
+ double distance = -9999.0;
+
+ while(num_stations == 0) {
+ num_stations = FindByPos(aPos, range, &stations, tp);
+ if(num_stations) {
+ double closest = max_range * SG_NM_TO_METER;
+ double tmp;
+ for(itr = stations.begin(); itr != stations.end(); ++itr) {
+ ATCData ad2 = *itr;
+ const FGAirport *a = fgFindAirportID(ad2.ident);
+ if (a) {
+ tmp = dclGetHorizontalSeparation(ad2.geod, aPos);
+ if(tmp <= closest) {
+ closest = tmp;
+ distance = tmp;
+ ad = *itr;
+ }
+ }
+ }
+ //cout << "Closest station is " << ad.ident << " at a range of " << distance << " meters\n";
+ return(distance);
+ }
+ if(range > max_range) {
+ break;
+ }
+ range *= 10;
+ }
+ return(-9999.0);
+*/
+}
+
+
+// Find by Airport code.
+// This is basically a wrapper for a call to the airport database to get the airport
+// position followed by a call to FindByPos(...)
+bool FGCommList::FindByCode( const string& ICAO, ATCData& ad, atc_type tp ) {
+/*
+ const FGAirport *a = fgFindAirportID( ICAO);
+ if ( a) {
+ comm_list_type stations;
+ int found = FindByPos(a->geod(), 10.0, &stations, tp);
+ if(found) {
+ comm_list_iterator itr = stations.begin();
+ while(itr != stations.end()) {
+ if(((*itr).ident == ICAO) && ((*itr).type == tp)) {
+ ad = *itr;
+ //cout << "FindByCode returns " << ICAO
+ // << " type: " << tp
+ // << " freq: " << itr->freq
+ // << endl;
+ return true;
+ }
+ ++itr;
+ }
+ }
+ }
+ return false;
+*/
+}
+
+// TODO - this function should move somewhere else eventually!
+// Return an appropriate sequence number for an ATIS transmission.
+// Return sequence number + 2600 if sequence is unchanged since
+// last time.
+int FGCommList::GetAtisSequence( const string& apt_id,
+ const double tstamp, const int interval, const int special)
+{
+/*
+ atis_transmission_type tran;
+
+ if(atislog.find(apt_id) == atislog.end()) { // New station
+ tran.tstamp = tstamp - interval;
+// Random number between 0 and 25 inclusive, i.e. 26 equiprobable outcomes:
+ tran.sequence = int(sg_random() * LTRS);
+ atislog[apt_id] = tran;
+ //cout << "New ATIS station: " << apt_id << " seq-1: "
+ // << tran.sequence << endl;
+ }
+
+// calculate the appropriate identifier and update the log
+ tran = atislog[apt_id];
+
+ int delta = int((tstamp - tran.tstamp) / interval);
+ tran.tstamp += delta * interval;
+ if (special && !delta) delta++; // a "special" ATIS update is required
+ tran.sequence = (tran.sequence + delta) % LTRS;
+ atislog[apt_id] = tran;
+ //if (delta) cout << "New ATIS sequence: " << tran.sequence
+ // << " Delta: " << delta << endl;
+ return(tran.sequence + (delta ? 0 : LTRS*1000));
+*/
+}
+/*****************************************************************************
+ * FGKln89AlignedProjection
+ ****************************************************************************/
+
+FGKln89AlignedProjection::FGKln89AlignedProjection() {
+ _origin.setLatitudeRad(0);
+ _origin.setLongitudeRad(0);
+ _origin.setElevationM(0);
+ _correction_factor = cos(_origin.getLatitudeRad());
+}
+
+FGKln89AlignedProjection::FGKln89AlignedProjection(const SGGeod& centre, double heading) {
+ _origin = centre;
+ _theta = heading * SG_DEGREES_TO_RADIANS;
+ _correction_factor = cos(_origin.getLatitudeRad());
+}
+
+FGKln89AlignedProjection::~FGKln89AlignedProjection() {
+}
+
+void FGKln89AlignedProjection::Init(const SGGeod& centre, double heading) {
+ _origin = centre;
+ _theta = heading * SG_DEGREES_TO_RADIANS;
+ _correction_factor = cos(_origin.getLatitudeRad());
+}
+
+SGVec3d FGKln89AlignedProjection::ConvertToLocal(const SGGeod& pt) {
+ // convert from lat/lon to orthogonal
+ double delta_lat = pt.getLatitudeRad() - _origin.getLatitudeRad();
+ double delta_lon = pt.getLongitudeRad() - _origin.getLongitudeRad();
+ double y = sin(delta_lat) * SG_EQUATORIAL_RADIUS_M;
+ double x = sin(delta_lon) * SG_EQUATORIAL_RADIUS_M * _correction_factor;
+
+ // Align
+ if(_theta != 0.0) {
+ double xbar = x;
+ x = x*cos(_theta) - y*sin(_theta);
+ y = (xbar*sin(_theta)) + (y*cos(_theta));
+ }
+
+ return SGVec3d(x, y, pt.getElevationM());
+}
+
+SGGeod FGKln89AlignedProjection::ConvertFromLocal(const SGVec3d& pt) {
+ // de-align
+ double thi = _theta * -1.0;
+ double x = pt.x()*cos(thi) - pt.y()*sin(thi);
+ double y = (pt.x()*sin(thi)) + (pt.y()*cos(thi));
+
+ // convert from orthogonal to lat/lon
+ double delta_lat = asin(y / SG_EQUATORIAL_RADIUS_M);
+ double delta_lon = asin(x / SG_EQUATORIAL_RADIUS_M) / _correction_factor;
+
+ return SGGeod::fromRadM(_origin.getLongitudeRad()+delta_lon, _origin.getLatitudeRad()+delta_lat, pt.z());
+}
+
+#endif // #ENABLE_ATCDCL
\ No newline at end of file
--- /dev/null
+// atcutils.hxx
+//
+// This file contains a collection of classes from David Luff's
+// AI/ATC code that are shared by non-AI parts of FlightGear.
+// more specifcially, it contains implementations of FGCommList and
+// FGATCAlign
+//
+// Written by David Luff and Alexander Kappes, started Jan 2003.
+// Based on navlist.hxx by Curtis Olson, started April 2000.
+//
+// Copyright (C) 2000 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.
+
+/*****************************************************************
+*
+* FGCommList is used to store communication frequency information
+* for the ATC and AI subsystems. Two maps are maintained - one
+* searchable by location and one searchable by frequency. The
+* data structure returned from the search is the ATCData struct
+* defined in ATC.hxx, containing location, frequency, name, range
+* and type of the returned station.
+*
+******************************************************************/
+
+#ifndef _FG_ATCUTILS_HXX
+#define _FG_ATCUTILS_HXX
+
+
+
+#include <simgear/compiler.h>
+
+#include <map>
+#include <list>
+#include <string>
+
+//#include "ATC.hxx"
+//#include "atis.hxx"
+
+#if !ENABLE_ATCDCL
+
+class SGPath;
+class ATCData;
+
+// Possible types of ATC type that the radios may be tuned to.
+// INVALID implies not tuned in to anything.
+enum atc_type {
+ AWOS,
+ ATIS,
+ GROUND,
+ TOWER,
+ APPROACH,
+ DEPARTURE,
+ ENROUTE,
+ INVALID /* must be last element; see ATC_NUM_TYPES */
+};
+
+
+// A list of ATC stations
+typedef std::list < ATCData > comm_list_type;
+typedef comm_list_type::iterator comm_list_iterator;
+typedef comm_list_type::const_iterator comm_list_const_iterator;
+
+// A map of ATC station lists
+typedef std::map < int, comm_list_type > comm_map_type;
+typedef comm_map_type::iterator comm_map_iterator;
+typedef comm_map_type::const_iterator comm_map_const_iterator;
+
+
+class FGCommList {
+
+public:
+
+ FGCommList();
+ ~FGCommList();
+
+ // load all comm frequencies and build the map
+ bool init( const SGPath& path );
+
+ // query the database for the specified frequency, lon and lat are
+ // If no atc_type is specified, it returns true if any non-invalid type is found.
+ // If atc_type is specifed, returns true only if the specified type is found.
+ // Returns the station closest to the supplied position.
+ // The data found is written into the passed-in ATCData structure.
+ bool FindByFreq(const SGGeod& aPos, double freq, ATCData* ad, atc_type tp = INVALID );
+
+ // query the database by location, lon and lat are in degrees, elev is in meters, range is in nautical miles.
+ // Returns the number of stations of the specified atc_type tp that are in range of the position defined by
+ // lon, lat and elev, and pushes them into stations.
+ // If no atc_type is specifed, returns the number of all stations in range, and pushes them into stations
+ // ** stations is erased before use **
+ int FindByPos(const SGGeod& aPos, double range, comm_list_type* stations, atc_type tp = INVALID );
+
+ // Returns the distance in meters to the closest station of a given type,
+ // with the details written into ATCData& ad. If no type is specifed simply
+ // returns the distance to the closest station of any type.
+ // Returns -9999 if no stations found within max_range in nautical miles (default 100 miles).
+ // Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if
+ // say 300 miles is specifed 10, then 100, then 1000 will be searched, breaking at first result
+ // and giving up after 1000.
+ // !!!Be warned that searching anything over 100 miles will pause the sim unacceptably!!!
+ // (The ability to search longer ranges should be used during init only).
+ double FindClosest(const SGGeod& aPos, ATCData& ad, atc_type tp = INVALID, double max_range = 100.0 );
+
+ // Find by Airport code.
+ bool FindByCode( const std::string& ICAO, ATCData& ad, atc_type tp = INVALID );
+
+ // Return the sequence letter for an ATIS transmission given transmission time and airport id
+ // This maybe should get moved somewhere else!!
+ int GetAtisSequence( const std::string& apt_id, const double tstamp,
+ const int interval, const int flush=0);
+
+ // Comm stations mapped by frequency
+ //comm_map_type commlist_freq;
+
+ // Comm stations mapped by bucket
+ //comm_map_type commlist_bck;
+
+ // Load comms from a specified path (which must include the filename)
+private:
+
+ bool LoadComms(const SGPath& path);
+
+//----------- This stuff is left over from atislist.[ch]xx and maybe should move somewhere else
+ // Add structure and map for storing a log of atis transmissions
+ // made in this session of FlightGear. This allows the callsign
+ // to be allocated correctly wrt time.
+ //typedef struct {
+ // double tstamp;
+ // int sequence;
+ //} atis_transmission_type;
+
+ //typedef std::map < std::string, atis_transmission_type > atis_log_type;
+ //typedef atis_log_type::iterator atis_log_iterator;
+ //typedef atis_log_type::const_iterator atis_log_const_iterator;
+
+ //atis_log_type atislog;
+//-----------------------------------------------------------------------------------------------
+
+};
+
+
+extern FGCommList *current_commlist;
+
+// FGATCAlignedProjection - a class to project an area local to a runway onto an orthogonal co-ordinate system
+// with the origin at the threshold and the runway aligned with the y axis.
+class FGKln89AlignedProjection {
+
+public:
+ FGKln89AlignedProjection();
+ FGKln89AlignedProjection(const SGGeod& centre, double heading);
+ ~FGKln89AlignedProjection();
+
+ void Init(const SGGeod& centre, double heading);
+
+ // Convert a lat/lon co-ordinate (degrees) to the local projection (meters)
+ SGVec3d ConvertToLocal(const SGGeod& pt);
+
+ // Convert a local projection co-ordinate (meters) to lat/lon (degrees)
+ SGGeod ConvertFromLocal(const SGVec3d& pt);
+
+private:
+ SGGeod _origin; // lat/lon of local area origin (the threshold)
+ double _theta; // the rotation angle for alignment in radians
+ double _correction_factor; // Reduction in surface distance per degree of longitude due to latitude. Saves having to do a cos() every call.
+
+};
+
+#endif // #if ENABLE_ATCDCL
+
+#endif // _FG_ATCUTILS_HXX
+
+
speed(0),
altitude(0),
radius(0),
+ frequencyId(0),
allowTransmission(true) {
}
if (other.intentions.size())
{
for (intVecIterator j = other.intentions.begin(); j != other.intentions.end(); j++)
- {
+ {
// cerr << "Current segment 1 " << (*i) << endl;
if ((*i) > 0) {
if ((opp = net->findSegment(*i)->opposite()))
string sender, receiver;
int stationFreq = 0;
int taxiFreq = 0;
+ int freqId = 0;
string atisInformation;
- //double commFreqD;
- switch (msgDir) {
- case ATC_AIR_TO_GROUND:
- sender = rec->getAircraft()->getTrafficRef()->getCallSign();
- switch (rec->getLeg()) {
- case 2:
- case 3:
- stationFreq =
- rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(rec->getLeg());
- taxiFreq =
- rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(3);
- receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
- atisInformation = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getAtisInformation();
- break;
- case 4:
- receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
- break;
- }
- break;
- case ATC_GROUND_TO_AIR:
- receiver = rec->getAircraft()->getTrafficRef()->getCallSign();
- switch (rec->getLeg()) {
- case 2:
- case 3:
- stationFreq =
- rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(rec->getLeg());
- taxiFreq =
- rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(3);
- sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
- atisInformation = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getAtisInformation();
- break;
-
- case 4:
- sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
- break;
- }
- break;
- }
string text;
string taxiFreqStr;
double heading = 0;
string transponderCode;
FGAIFlightPlan *fp;
string fltRules;
+
+ //double commFreqD;
+ sender = rec->getAircraft()->getTrafficRef()->getCallSign();
+ switch (rec->getLeg()) {
+ case 2:
+ case 3:
+ freqId = rec->getNextFrequency();
+ stationFreq =
+ rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(rec->getLeg()+freqId);
+ taxiFreq =
+ rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(3);
+ receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
+ atisInformation = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getAtisInformation();
+ break;
+ case 4:
+ receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
+ break;
+ }
+ // Swap sender and receiver value in case of a ground to air transmission
+ if (msgDir == ATC_GROUND_TO_AIR) {
+ string tmp = sender;
+ sender = receiver;
+ receiver = tmp;
+ }
switch (msgId) {
case MSG_ANNOUNCE_ENGINE_START:
text = sender + ". Ready to Start up";
// Assign SID, if necessery (TODO)
case MSG_PERMIT_ENGINE_START:
taxiFreqStr = formatATCFrequency3_2(taxiFreq);
-
+
heading = rec->getAircraft()->getTrafficRef()->getCourse();
fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
rwyClass= rec->getAircraft()->GetFlightPlan()->getRunwayClassFromTrafficType(fltType);
activeRunway + ", " + SID + ", squawk " + transponderCode + ". " +
"For push-back and taxi clearance call " + taxiFreqStr + ". " + sender;
break;
+ case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
+ taxiFreqStr = formatATCFrequency3_2(taxiFreq);
+ text = receiver + ". Switching to " + taxiFreqStr + ". " + sender;
+ break;
case MSG_REQUEST_PUSHBACK_CLEARANCE:
text = receiver + ". Request push-back. " + sender;
break;
// Display ATC message only when one of the radios is tuned
// the relevant frequency.
// Note that distance attenuation is currently not yet implemented
- //cerr << "Transmitting " << text << " at " << stationFreq;
if ((onBoardRadioFreqI0 == stationFreq) || (onBoardRadioFreqI1 == stationFreq)) {
if (rec->allowTransmissions()) {
fgSetString("/sim/messages/atc", text.c_str());
return string(buffer);
}
+// TODO: Set transponder codes according to real-world routes.
+// The current version just returns a random string of four octal numbers.
string FGATCController::genTransponderCode(string fltRules) {
if (fltRules == "VFR") {
return string("1200");
available = false;
}
}
- // TODO: Switch to APRON control and request pushback Clearance.
- // Get Push back clearance
+ // Note: The next two stages are only necessesary when Startup control is
+ // on a different frequency, compared to ground control
if ((state == 4) && available){
if (now > startTime+130) {
- transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND);
+ transmit(&(*i), MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, ATC_AIR_TO_GROUND);
i->updateState();
+ i->nextFrequency();
lastTransmission = now;
available = false;
}
}
+
+
+ // TODO: Switch to APRON control and request pushback Clearance.
+ // Get Push back clearance
if ((state == 5) && available){
- if (now > startTime+130) {
+ if (now > startTime+160) {
+ transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND);
+ i->updateState();
+ lastTransmission = now;
+ available = false;
+ }
+ }
+ if ((state == 6) && available){
+ if (now > startTime+180) {
if (i->pushBackAllowed()) {
i->allowRepeatedTransmissions();
transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE, ATC_GROUND_TO_AIR);
class FGAIFlightPlan; // forward reference
class FGGroundNetwork; // forward reference
-//class FGAISchedule; // forward reference
class FGAIAircraft; // forward reference
/**************************************************************************************
int id, waitsForId;
int currentPos;
int leg;
+ int frequencyId;
int state;
bool allowTransmission;
time_t timer;
bool allowTransmissions() { return allowTransmission; };
void suppressRepeatedTransmissions () { allowTransmission=false; };
void allowRepeatedTransmissions () { allowTransmission=true; };
+ void nextFrequency() { frequencyId++; };
+ int getNextFrequency() { return frequencyId; };
};
typedef vector<FGTrafficRecord> TrafficVector;
MSG_ACKNOWLEDGE_ENGINE_START,
MSG_REQUEST_PUSHBACK_CLEARANCE,
MSG_PERMIT_PUSHBACK_CLEARANCE,
- MSG_HOLD_PUSHBACK_CLEARANCE } AtcMsgId;
+ MSG_HOLD_PUSHBACK_CLEARANCE,
+ MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY } AtcMsgId;
typedef enum {
ATC_AIR_TO_GROUND,
ATC_GROUND_TO_AIR } AtcMsgDir;
timingInfo.clear();
stamp("begin");
static SGPropertyNode *replay_master
- = fgGetNode( "/sim/freeze/replay", true );
+ = fgGetNode( "/sim/freeze/replay-state", true );
if( disable_replay->getBoolValue() ) {
- if( sim_time != 0.0 ) {
+ if ( sim_time != 0.0 ) {
// we were recording data
init();
}
return;
}
//stamp("point_01");
- if ( replay_master->getBoolValue() ) {
+ if ( replay_master->getIntValue() > 0 ) {
// don't record the replay session
return;
}
r = new FGReplayData;
stamp("Replay_02");
} else {
- r = recycler.front();
- recycler.pop_front();
- //stamp("point_04be");
+ r = recycler.front();
+ recycler.pop_front();
+ //stamp("point_04be");
}
r->sim_time = sim_time;
//r->ctrls = c;
#include "simple.hxx"
#include "runways.hxx"
#include "pavement.hxx"
-#include <ATCDCL/commlist.hxx>
+#if ENABLE_ATCDCL
+# include <ATCDCL/commlist.hxx>
+#else
+ #include <ATC/atcutils.hxx>
+#endif
#include <iostream>
last_apt_type("")
{}
+
+
void parseAPT(const string &aptdb_file, FGCommList *comm_list)
{
sg_gzifstream in( aptdb_file );
} else if ( line_id == 0 ) {
// ??
} else if ( line_id == 50 ) {
+
parseATISLine(comm_list, simgear::strutils::split(line));
+
} else if ( line_id >= 51 && line_id <= 56 ) {
// other frequency entries (ignore)
} else if ( line_id == 110 ) {
pvt->addNode(pos, num == 113);
}
}
-
+
void parseATISLine(FGCommList *comm_list, const vector<string>& token)
{
if ( rwy_count <= 0 ) {
// 50 11770 AWOS 3
// This code parallels code found in "operator>>" in ATC.hxx;
// FIXME: unify the code.
+#if ENABLE_ATCDCL
ATCData a;
a.geod = SGGeod::fromDegFt(rwy_lon_accum / (double)rwy_count,
rwy_lat_accum / (double)rwy_count, last_apt_elev);
SGBucket bucket(a.geod);
int bucknum = bucket.gen_index();
comm_list->commlist_bck[bucknum].push_back(a);
+#else
+#endif
#if 0
SG_LOG( SG_GENERAL, SG_ALERT,
"Loaded ATIS/AWOS for airport: " << a.ident
<< " type: " << a.type );
#endif
}
+
};
+
// Load the airport data base from the specified aptdb file. The
// metar file is used to mark the airports as having metar available
// or not.
{
APTLoader ld;
-
ld.parseAPT(aptdb_file, comm_list);
-
//
// Load the metar.dat file and update apt db with stations that
// have metar data.
// Load the airport data base from the specified aptdb file. The
// metar file is used to mark the airports as having metar available
// or not.
+
bool fgAirportDBLoad( const std::string &aptdb_file,
FGCommList *comm_list, const std::string &metar_file );
-
#endif // _FG_APT_LOADER_HXX
void FGGroundNetwork::update(int id, double lat, double lon, double heading, double speed, double alt,
double dt) {
+ // Check whether aircraft are on hold due to a preceding pushback. If so, make sure to
+ // Transmit air-to-ground "Ready to taxi request:
+ // Transmit ground to air approval / hold
+ // Transmit confirmation ...
+ // Probably use a status mechanism similar to the Engine start procedure in the startup controller.
+
+
TrafficVectorIterator i = activeTraffic.begin();
// Search search if the current id has an entry
// This might be faster using a map instead of a vector, but let's start by taking a safe route
wpn->getChild("eta", 0, true);
_route->clear();
+ _route->set_current(0);
update_mirror();
_pathNode = fgGetNode(RM "file-path", 0, true);
aProp->setStringValue( eta_str );
}
-void FGRouteMgr::add_waypoint( const SGWayPoint& wp, int n ) {
+void FGRouteMgr::add_waypoint( const SGWayPoint& wp, int n )
+{
_route->add_waypoint( wp, n );
- if (_route->current_index() > n) {
+ if ((n >= 0) && (_route->current_index() > n)) {
_route->set_current(_route->current_index() + 1);
}
+ waypointsChanged();
+}
+
+void FGRouteMgr::waypointsChanged()
+{
+ double routeDistanceNm = _route->total_distance() * SG_METER_TO_NM;
+ totalDistance->setDoubleValue(routeDistanceNm);
+ double cruiseSpeedKts = cruise->getDoubleValue("speed", 0.0);
+ if (cruiseSpeedKts > 1.0) {
+ // very very crude approximation, doesn't allow for climb / descent
+ // performance or anything else at all
+ ete->setDoubleValue(routeDistanceNm / cruiseSpeedKts * (60.0 * 60.0));
+ }
+
update_mirror();
_edited->fireValueChanged();
+ checkFinished();
}
-
SGWayPoint FGRouteMgr::pop_waypoint( int n ) {
if ( _route->size() <= 0 ) {
return SGWayPoint();
SGWayPoint wp = _route->get_waypoint(n);
_route->delete_waypoint(n);
- update_mirror();
- _edited->fireValueChanged();
- checkFinished();
-
+ waypointsChanged();
return wp;
}
mgr->loadRoute();
} else if (!strcmp(s, "@SAVE")) {
mgr->saveRoute();
- } else if (!strcmp(s, "@POP"))
- mgr->pop_waypoint(0);
- else if (!strncmp(s, "@DELETE", 7))
+ } else if (!strcmp(s, "@POP")) {
+ SG_LOG(SG_AUTOPILOT, SG_WARN, "route-manager @POP command is deprecated");
+ } else if (!strcmp(s, "@NEXT")) {
+ mgr->jumpToIndex(mgr->currentWaypoint() + 1);
+ } else if (!strcmp(s, "@PREVIOUS")) {
+ mgr->jumpToIndex(mgr->currentWaypoint() - 1);
+ } else if (!strncmp(s, "@JUMP", 5)) {
+ mgr->jumpToIndex(atoi(s + 5));
+ } else if (!strncmp(s, "@DELETE", 7))
mgr->pop_waypoint(atoi(s + 7));
else if (!strncmp(s, "@INSERT", 7)) {
char *r;
}
_route->set_current(0);
-
- double routeDistanceNm = _route->total_distance() * SG_METER_TO_NM;
- totalDistance->setDoubleValue(routeDistanceNm);
- double cruiseSpeedKts = cruise->getDoubleValue("speed", 0.0);
- if (cruiseSpeedKts > 1.0) {
- // very very crude approximation, doesn't allow for climb / descent
- // performance or anything else at all
- ete->setDoubleValue(routeDistanceNm / cruiseSpeedKts * (60.0 * 60.0));
- }
-
active->setBoolValue(true);
SG_LOG(SG_AUTOPILOT, SG_INFO, "route-manager, activate route ok");
return true;
void FGRouteMgr::jumpToIndex(int index)
{
- if (!active->getBoolValue()) {
- SG_LOG(SG_AUTOPILOT, SG_ALERT, "trying to sequence waypoints with no active route");
- return;
- }
-
if ((index < 0) || (index >= _route->size())) {
SG_LOG(SG_AUTOPILOT, SG_ALERT, "passed invalid index (" <<
index << ") to FGRouteMgr::jumpToIndex");
return _route->current_index();
}
+void FGRouteMgr::setWaypointTargetAltitudeFt(unsigned int index, int altFt)
+{
+ SGWayPoint wp = _route->get_waypoint(index);
+ wp.setTargetAltFt(altFt);
+ // simplest way to update a waypoint is to remove and re-add it
+ _route->delete_waypoint(index);
+ _route->add_waypoint(wp, index);
+ waypointsChanged();
+}
+
void FGRouteMgr::saveRoute()
{
SGPath path(_pathNode->getStringValue());
*/
SGWayPoint* make_waypoint(const string& target);
+ /**
+ * Helper to keep various pieces of state in sync when the SGRoute is
+ * modified (waypoints added, inserted, removed). Notably, this fires the
+ * 'edited' signal.
+ */
+ void waypointsChanged();
void update_mirror();
*/
void jumpToIndex(int index);
+ /**
+ *
+ */
+ void setWaypointTargetAltitudeFt(unsigned int index, int altFt);
+
void saveRoute();
void loadRoute();
};
using std::cout;
using std::endl;
+FGPeriodicalValue::FGPeriodicalValue( SGPropertyNode_ptr root )
+{
+ SGPropertyNode_ptr minNode = root->getChild( "min" );
+ SGPropertyNode_ptr maxNode = root->getChild( "max" );
+ if( minNode == NULL || maxNode == NULL ) {
+ SG_LOG(SG_AUTOPILOT, SG_ALERT, "periodical defined, but no <min> and/or <max> tag. Period ignored." );
+ } else {
+ minPeriod = new FGXMLAutoInput( minNode );
+ maxPeriod = new FGXMLAutoInput( maxNode );
+ }
+}
+
+double FGPeriodicalValue::normalize( double value )
+{
+ if( !(minPeriod && maxPeriod )) return value;
+
+ double p1 = minPeriod->get_value();
+ double p2 = maxPeriod->get_value();
+
+ double min = std::min<double>(p1,p2);
+ double max = std::max<double>(p1,p2);
+ double phase = fabs(max - min);
+
+ if( phase > SGLimitsd::min() ) {
+ while( value < min ) value += phase;
+ while( value >= max ) value -= phase;
+ } else {
+ value = min; // phase is zero
+ }
+
+ return value;
+}
+
FGXMLAutoInput::FGXMLAutoInput( SGPropertyNode_ptr node, double value, double offset, double scale) :
value(0.0),
abs(false),
- property(NULL),
- offset(NULL),
- scale(NULL),
- min(NULL),
- max(NULL),
_condition(NULL)
{
parse( node, value, offset, scale );
scale = NULL;
min = NULL;
max = NULL;
+ periodical = NULL;
if( node == NULL )
return;
SGPropertyNode * n;
if( (n = node->getChild("condition")) != NULL ) {
- _condition = sgReadCondition(node, n);
+ _condition = sgReadCondition(fgGetNode("/"), n);
}
if( (n = node->getChild( "scale" )) != NULL ) {
abs = n->getBoolValue();
}
+ if( (n = node->getChild( "period" )) != NULL ) {
+ periodical = new FGPeriodicalValue( n );
+ }
+
SGPropertyNode *valueNode = node->getChild( "value" );
if ( valueNode != NULL ) {
value = valueNode->getDoubleValue();
if( value > m )
value = m;
}
+
+ if( periodical ) {
+ value = periodical->normalize( value );
+ }
return abs ? fabs(value) : value;
}
debug = child->getBoolValue();
} else if ( cname == "enable" ) {
if( (prop = child->getChild("condition")) != NULL ) {
- _condition = sgReadCondition(child, prop);
+ _condition = sgReadCondition(fgGetNode("/"), prop);
} else {
if ( (prop = child->getChild( "prop" )) != NULL ) {
enable_prop = fgGetNode( prop->getStringValue(), true );
umaxInput.push_back( new FGXMLAutoInput( child ) );
} else if ( cname == "u_max" ) {
umaxInput.push_back( new FGXMLAutoInput( child ) );
+ } else if ( cname == "period" ) {
+ periodical = new FGPeriodicalValue( child );
} else {
SG_LOG(SG_AUTOPILOT, SG_ALERT, "malformed autopilot definition - unrecognized node:"
<< cname << " in section " << name);
double FGXMLAutoComponent::clamp( double value )
{
+ //If this is a periodical value, normalize it into our domain
+ // before clamping
+ if( periodical )
+ value = periodical->normalize( value );
+
// clamp, if either min or max is defined
if( uminInput.size() + umaxInput.size() > 0 ) {
double d = umaxInput.get_value( 0.0 );
*/
void FGPIDController::update( double dt ) {
- double ep_n; // proportional error with reference weighing
double e_n; // error
- double ed_n; // derivative error
- double edf_n; // derivative error filter
- double Tf; // filter time
+ double edf_n;
double delta_u_n = 0.0; // incremental output
double u_n = 0.0; // absolute output
double Ts; // sampling interval (sec)
if ( debug ) cout << " input = " << y_n << " ref = " << r_n << endl;
// Calculates proportional error:
- ep_n = beta * r_n - y_n;
+ double ep_n = beta * r_n - y_n;
if ( debug ) cout << " ep_n = " << ep_n;
if ( debug ) cout << " ep_n_1 = " << ep_n_1;
e_n = r_n - y_n;
if ( debug ) cout << " e_n = " << e_n;
- // Calculates derivate error:
- ed_n = gamma * r_n - y_n;
- if ( debug ) cout << " ed_n = " << ed_n;
-
double td = Td.get_value();
- if ( td > 0.0 ) {
+ if ( td > 0.0 ) { // do we need to calcluate derivative error?
+
+ // Calculates derivate error:
+ double ed_n = gamma * r_n - y_n;
+ if ( debug ) cout << " ed_n = " << ed_n;
+
// Calculates filter time:
- Tf = alpha * td;
+ double Tf = alpha * td;
if ( debug ) cout << " Tf = " << Tf;
// Filters the derivate error:
+ ed_n * (Ts/Tf) / (Ts/Tf + 1);
if ( debug ) cout << " edf_n = " << edf_n;
} else {
- edf_n = ed_n;
+ edf_n_2 = edf_n_1 = edf_n = 0.0;
}
// Calculates the incremental output:
delta_u_n = Kp.get_value() * ( (ep_n - ep_n_1)
+ ((Ts/ti) * e_n)
+ ((td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2)) );
- }
- if ( debug ) {
- cout << " delta_u_n = " << delta_u_n << endl;
- cout << "P:" << Kp.get_value() * (ep_n - ep_n_1)
- << " I:" << Kp.get_value() * ((Ts/ti) * e_n)
- << " D:" << Kp.get_value() * ((td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2))
- << endl;
+ if ( debug ) {
+ cout << " delta_u_n = " << delta_u_n << endl;
+ cout << "P:" << Kp.get_value() * (ep_n - ep_n_1)
+ << " I:" << Kp.get_value() * ((Ts/ti) * e_n)
+ << " D:" << Kp.get_value() * ((td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2))
+ << endl;
+ }
}
// Integrator anti-windup logic:
set_output_value( u_n );
} else if ( !enabled ) {
- ep_n = 0.0;
- edf_n = 0.0;
- // Updates indexed values;
- u_n_1 = u_n;
- ep_n_1 = ep_n;
- edf_n_2 = edf_n_1;
- edf_n_1 = edf_n;
+ ep_n_1 = 0.0;
+ edf_n_2 = edf_n_1 = edf_n = 0.0;
}
}
<< " error = " << error
<< endl;
- double prop_comp = error * Kp.get_value();
+ double prop_comp = clamp(error * Kp.get_value());
int_sum += error * Ki.get_value() * dt;
+ double output = prop_comp + int_sum;
+ double clamped_output = clamp( output );
+ if( output != clamped_output ) // anti-windup
+ int_sum = clamped_output - prop_comp;
+
if ( debug ) cout << "prop_comp = " << prop_comp
<< " int_sum = " << int_sum << endl;
- double output = prop_comp + int_sum;
- output = clamp( output );
- set_output_value( output );
- if ( debug ) cout << "output = " << output << endl;
+ set_output_value( clamped_output );
+ if ( debug ) cout << "output = " << clamped_output << endl;
}
}
filterType = gain;
} else if (val == "reciprocal") {
filterType = reciprocal;
+ } else if (val == "differential") {
+ filterType = differential;
+ // use a constant of two samples for current and previous input value
+ samplesInput.push_back( new FGXMLAutoInput(NULL, 2.0 ) );
}
} else if (aName == "filter-time" ) {
TfInput.push_back( new FGXMLAutoInput( aNode, 1.0 ) );
{
if ( isPropertyEnabled() ) {
- input.push_front(valueInput.get_value());
+ input.push_front(valueInput.get_value()-referenceInput.get_value());
input.resize(samplesInput.get_value() + 1, 0.0);
if ( !enabled ) {
do_feedback();
}
- if ( enabled && dt > 0.0 ) {
- /*
- * Exponential filter
- *
- * Output[n] = alpha*Input[n] + (1-alpha)*Output[n-1]
- *
- */
- if( debug ) cout << "Updating " << get_name()
- << " dt " << dt << endl;
-
- if (filterType == exponential)
- {
- double alpha = 1 / ((TfInput.get_value()/dt) + 1);
- output.push_front(alpha * input[0] +
- (1 - alpha) * output[0]);
- }
- else if (filterType == doubleExponential)
+ if ( !enabled || dt < SGLimitsd::min() )
+ return;
+
+ /*
+ * Exponential filter
+ *
+ * Output[n] = alpha*Input[n] + (1-alpha)*Output[n-1]
+ *
+ */
+ if( debug ) cout << "Updating " << get_name()
+ << " dt " << dt << endl;
+
+ if (filterType == exponential)
+ {
+ double alpha = 1 / ((TfInput.get_value()/dt) + 1);
+ output.push_front(alpha * input[0] +
+ (1 - alpha) * output[0]);
+ }
+ else if (filterType == doubleExponential)
+ {
+ double alpha = 1 / ((TfInput.get_value()/dt) + 1);
+ output.push_front(alpha * alpha * input[0] +
+ 2 * (1 - alpha) * output[0] -
+ (1 - alpha) * (1 - alpha) * output[1]);
+ }
+ else if (filterType == movingAverage)
+ {
+ output.push_front(output[0] +
+ (input[0] - input.back()) / samplesInput.get_value());
+ }
+ else if (filterType == noiseSpike)
+ {
+ double maxChange = rateOfChangeInput.get_value() * dt;
+
+ if ((output[0] - input[0]) > maxChange)
{
- double alpha = 1 / ((TfInput.get_value()/dt) + 1);
- output.push_front(alpha * alpha * input[0] +
- 2 * (1 - alpha) * output[0] -
- (1 - alpha) * (1 - alpha) * output[1]);
+ output.push_front(output[0] - maxChange);
}
- else if (filterType == movingAverage)
+ else if ((output[0] - input[0]) < -maxChange)
{
- output.push_front(output[0] +
- (input[0] - input.back()) / samplesInput.get_value());
+ output.push_front(output[0] + maxChange);
}
- else if (filterType == noiseSpike)
+ else if (fabs(input[0] - output[0]) <= maxChange)
{
- double maxChange = rateOfChangeInput.get_value() * dt;
-
- if ((output[0] - input[0]) > maxChange)
- {
- output.push_front(output[0] - maxChange);
- }
- else if ((output[0] - input[0]) < -maxChange)
- {
- output.push_front(output[0] + maxChange);
- }
- else if (fabs(input[0] - output[0]) <= maxChange)
- {
- output.push_front(input[0]);
- }
+ output.push_front(input[0]);
}
- else if (filterType == gain)
- {
- output[0] = gainInput.get_value() * input[0];
+ }
+ else if (filterType == gain)
+ {
+ output[0] = gainInput.get_value() * input[0];
+ }
+ else if (filterType == reciprocal)
+ {
+ if (input[0] != 0.0) {
+ output[0] = gainInput.get_value() / input[0];
}
- else if (filterType == reciprocal)
- {
- if (input[0] != 0.0) {
- output[0] = gainInput.get_value() / input[0];
- }
+ }
+ else if (filterType == differential)
+ {
+ if( dt > SGLimitsd::min() ) {
+ output[0] = (input[0]-input[1]) * TfInput.get_value() / dt;
}
+ }
- output[0] = clamp(output[0]) ;
- set_output_value( output[0] );
+ output[0] = clamp(output[0]) ;
+ set_output_value( output[0] );
- output.resize(2);
+ output.resize(2);
- if (debug)
- {
- cout << "input:" << input[0]
- << "\toutput:" << output[0] << endl;
- }
+ if (debug)
+ {
+ cout << "input:" << input[0]
+ << "\toutput:" << output[0] << endl;
}
}
-
-FGXMLAutopilot::FGXMLAutopilot() {
+FGXMLAutoLogic::FGXMLAutoLogic(SGPropertyNode * node ) :
+ FGXMLAutoComponent(),
+ inverted(false)
+{
+ parseNode(node);
}
-
-FGXMLAutopilot::~FGXMLAutopilot() {
+bool FGXMLAutoLogic::parseNodeHook(const std::string& aName, SGPropertyNode* aNode)
+{
+ if (aName == "input") {
+ input = sgReadCondition( fgGetNode("/"), aNode );
+ } else if (aName == "inverted") {
+ inverted = aNode->getBoolValue();
+ } else {
+ return false;
+ }
+
+ return true;
}
-
-void FGXMLAutopilot::init() {
- config_props = fgGetNode( "/autopilot/new-config", true );
-
- SGPropertyNode *path_n = fgGetNode("/sim/systems/autopilot/path");
-
- if ( path_n ) {
- SGPath config( globals->get_fg_root() );
- config.append( path_n->getStringValue() );
-
- SG_LOG( SG_ALL, SG_INFO, "Reading autopilot configuration from "
- << config.str() );
- try {
- readProperties( config.str(), config_props );
-
- if ( ! build() ) {
- SG_LOG( SG_ALL, SG_ALERT,
- "Detected an internal inconsistency in the autopilot");
- SG_LOG( SG_ALL, SG_ALERT,
- " configuration. See earlier errors for" );
- SG_LOG( SG_ALL, SG_ALERT,
- " details.");
- exit(-1);
- }
- } catch (const sg_exception& e) {
- SG_LOG( SG_ALL, SG_ALERT, "Failed to load autopilot configuration: "
- << config.str() << ":" << e.getMessage() );
+void FGXMLAutoLogic::update(double dt)
+{
+ if ( isPropertyEnabled() ) {
+ if ( !enabled ) {
+ // we have just been enabled
}
-
+ enabled = true;
} else {
- SG_LOG( SG_ALL, SG_WARN,
- "No autopilot configuration specified for this model!");
+ enabled = false;
+ do_feedback();
}
-}
+ if ( !enabled || dt < SGLimitsd::min() )
+ return;
-void FGXMLAutopilot::reinit() {
- components.clear();
- init();
-}
+ if( input == NULL ) {
+ if ( debug ) cout << "No input for " << get_name() << endl;
+ return;
+ }
+ bool i = input->test();
-void FGXMLAutopilot::bind() {
-}
+ if ( debug ) cout << "Updating " << get_name() << ": " << (inverted ? !i : i) << endl;
-void FGXMLAutopilot::unbind() {
+ set_output_value( i );
}
-bool FGXMLAutopilot::build() {
- SGPropertyNode *node;
- int i;
-
- int count = config_props->nChildren();
- for ( i = 0; i < count; ++i ) {
- node = config_props->getChild(i);
- string name = node->getName();
- // cout << name << endl;
- if ( name == "pid-controller" ) {
- components.push_back( new FGPIDController( node ) );
- } else if ( name == "pi-simple-controller" ) {
- components.push_back( new FGPISimpleController( node ) );
- } else if ( name == "predict-simple" ) {
- components.push_back( new FGPredictor( node ) );
- } else if ( name == "filter" ) {
- components.push_back( new FGDigitalFilter( node ) );
- } else {
- SG_LOG( SG_ALL, SG_ALERT, "Unknown top level section: "
- << name );
- return false;
- }
- }
- return true;
+FGXMLAutopilotGroup::FGXMLAutopilotGroup() :
+ SGSubsystemGroup()
+#ifdef XMLAUTO_USEHELPER
+ ,average(0.0), // average/filtered prediction
+ v_last(0.0), // last velocity
+ last_static_pressure(0.0),
+ vel(fgGetNode( "/velocities/airspeed-kt", true )),
+ // Estimate speed in 5,10 seconds
+ lookahead5(fgGetNode( "/autopilot/internal/lookahead-5-sec-airspeed-kt", true )),
+ lookahead10(fgGetNode( "/autopilot/internal/lookahead-10-sec-airspeed-kt", true )),
+ bug(fgGetNode( "/autopilot/settings/heading-bug-deg", true )),
+ mag_hdg(fgGetNode( "/orientation/heading-magnetic-deg", true )),
+ bug_error(fgGetNode( "/autopilot/internal/heading-bug-error-deg", true )),
+ fdm_bug_error(fgGetNode( "/autopilot/internal/fdm-heading-bug-error-deg", true )),
+ target_true(fgGetNode( "/autopilot/settings/true-heading-deg", true )),
+ true_hdg(fgGetNode( "/orientation/heading-deg", true )),
+ true_error(fgGetNode( "/autopilot/internal/true-heading-error-deg", true )),
+ target_nav1(fgGetNode( "/instrumentation/nav[0]/radials/target-auto-hdg-deg", true )),
+ true_nav1(fgGetNode( "/autopilot/internal/nav1-heading-error-deg", true )),
+ true_track_nav1(fgGetNode( "/autopilot/internal/nav1-track-error-deg", true )),
+ nav1_course_error(fgGetNode( "/autopilot/internal/nav1-course-error", true )),
+ nav1_selected_course(fgGetNode( "/instrumentation/nav[0]/radials/selected-deg", true )),
+ vs_fps(fgGetNode( "/velocities/vertical-speed-fps", true )),
+ vs_fpm(fgGetNode( "/autopilot/internal/vert-speed-fpm", true )),
+ static_pressure(fgGetNode( "/systems/static[0]/pressure-inhg", true )),
+ pressure_rate(fgGetNode( "/autopilot/internal/pressure-rate", true )),
+ track(fgGetNode( "/orientation/track-deg", true ))
+#endif
+{
}
-
-/*
- * Update helper values
- */
-static void update_helper( double dt ) {
- // Estimate speed in 5,10 seconds
- static SGPropertyNode_ptr vel = fgGetNode( "/velocities/airspeed-kt", true );
- static SGPropertyNode_ptr lookahead5
- = fgGetNode( "/autopilot/internal/lookahead-5-sec-airspeed-kt", true );
- static SGPropertyNode_ptr lookahead10
- = fgGetNode( "/autopilot/internal/lookahead-10-sec-airspeed-kt", true );
-
- static double average = 0.0; // average/filtered prediction
- static double v_last = 0.0; // last velocity
-
+void FGXMLAutopilotGroup::update( double dt )
+{
+ // update all configured autopilots
+ SGSubsystemGroup::update( dt );
+#ifdef XMLAUTO_USEHELPER
+ // update helper values
double v = vel->getDoubleValue();
double a = 0.0;
if ( dt > 0.0 ) {
v_last = v;
}
- // Calculate heading bug error normalized to +/- 180.0 (based on
- // DG indicated heading)
- static SGPropertyNode_ptr bug
- = fgGetNode( "/autopilot/settings/heading-bug-deg", true );
- static SGPropertyNode_ptr ind_hdg
- = fgGetNode( "/instrumentation/heading-indicator/indicated-heading-deg",
- true );
- static SGPropertyNode_ptr ind_bug_error
- = fgGetNode( "/autopilot/internal/heading-bug-error-deg", true );
-
- double diff = bug->getDoubleValue() - ind_hdg->getDoubleValue();
- if ( diff < -180.0 ) { diff += 360.0; }
- if ( diff > 180.0 ) { diff -= 360.0; }
- ind_bug_error->setDoubleValue( diff );
-
- // Calculate heading bug error normalized to +/- 180.0 (based on
- // actual/nodrift magnetic-heading, i.e. a DG slaved to magnetic
- // compass.)
- static SGPropertyNode_ptr mag_hdg
- = fgGetNode( "/orientation/heading-magnetic-deg", true );
- static SGPropertyNode_ptr fdm_bug_error
- = fgGetNode( "/autopilot/internal/fdm-heading-bug-error-deg", true );
-
- diff = bug->getDoubleValue() - mag_hdg->getDoubleValue();
- if ( diff < -180.0 ) { diff += 360.0; }
- if ( diff > 180.0 ) { diff -= 360.0; }
+ // Calculate heading bug error normalized to +/- 180.0
+ double diff = bug->getDoubleValue() - mag_hdg->getDoubleValue();
+ SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
+ bug_error->setDoubleValue( diff );
+
fdm_bug_error->setDoubleValue( diff );
// Calculate true heading error normalized to +/- 180.0
- static SGPropertyNode_ptr target_true
- = fgGetNode( "/autopilot/settings/true-heading-deg", true );
- static SGPropertyNode_ptr true_hdg
- = fgGetNode( "/orientation/heading-deg", true );
- static SGPropertyNode_ptr true_track
- = fgGetNode( "/instrumentation/gps/indicated-track-true-deg", true );
- static SGPropertyNode_ptr true_error
- = fgGetNode( "/autopilot/internal/true-heading-error-deg", true );
-
diff = target_true->getDoubleValue() - true_hdg->getDoubleValue();
- if ( diff < -180.0 ) { diff += 360.0; }
- if ( diff > 180.0 ) { diff -= 360.0; }
+ SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
true_error->setDoubleValue( diff );
// Calculate nav1 target heading error normalized to +/- 180.0
- static SGPropertyNode_ptr target_nav1
- = fgGetNode( "/instrumentation/nav[0]/radials/target-auto-hdg-deg", true );
- static SGPropertyNode_ptr true_nav1
- = fgGetNode( "/autopilot/internal/nav1-heading-error-deg", true );
- static SGPropertyNode_ptr true_track_nav1
- = fgGetNode( "/autopilot/internal/nav1-track-error-deg", true );
-
diff = target_nav1->getDoubleValue() - true_hdg->getDoubleValue();
- if ( diff < -180.0 ) { diff += 360.0; }
- if ( diff > 180.0 ) { diff -= 360.0; }
+ SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
true_nav1->setDoubleValue( diff );
- diff = target_nav1->getDoubleValue() - true_track->getDoubleValue();
- if ( diff < -180.0 ) { diff += 360.0; }
- if ( diff > 180.0 ) { diff -= 360.0; }
+ // Calculate true groundtrack
+ diff = target_nav1->getDoubleValue() - track->getDoubleValue();
+ SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
true_track_nav1->setDoubleValue( diff );
// Calculate nav1 selected course error normalized to +/- 180.0
- // (based on DG indicated heading)
- static SGPropertyNode_ptr nav1_course_error
- = fgGetNode( "/autopilot/internal/nav1-course-error", true );
- static SGPropertyNode_ptr nav1_selected_course
- = fgGetNode( "/instrumentation/nav[0]/radials/selected-deg", true );
-
- diff = nav1_selected_course->getDoubleValue() - ind_hdg->getDoubleValue();
-// if ( diff < -180.0 ) { diff += 360.0; }
-// if ( diff > 180.0 ) { diff -= 360.0; }
+ diff = nav1_selected_course->getDoubleValue() - mag_hdg->getDoubleValue();
SG_NORMALIZE_RANGE( diff, -180.0, 180.0 );
nav1_course_error->setDoubleValue( diff );
// Calculate vertical speed in fpm
- static SGPropertyNode_ptr vs_fps
- = fgGetNode( "/velocities/vertical-speed-fps", true );
- static SGPropertyNode_ptr vs_fpm
- = fgGetNode( "/autopilot/internal/vert-speed-fpm", true );
-
vs_fpm->setDoubleValue( vs_fps->getDoubleValue() * 60.0 );
// Calculate static port pressure rate in [inhg/s].
// Used to determine vertical speed.
- static SGPropertyNode_ptr static_pressure
- = fgGetNode( "/systems/static[0]/pressure-inhg", true );
- static SGPropertyNode_ptr pressure_rate
- = fgGetNode( "/autopilot/internal/pressure-rate", true );
+ if ( dt > 0.0 ) {
+ double current_static_pressure = static_pressure->getDoubleValue();
+ double current_pressure_rate =
+ ( current_static_pressure - last_static_pressure ) / dt;
+
+ pressure_rate->setDoubleValue(current_pressure_rate);
+ last_static_pressure = current_static_pressure;
+ }
+#endif
+}
- static double last_static_pressure = 0.0;
+void FGXMLAutopilotGroup::reinit()
+{
+ for( vector<string>::size_type i = 0; i < _autopilotNames.size(); i++ ) {
+ FGXMLAutopilot * ap = (FGXMLAutopilot*)get_subsystem( _autopilotNames[i] );
+ if( ap == NULL ) continue; // ?
+ remove_subsystem( _autopilotNames[i] );
+ delete ap;
+ }
+ _autopilotNames.clear();
+ init();
+}
- if ( dt > 0.0 ) {
- double current_static_pressure = static_pressure->getDoubleValue();
+void FGXMLAutopilotGroup::init()
+{
+ vector<SGPropertyNode_ptr> autopilotNodes = fgGetNode( "/sim/systems", true )->getChildren("autopilot");
+ if( autopilotNodes.size() == 0 ) {
+ SG_LOG( SG_ALL, SG_WARN, "No autopilot configuration specified for this model!");
+ return;
+ }
- double current_pressure_rate =
- ( current_static_pressure - last_static_pressure ) / dt;
+ for( vector<SGPropertyNode_ptr>::size_type i = 0; i < autopilotNodes.size(); i++ ) {
+ SGPropertyNode_ptr pathNode = autopilotNodes[i]->getNode( "path" );
+ if( pathNode == NULL ) {
+ SG_LOG( SG_ALL, SG_WARN, "No autopilot configuration file specified for this autopilot!");
+ continue;
+ }
- pressure_rate->setDoubleValue(current_pressure_rate);
+ string apName;
+ SGPropertyNode_ptr nameNode = autopilotNodes[i]->getNode( "name" );
+ if( nameNode != NULL ) {
+ apName = nameNode->getStringValue();
+ } else {
+ std::ostringstream buf;
+ buf << "unnamed_autopilot_" << i;
+ apName = buf.str();
+ }
+
+ if( get_subsystem( apName.c_str() ) != NULL ) {
+ SG_LOG( SG_ALL, SG_ALERT, "Duplicate autopilot configuration name " << apName << " ignored" );
+ continue;
+ }
+
+ SGPath config( globals->get_fg_root() );
+ config.append( pathNode->getStringValue() );
+
+ SG_LOG( SG_ALL, SG_INFO, "Reading autopilot configuration from " << config.str() );
+ // FGXMLAutopilot
+ FGXMLAutopilot * ap = new FGXMLAutopilot;
+ try {
+ SGPropertyNode_ptr root = new SGPropertyNode();
+ readProperties( config.str(), root );
+
+
+ if ( ! ap->build( root ) ) {
+ SG_LOG( SG_ALL, SG_ALERT,
+ "Detected an internal inconsistency in the autopilot configuration." << endl << " See earlier errors for details." );
+ delete ap;
+ continue;
+ }
+ } catch (const sg_exception& e) {
+ SG_LOG( SG_AUTOPILOT, SG_ALERT, "Failed to load autopilot configuration: "
+ << config.str() << ":" << e.getMessage() );
+ delete ap;
+ continue;
+ }
- last_static_pressure = current_static_pressure;
+ SG_LOG( SG_AUTOPILOT, SG_INFO, "adding autopilot subsystem " << apName );
+ set_subsystem( apName, ap );
+ _autopilotNames.push_back( apName );
}
+ SGSubsystemGroup::init();
+}
+
+FGXMLAutopilot::FGXMLAutopilot() {
+}
+
+
+FGXMLAutopilot::~FGXMLAutopilot() {
+}
+
+
+/* read all /sim/systems/autopilot[n]/path properties, try to read the file specified therein
+ * and configure/add the digital filters specified in that file
+ */
+void FGXMLAutopilot::init()
+{
+}
+
+
+void FGXMLAutopilot::reinit() {
+ components.clear();
+ init();
+}
+
+
+void FGXMLAutopilot::bind() {
+}
+
+void FGXMLAutopilot::unbind() {
}
+bool FGXMLAutopilot::build( SGPropertyNode_ptr config_props ) {
+ SGPropertyNode *node;
+ int i;
+
+ int count = config_props->nChildren();
+ for ( i = 0; i < count; ++i ) {
+ node = config_props->getChild(i);
+ string name = node->getName();
+ // cout << name << endl;
+ SG_LOG( SG_AUTOPILOT, SG_BULK, "adding autopilot component " << name );
+ if ( name == "pid-controller" ) {
+ components.push_back( new FGPIDController( node ) );
+ } else if ( name == "pi-simple-controller" ) {
+ components.push_back( new FGPISimpleController( node ) );
+ } else if ( name == "predict-simple" ) {
+ components.push_back( new FGPredictor( node ) );
+ } else if ( name == "filter" ) {
+ components.push_back( new FGDigitalFilter( node ) );
+ } else if ( name == "logic" ) {
+ components.push_back( new FGXMLAutoLogic( node ) );
+ } else {
+ SG_LOG( SG_AUTOPILOT, SG_WARN, "Unknown top level autopilot section: " << name );
+// return false;
+ }
+ }
+
+ return true;
+}
/*
* Update the list of autopilot components
*/
-void FGXMLAutopilot::update( double dt ) {
- update_helper( dt );
-
+void FGXMLAutopilot::update( double dt )
+{
unsigned int i;
for ( i = 0; i < components.size(); ++i ) {
components[i]->update( dt );
#ifndef _XMLAUTO_HXX
#define _XMLAUTO_HXX 1
+/*
+Torsten Dreyer:
+I'd like to deprecate the so called autopilot helper function
+(which is now part of the AutopilotGroup::update() method).
+Every property calculated within this helper can be calculated
+using filters defined in an external autopilot definition file.
+The complete set of calculations may be extracted into a separate
+configuration file. The current implementation is able to hande
+multiple config files and autopilots. The helper doubles code
+and writes properties used only by a few aircraft.
+*/
+// FIXME: this should go into config.h and/or configure
+// or removed along with the "helper" one day.
+#define XMLAUTO_USEHELPER
+
#include <simgear/compiler.h>
#include <string>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/props/condition.hxx>
+typedef SGSharedPtr<class FGXMLAutoInput> FGXMLAutoInput_ptr;
+typedef SGSharedPtr<class FGPeriodicalValue> FGPeriodicalValue_ptr;
+
+class FGPeriodicalValue : public SGReferenced {
+private:
+ FGXMLAutoInput_ptr minPeriod; // The minimum value of the period
+ FGXMLAutoInput_ptr maxPeriod; // The maximum value of the period
+public:
+ FGPeriodicalValue( SGPropertyNode_ptr node );
+ double normalize( double value );
+};
class FGXMLAutoInput : public SGReferenced {
private:
double value; // The value as a constant or initializer for the property
bool abs; // return absolute value
SGPropertyNode_ptr property; // The name of the property containing the value
- SGSharedPtr<FGXMLAutoInput> offset; // A fixed offset, defaults to zero
- SGSharedPtr<FGXMLAutoInput> scale; // A constant scaling factor defaults to one
- SGSharedPtr<FGXMLAutoInput> min; // A minimum clip defaults to no clipping
- SGSharedPtr<FGXMLAutoInput> max; // A maximum clip defaults to no clipping
+ FGXMLAutoInput_ptr offset; // A fixed offset, defaults to zero
+ FGXMLAutoInput_ptr scale; // A constant scaling factor defaults to one
+ FGXMLAutoInput_ptr min; // A minimum clip defaults to no clipping
+ FGXMLAutoInput_ptr max; // A maximum clip defaults to no clipping
+ FGPeriodicalValue_ptr periodical; //
SGSharedPtr<const SGCondition> _condition;
public:
};
-class FGXMLAutoInputList : public std::vector<SGSharedPtr<FGXMLAutoInput> > {
+class FGXMLAutoInputList : public std::vector<FGXMLAutoInput_ptr> {
public:
- FGXMLAutoInput * get_active() {
+ FGXMLAutoInput_ptr get_active() {
for (iterator it = begin(); it != end(); ++it) {
if( (*it)->is_enabled() )
return *it;
}
double get_value( double def = 0.0 ) {
- FGXMLAutoInput * input = get_active();
+ FGXMLAutoInput_ptr input = get_active();
return input == NULL ? def : input->get_value();
}
FGXMLAutoInputList referenceInput;
FGXMLAutoInputList uminInput;
FGXMLAutoInputList umaxInput;
+ FGPeriodicalValue_ptr periodical;
// debug flag
bool debug;
bool enabled;
(*it)->setDoubleValue( clamp( value ) );
}
+ inline void set_output_value( bool value ) {
+ // passive_ignore == true means that we go through all the
+ // motions, but drive the outputs. This is analogous to
+ // running the autopilot with the "servos" off. This is
+ // helpful for things like flight directors which position
+ // their vbars from the autopilot computations.
+ if ( honor_passive && passive_mode->getBoolValue() ) return;
+ for( std::vector <SGPropertyNode_ptr>::iterator it = output_list.begin(); it != output_list.end(); ++it)
+ (*it)->setBoolValue( value ); // don't use clamp here, bool is clamped anyway
+ }
+
inline double get_output_value() {
return output_list.size() == 0 ? 0.0 : clamp(output_list[0]->getDoubleValue());
}
bool isPropertyEnabled();
};
+typedef SGSharedPtr<FGXMLAutoComponent> FGXMLAutoComponent_ptr;
+
/**
* Roy Ovesen's PID controller
std::deque <double> output;
std::deque <double> input;
enum filterTypes { exponential, doubleExponential, movingAverage,
- noiseSpike, gain, reciprocal, none };
+ noiseSpike, gain, reciprocal, differential, none };
filterTypes filterType;
protected:
void update(double dt);
};
+class FGXMLAutoLogic : public FGXMLAutoComponent
+{
+private:
+ SGSharedPtr<SGCondition> input;
+ bool inverted;
+
+protected:
+ bool parseNodeHook(const std::string& aName, SGPropertyNode* aNode);
+
+public:
+ FGXMLAutoLogic(SGPropertyNode * node );
+ ~FGXMLAutoLogic() {}
+
+ void update(double dt);
+};
+
/**
* Model an autopilot system.
*
*/
+class FGXMLAutopilotGroup : public SGSubsystemGroup
+{
+public:
+ FGXMLAutopilotGroup();
+ void init();
+ void reinit();
+ void update( double dt );
+private:
+ std::vector<std::string> _autopilotNames;
+
+#ifdef XMLAUTO_USEHELPER
+ double average;
+ double v_last;
+ double last_static_pressure;
+
+ SGPropertyNode_ptr vel;
+ SGPropertyNode_ptr lookahead5;
+ SGPropertyNode_ptr lookahead10;
+ SGPropertyNode_ptr bug;
+ SGPropertyNode_ptr mag_hdg;
+ SGPropertyNode_ptr bug_error;
+ SGPropertyNode_ptr fdm_bug_error;
+ SGPropertyNode_ptr target_true;
+ SGPropertyNode_ptr true_hdg;
+ SGPropertyNode_ptr true_error;
+ SGPropertyNode_ptr target_nav1;
+ SGPropertyNode_ptr true_nav1;
+ SGPropertyNode_ptr true_track_nav1;
+ SGPropertyNode_ptr nav1_course_error;
+ SGPropertyNode_ptr nav1_selected_course;
+ SGPropertyNode_ptr vs_fps;
+ SGPropertyNode_ptr vs_fpm;
+ SGPropertyNode_ptr static_pressure;
+ SGPropertyNode_ptr pressure_rate;
+ SGPropertyNode_ptr track;
+#endif
+};
+
class FGXMLAutopilot : public SGSubsystem
{
void unbind();
void update( double dt );
- bool build();
+ bool build( SGPropertyNode_ptr );
protected:
-
- typedef std::vector<SGSharedPtr<FGXMLAutoComponent> > comp_list;
+ typedef std::vector<FGXMLAutoComponent_ptr> comp_list;
private:
-
bool serviceable;
- SGPropertyNode_ptr config_props;
comp_list components;
+
};
}
- SG_LOG(SG_INPUT, SG_INFO, "Read properties for " <<
+ SG_LOG(SG_INPUT, SG_DEBUG, "Read properties for " <<
root.getStringValue("name"));
if (!root.getNode("depreciated"))
HUD_deque.erase( HUD_deque.begin(), HUD_deque.end());
- SG_LOG(SG_INPUT, SG_INFO, "Reading Hud instruments");
+ SG_LOG(SG_INPUT, SG_DEBUG, "Reading Hud instruments");
const SGPropertyNode * instrument_group = root.getChild("instruments");
int nInstruments = instrument_group->nChildren();
SGPath path( globals->get_fg_root() );
path.append(node->getStringValue("path"));
- SG_LOG(SG_INPUT, SG_INFO, "Reading Instrument "
+ SG_LOG(SG_INPUT, SG_DEBUG, "Reading Instrument "
<< node->getName()
<< " from "
<< path.str());
Maj_div(node->getIntValue("major_divs")), // FIXME dup
Min_div(node->getIntValue("minor_divs")) // FIXME dup
{
- SG_LOG(SG_INPUT, SG_INFO, "Done reading dial/tape instrument "
+ SG_LOG(SG_INPUT, SG_BULK, "Done reading dial/tape instrument "
<< node->getStringValue("name", "[unnamed]"));
set_data_source(get_func(node->getStringValue("loadfn")));
0, /* hud.cxx: static int dp_shoing = 0; */ // FIXME
node->getBoolValue("working", true))
{
- SG_LOG(SG_INPUT, SG_INFO, "Done reading gauge instrument "
+ SG_LOG(SG_INPUT, SG_BULK, "Done reading gauge instrument "
<< node->getStringValue("name", "[unnamed]"));
set_data_source(get_func(node->getStringValue("loadfn")));
lon_node(fgGetNode("/position/longitude-string", true)),
lat_node(fgGetNode("/position/latitude-string", true))
{
- SG_LOG(SG_INPUT, SG_INFO, "Done reading instr_label instrument "
+ SG_LOG(SG_INPUT, SG_BULK, "Done reading instr_label instrument "
<< node->getStringValue("name", "[unnamed]"));
set_data_source(get_func(node->getStringValue("data_source")));
if (fgGetBool("/sim/hud/enable3d", true) && HUD_style == 1)
factor = 640.0 / 55.0;
- SG_LOG(SG_INPUT, SG_INFO, "Done reading HudLadder instrument"
+ SG_LOG(SG_INPUT, SG_BULK, "Done reading HudLadder instrument"
<< node->getStringValue("name", "[unnamed]"));
if (!width_units)
drawIA(arrowScale > 0 ? true : false),
drawIAAlways(arrowScale > 0 ? node->getBoolValue("arrow_always") : false)
{
- SG_LOG(SG_INPUT, SG_INFO, "Done reading runway instrument "
+ SG_LOG(SG_INPUT, SG_BULK, "Done reading runway instrument "
<< node->getStringValue("name", "[unnamed]"));
view[0] = 0;
rad(node->getFloatValue("rad"))
{
- SG_LOG(SG_INPUT, SG_INFO, "Done reading TBI instrument"
+ SG_LOG(SG_INPUT, SG_BULK, "Done reading TBI instrument"
<< node->getStringValue("name", "[unnamed]"));
}
transform();
FGFontCache *fc = globals->get_fontcache();
- text_renderer.setFont(fc->getTexFont(_font_name.c_str()));
+ fntFont* font = fc->getTexFont(_font_name.c_str());
+ if (!font) {
+ return; // don't crash on missing fonts
+ }
+
+ text_renderer.setFont(font);
text_renderer.setPointSize(_pointSize);
text_renderer.begin();
FGTextLayer::setFontName(const string &name)
{
_font_name = name + ".txf";
+ FGFontCache *fc = globals->get_fontcache();
+ fntFont* font = fc->getTexFont(_font_name.c_str());
+ if (!font) {
+ SG_LOG(SG_GENERAL, SG_WARN, "unable to find font:" << name);
+ }
}
if (bgTexture.empty())
bgTexture = "FOO";
panel->setBackground(FGTextureManager::createTexture(bgTexture.c_str()));
- SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << bgTexture );
//
// Get multibackground if any...
string mbgTexture = root->getStringValue("multibackground[0]");
if (!mbgTexture.empty()) {
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 0);
- SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture );
mbgTexture = root->getStringValue("multibackground[1]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 1);
- SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture );
mbgTexture = root->getStringValue("multibackground[2]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 2);
- SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture );
mbgTexture = root->getStringValue("multibackground[3]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 3);
- SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture );
mbgTexture = root->getStringValue("multibackground[4]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 4);
- SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture );
mbgTexture = root->getStringValue("multibackground[5]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 5);
- SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture );
mbgTexture = root->getStringValue("multibackground[6]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 6);
- SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture );
mbgTexture = root->getStringValue("multibackground[7]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 7);
- SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture );
}
//
// Create each instrument.
//
- SG_LOG( SG_COCKPIT, SG_INFO, "Reading panel instruments" );
+ SG_LOG( SG_COCKPIT, SG_DEBUG, "Reading panel instruments" );
const SGPropertyNode * instrument_group = root->getChild("instruments");
if (instrument_group != 0) {
int nInstruments = instrument_group->nChildren();
SG_LOG( SG_COCKPIT, SG_WARN, "Unknown special instrument found" );
}
} else {
- SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName()
+ SG_LOG( SG_COCKPIT, SG_WARN, "Skipping " << node->getName()
<< " in instruments section" );
}
}
}
- SG_LOG( SG_COCKPIT, SG_INFO, "Done reading panel instruments" );
+ SG_LOG( SG_COCKPIT, SG_BULK, "Done reading panel instruments" );
//
metar_valid(false),
setup_winds_aloft(true),
wind_interpolation_required(true),
+ metar_sealevel_temperature(15.0),
+ metar_sealevel_dewpoint(5.0),
// Interpolation constant definitions.
- EnvironmentUpdatePeriodSec( 0.2 ),
MaxWindChangeKtsSec( 0.2 ),
MaxVisChangePercentSec( 0.05 ),
- MaxPressureChangeInHgSec( 0.0033 ),
+ MaxPressureChangeInHgSec( 0.0005 ), // approx 1hpa/min
+ MaxTemperatureChangeDegcSec(10.0/60.0), // approx 10degc/min
MaxCloudAltitudeChangeFtSec( 20.0 ),
MaxCloudThicknessChangeFtSec( 50.0 ),
MaxCloudInterpolationHeightFt( 5000.0 ),
latitude_n = fgGetNode( "/position/latitude-deg", true );
environment_clouds_n = fgGetNode("/environment/clouds");
- boundary_wind_speed_n = fgGetNode("/environment/config/boundary/entry/wind-speed-kt");
- boundary_wind_from_heading_n = fgGetNode("/environment/config/boundary/entry/wind-from-heading-deg");
- boundary_visibility_n = fgGetNode("/environment/config/boundary/entry/visibility-m");
- boundary_sea_level_pressure_n = fgGetNode("/environment/config/boundary/entry/pressure-sea-level-inhg");
+ boundary_wind_speed_n = fgGetNode("/environment/config/boundary/entry/wind-speed-kt", true );
+ boundary_wind_from_heading_n = fgGetNode("/environment/config/boundary/entry/wind-from-heading-deg", true );
+ boundary_visibility_n = fgGetNode("/environment/config/boundary/entry/visibility-m", true );
+ boundary_sea_level_pressure_n = fgGetNode("/environment/config/boundary/entry/pressure-sea-level-inhg", true );
+ boundary_sea_level_temperature_n = fgGetNode("/environment/config/boundary/entry/temperature-sea-level-degc", true );
+ boundary_sea_level_dewpoint_n = fgGetNode("/environment/config/boundary/entry/dewpoint-sea-level-degc", true );
}
FGMetarCtrl::~FGMetarCtrl ()
}
// use a "command" to set station temp at station elevation
-static void set_temp_at_altitude( float temp_degc, float altitude_ft ) {
+static void set_temp_at_altitude( double temp_degc, double altitude_ft ) {
SGPropertyNode args;
SGPropertyNode *node = args.getNode("temp-degc", 0, true);
- node->setFloatValue( temp_degc );
+ node->setDoubleValue( temp_degc );
node = args.getNode("altitude-ft", 0, true);
- node->setFloatValue( altitude_ft );
- globals->get_commands()->execute("set-outside-air-temp-degc", &args);
+ node->setDoubleValue( altitude_ft );
+ globals->get_commands()->execute( altitude_ft == 0.0 ?
+ "set-sea-level-air-temp-degc" :
+ "set-outside-air-temp-degc", &args);
}
-static void set_dewpoint_at_altitude( float dewpoint_degc, float altitude_ft ) {
+static void set_dewpoint_at_altitude( double dewpoint_degc, double altitude_ft ) {
SGPropertyNode args;
SGPropertyNode *node = args.getNode("dewpoint-degc", 0, true);
- node->setFloatValue( dewpoint_degc );
+ node->setDoubleValue( dewpoint_degc );
node = args.getNode("altitude-ft", 0, true);
- node->setFloatValue( altitude_ft );
- globals->get_commands()->execute("set-dewpoint-temp-degc", &args);
+ node->setDoubleValue( altitude_ft );
+ globals->get_commands()->execute( altitude_ft == 0.0 ?
+ "set-dewpoint-sea-level-air-temp-degc" :
+ "set-dewpoint-temp-degc", &args);
}
/*
setupWindBranch( "aloft", dir, speed, gust );
}
-double FGMetarCtrl::interpolate_val(double currentval, double requiredval, double dt)
+double FGMetarCtrl::interpolate_val(double currentval, double requiredval, double dval )
{
- double dval = EnvironmentUpdatePeriodSec * dt;
-
if (fabs(currentval - requiredval) < dval) return requiredval;
if (currentval < requiredval) return (currentval + dval);
if (currentval > requiredval) return (currentval - dval);
static double reducePressureSl(double metarPressure, double fieldHt,
double fieldTemp)
{
- double elev = fieldHt * SG_FEET_TO_METER;
- double fieldPressure
- = FGAtmo::fieldPressure(elev, metarPressure * atmodel::inHg);
- double slPressure = P_layer(0, elev, fieldPressure,
- fieldTemp + atmodel::freezing,
- atmodel::ISA::lam0);
- return slPressure / atmodel::inHg;
+ double elev = fieldHt * SG_FEET_TO_METER;
+ double fieldPressure
+ = FGAtmo::fieldPressure(elev, metarPressure * atmodel::inHg);
+ double slPressure = P_layer(0, elev, fieldPressure,
+ fieldTemp + atmodel::freezing, atmodel::ISA::lam0);
+ return slPressure / atmodel::inHg;
}
void
bool reinit_required = false;
bool layer_rebuild_required = false;
- double station_elevation_ft = station_elevation_n->getDoubleValue();
+ double station_elevation_ft = station_elevation_n->getDoubleValue();
if (first_update) {
double dir = base_wind_dir_n->getDoubleValue()+magnetic_variation_n->getDoubleValue();
double metarvis = min_visibility_n->getDoubleValue();
fgDefaultWeatherValue("visibility-m", metarvis);
+ set_temp_at_altitude(temperature_n->getDoubleValue(), station_elevation_ft);
+ set_dewpoint_at_altitude(dewpoint_n->getDoubleValue(), station_elevation_ft);
+
double metarpressure = pressure_n->getDoubleValue();
fgDefaultWeatherValue("pressure-sea-level-inhg",
- reducePressureSl(metarpressure,
- station_elevation_ft,
- temperature_n->getDoubleValue()));
+ reducePressureSl(metarpressure,
+ station_elevation_ft,
+ temperature_n->getDoubleValue()));
// We haven't already loaded a METAR, so apply it immediately.
vector<SGPropertyNode_ptr> layers = clouds_n->getChildren("layer");
double maxdy = dy * MaxWindChangeKtsSec;
// Interpolate each component separately.
- current[0] = interpolate_val(current[0], metar[0], maxdx);
- current[1] = interpolate_val(current[1], metar[1], maxdy);
+ current[0] = interpolate_val(current[0], metar[0], maxdx*dt);
+ current[1] = interpolate_val(current[1], metar[1], maxdy*dt);
// Now convert back to polar coordinates.
if ((fabs(current[0]) > 0.1) || (fabs(current[1]) > 0.1)) {
double currentxval = log(1000.0 + vis);
double metarxval = log(1000.0 + metarvis);
- currentxval = interpolate_val(currentxval, metarxval, MaxVisChangePercentSec);
+ currentxval = interpolate_val(currentxval, metarxval, MaxVisChangePercentSec*dt);
// Now convert back from an X-value to a straightforward visibility.
vis = exp(currentxval) - 1000.0;
double pressure = boundary_sea_level_pressure_n->getDoubleValue();
double metarpressure = pressure_n->getDoubleValue();
- double newpressure = reducePressureSl(metarpressure,
- station_elevation_ft,
- temperature_n->getDoubleValue());
+ double newpressure = reducePressureSl(metarpressure,
+ station_elevation_ft,
+ temperature_n->getDoubleValue());
if( pressure != newpressure ) {
- pressure = interpolate_val( pressure, newpressure, MaxPressureChangeInHgSec );
+ pressure = interpolate_val( pressure, newpressure, MaxPressureChangeInHgSec*dt );
fgDefaultWeatherValue("pressure-sea-level-inhg", pressure);
reinit_required = true;
}
+ {
+ double temperature = boundary_sea_level_temperature_n->getDoubleValue();
+ double dewpoint = boundary_sea_level_dewpoint_n->getDoubleValue();
+ if( metar_sealevel_temperature != temperature ) {
+ temperature = interpolate_val( temperature, metar_sealevel_temperature, MaxTemperatureChangeDegcSec*dt );
+ set_temp_at_altitude( temperature, 0.0 );
+ }
+ if( metar_sealevel_dewpoint != dewpoint ) {
+ dewpoint = interpolate_val( dewpoint, metar_sealevel_dewpoint, MaxTemperatureChangeDegcSec*dt );
+ set_dewpoint_at_altitude( dewpoint, 0.0 );
+ }
+ }
+
// Set the cloud layers by interpolating over the METAR versions.
vector<SGPropertyNode_ptr> layers = clouds_n->getChildren("layer");
vector<SGPropertyNode_ptr>::const_iterator layer;
} else {
// Interpolate the other values in the usual way
if (current_alt != required_alt) {
- current_alt = interpolate_val(current_alt, required_alt, MaxCloudAltitudeChangeFtSec);
+ current_alt = interpolate_val(current_alt, required_alt, MaxCloudAltitudeChangeFtSec*dt);
target->setDoubleValue("elevation-ft", current_alt);
}
if (current_thickness != required_thickness) {
current_thickness = interpolate_val(current_thickness,
required_thickness,
- MaxCloudThicknessChangeFtSec);
+ MaxCloudThicknessChangeFtSec*dt);
thickness->setDoubleValue(current_thickness);
}
}
}
}
- set_temp_at_altitude(temperature_n->getDoubleValue(), station_elevation_ft);
- set_dewpoint_at_altitude(dewpoint_n->getDoubleValue(), station_elevation_ft);
- //TODO: check if temperature/dewpoint have changed. This requires reinit.
// Force an update of the 3D clouds
if( layer_rebuild_required )
station_elevation_n->setDoubleValue( station_elevation_ft );
+ { // calculate sea level temperature and dewpoint
+ FGEnvironment dummy; // instantiate a dummy so we can leech a method
+ dummy.set_elevation_ft( station_elevation_ft );
+ dummy.set_temperature_degc( temperature_n->getDoubleValue() );
+ dummy.set_dewpoint_degc( dewpoint_n->getDoubleValue() );
+ metar_sealevel_temperature = dummy.get_temperature_sea_level_degc();
+ metar_sealevel_dewpoint = dummy.get_dewpoint_sea_level_degc();
+ }
+
vector<SGMetarCloud> cv = m->getClouds();
vector<SGMetarCloud>::const_iterator cloud, cloud_end = cv.end();
bool first_update;
bool wind_interpolation_required;
string metar;
+ double metar_sealevel_temperature;
+ double metar_sealevel_dewpoint;
double interpolate_prop(const char * currentname, const char * requiredname, double dvalue);
double interpolate_val(double currentval, double requiredval, double dvalue);
- const double EnvironmentUpdatePeriodSec; // Seconds between interpolations
const double MaxWindChangeKtsSec; // Max wind change in kts/sec
const double MaxVisChangePercentSec; // Max visibility change in %/sec
const double MaxPressureChangeInHgSec; // Max pressure change in InHg/sec
+ const double MaxTemperatureChangeDegcSec; // Max temperature change in degc/s
const double MaxCloudAltitudeChangeFtSec; // Max cloud altitude change in ft/s
const double MaxCloudThicknessChangeFtSec; // Max cloud thickness change in ft/s
const double MaxCloudInterpolationHeightFt; // Max distance from aircraft to
SGPropertyNode_ptr boundary_wind_from_heading_n;
SGPropertyNode_ptr boundary_visibility_n;
SGPropertyNode_ptr boundary_sea_level_pressure_n;
+ SGPropertyNode_ptr boundary_sea_level_temperature_n;
+ SGPropertyNode_ptr boundary_sea_level_dewpoint_n;
private:
};
_set_V_calibrated_kts( kts );
_set_V_ground_speed( kts );
- SGGeod pos = SGGeod::fromDegM(get_Longitude(), get_Latitude(), get_Altitude());
+ SGGeod pos = getPosition();
// update (lon/lat) position
SGGeod pos2;
double az2;
geo_direct_wgs_84 ( pos, heading * SGD_RADIANS_TO_DEGREES,
dist, pos2, &az2 );
- _set_Longitude( pos2.getLongitudeRad() );
- _set_Latitude( pos2.getLatitudeRad() );
+ _set_Geodetic_Position( pos2.getLatitudeRad(), pos2.getLongitudeRad(), pos.getElevationFt() );
double sl_radius, lat_geoc;
sgGeodToGeoc( get_Latitude(), get_Altitude(), &sl_radius, &lat_geoc );
get_Psi() * SGD_RADIANS_TO_DEGREES,
dist, &lat2, &lon2, &az2 );
- _set_Longitude( lon2 * SGD_DEGREES_TO_RADIANS );
- _set_Latitude( lat2 * SGD_DEGREES_TO_RADIANS );
+ _set_Geodetic_Position( lat2 * SGD_DEGREES_TO_RADIANS, lon2 * SGD_DEGREES_TO_RADIANS );
}
// cout << "lon error = " << fabs(end.x()*SGD_RADIANS_TO_DEGREES - lon2)
get_Psi() * SGD_RADIANS_TO_DEGREES,
dist, &lat2, &lon2, &az2 );
- _set_Longitude( lon2 * SGD_DEGREES_TO_RADIANS );
- _set_Latitude( lat2 * SGD_DEGREES_TO_RADIANS );
+ _set_Geodetic_Position( lat2 * SGD_DEGREES_TO_RADIANS, lon2 * SGD_DEGREES_TO_RADIANS );
}
// cout << "lon error = " << fabs(end.x()*SGD_RADIANS_TO_DEGREES - lon2)
runway_altitude=0;
climb_rate=0;
altitude_agl=0;
+ track=0;
}
void
&FGInterface::get_Psi_deg,
&FGInterface::set_Psi_deg);
fgSetArchivable("/orientation/heading-deg");
+ fgTie("/orientation/track-deg", this,
+ &FGInterface::get_Track);
// Body-axis "euler rates" (rotation speed, but in a funny
// representation).
fgUntie("/orientation/roll-deg");
fgUntie("/orientation/pitch-deg");
fgUntie("/orientation/heading-deg");
+ fgUntie("/orientation/track-deg");
fgUntie("/orientation/roll-rate-degps");
fgUntie("/orientation/pitch-rate-degps");
fgUntie("/orientation/yaw-rate-degps");
void FGInterface::_updatePositionM(const SGVec3d& cartPos)
{
+ TrackComputer tracker( track, geodetic_position_v );
cartesian_position_v = cartPos;
geodetic_position_v = SGGeod::fromCart(cartesian_position_v);
geocentric_position_v = SGGeoc::fromCart(cartesian_position_v);
void FGInterface::_updatePosition(const SGGeod& geod)
{
+ TrackComputer tracker( track, geodetic_position_v );
geodetic_position_v = geod;
cartesian_position_v = SGVec3d::fromGeod(geodetic_position_v);
geocentric_position_v = SGGeoc::fromCart(cartesian_position_v);
void FGInterface::_updatePosition(const SGGeoc& geoc)
{
+ TrackComputer tracker( track, geodetic_position_v );
geocentric_position_v = geoc;
cartesian_position_v = SGVec3d::fromGeoc(geocentric_position_v);
geodetic_position_v = SGGeod::fromCart(cartesian_position_v);
using std::vector;
using std::string;
+/**
+ * A little helper class to update the track if
+ * the position has changed. In the constructor,
+ * create a copy of the current position and store
+ * references to the position object and the track
+ * variable to update.
+ * The destructor, called at TrackComputer's end of
+ * life/visibility, computes the track if the
+ * position has changed.
+ */
+class TrackComputer {
+public:
+ inline TrackComputer( double & track, const SGGeod & position ) :
+ _track( track ),
+ _position( position ),
+ _prevPosition( position ) {
+ }
+
+ inline ~TrackComputer() {
+ if( _prevPosition == _position ) return;
+ _track = SGGeodesy::courseDeg( _prevPosition, _position );
+ }
+private:
+ double & _track;
+ const SGGeod & _position;
+ const SGGeod _prevPosition;
+};
+
// This is based heavily on LaRCsim/ls_generic.h
class FGInterface : public SGSubsystem {
double runway_altitude;
double climb_rate; // in feet per second
double altitude_agl;
+ double track;
double daux[16]; // auxilliary doubles
float faux[16]; // auxilliary floats
geocentric_position_v.setLongitudeRad(lon);
geocentric_position_v.setRadiusFt(rad);
}
+/* Don't call _set_L[at|ong]itude() directly, use _set_Geodetic_Position() instead.
+ These methods can't update the track.
+ *
inline void _set_Latitude(double lat) {
geodetic_position_v.setLatitudeRad(lat);
}
inline void _set_Longitude(double lon) {
geodetic_position_v.setLongitudeRad(lon);
}
+*/
inline void _set_Altitude(double altitude) {
geodetic_position_v.setElevationFt(altitude);
}
inline void _set_Altitude_AGL(double agl) {
altitude_agl = agl;
}
+ inline void _set_Geodetic_Position( double lat, double lon ) {
+ _set_Geodetic_Position( lat, lon, geodetic_position_v.getElevationFt());
+ }
inline void _set_Geodetic_Position( double lat, double lon, double alt ) {
+ TrackComputer tracker( track, geodetic_position_v );
geodetic_position_v.setLatitudeRad(lat);
geodetic_position_v.setLongitudeRad(lon);
geodetic_position_v.setElevationFt(alt);
return geodetic_position_v.getElevationFt();
}
inline double get_Altitude_AGL(void) const { return altitude_agl; }
+ inline double get_Track(void) const { return track; }
inline double get_Latitude_deg () const {
return geodetic_position_v.getLatitudeDeg();
libGUI_a_SOURCES = \
new_gui.cxx new_gui.hxx \
dialog.cxx dialog.hxx \
- menubar.cxx menubar.hxx \
- gui.cxx gui.h gui_funcs.cxx \
- fonts.cxx \
- AirportList.cxx AirportList.hxx \
+ menubar.cxx menubar.hxx \
+ gui.cxx gui.h gui_funcs.cxx \
+ fonts.cxx \
+ AirportList.cxx AirportList.hxx \
property_list.cxx property_list.hxx \
layout.cxx layout-props.cxx layout.hxx \
- SafeTexFont.cxx SafeTexFont.hxx
-
+ SafeTexFont.cxx SafeTexFont.hxx \
+ WaypointList.cxx WaypointList.hxx
+
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
layout_test_SOURCES = layout-test.cxx
--- /dev/null
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "WaypointList.hxx"
+
+#include <algorithm>
+#include <plib/puAux.h>
+
+#include <simgear/route/waypoint.hxx>
+#include <simgear/structure/callback.hxx>
+#include <simgear/sg_inlines.h>
+
+#include <Main/globals.hxx>
+#include <Main/fg_props.hxx>
+
+#include <Autopilot/route_mgr.hxx>
+
+enum {
+ SCROLL_NO = 0,
+ SCROLL_UP,
+ SCROLL_DOWN
+};
+
+static const int DRAG_START_DISTANCE_PX = 5;
+
+class RouteManagerWaypointModel :
+ public WaypointList::Model,
+ public SGPropertyChangeListener
+{
+public:
+ RouteManagerWaypointModel()
+ {
+ _rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
+
+ SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true);
+ routeEdited->addChangeListener(this);
+ }
+
+ virtual ~RouteManagerWaypointModel()
+ {
+ SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true);
+ routeEdited->removeChangeListener(this);
+ }
+
+// implement WaypointList::Model
+ virtual unsigned int numWaypoints() const
+ {
+ return _rm->size();
+ }
+
+ virtual int currentWaypoint() const
+ {
+ return _rm->currentWaypoint();
+ }
+
+ virtual SGWayPoint waypointAt(unsigned int index) const
+ {
+ return _rm->get_waypoint(index);
+ }
+
+ virtual void deleteAt(unsigned int index)
+ {
+ _rm->pop_waypoint(index);
+ }
+
+ virtual void setWaypointTargetAltitudeFt(unsigned int index, int altFt)
+ {
+ _rm->setWaypointTargetAltitudeFt(index, altFt);
+ }
+
+ virtual void moveWaypointToIndex(unsigned int srcIndex, unsigned int destIndex)
+ {
+ if (destIndex > srcIndex) {
+ --destIndex;
+ }
+
+ SGWayPoint wp = _rm->pop_waypoint(srcIndex);
+ _rm->add_waypoint(wp, destIndex);
+ }
+
+ virtual void setUpdateCallback(SGCallback* cb)
+ {
+ _cb = cb;
+ }
+
+// implement SGPropertyChangeListener
+ void valueChanged(SGPropertyNode *prop)
+ {
+ if (_cb) {
+ (*_cb)();
+ }
+ }
+private:
+ FGRouteMgr* _rm;
+ SGCallback* _cb;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void drawClippedString(puFont& font, const char* s, int x, int y, int maxWidth)
+{
+ int fullWidth = font.getStringWidth(s);
+ if (fullWidth <= maxWidth) { // common case, easy and efficent
+ font.drawString(s, x, y);
+ return;
+ }
+
+ std::string buf(s);
+ int len = buf.size();
+ do {
+ buf.resize(--len);
+ fullWidth = font.getStringWidth(buf.c_str());
+ } while (fullWidth > maxWidth);
+
+ font.drawString(buf.c_str(), x, y);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+WaypointList::WaypointList(int x, int y, int width, int height) :
+ puFrame(x, y, width, height),
+ GUI_ID(FGCLASS_WAYPOINTLIST),
+ _scrollPx(0),
+ _dragging(false),
+ _dragScroll(SCROLL_NO),
+ _showLatLon(false),
+ _model(NULL),
+ _updateCallback(NULL),
+ _scrollCallback(NULL)
+{
+ // pretend to be a list, so fgPopup doesn't mess with our mouse events
+ type |= PUCLASS_LIST;
+ setModel(new RouteManagerWaypointModel());
+ setSize(width, height);
+ setValue(-1);
+}
+
+WaypointList::~WaypointList()
+{
+ delete _model;
+ delete _updateCallback;
+ delete _scrollCallback;
+}
+
+void WaypointList::setUpdateCallback(SGCallback* cb)
+{
+ _updateCallback = cb;
+}
+
+void WaypointList::setScrollCallback(SGCallback* cb)
+{
+ _scrollCallback = cb;
+}
+
+void WaypointList::setSize(int width, int height)
+{
+ double scrollP = getVScrollPercent();
+ _heightPx = height;
+ puFrame::setSize(width, height);
+
+ if (wantsVScroll()) {
+ setVScrollPercent(scrollP);
+ } else {
+ _scrollPx = 0;
+ }
+}
+
+int WaypointList::checkHit ( int button, int updown, int x, int y )
+{
+ if ( isHit( x, y ) || ( puActiveWidget () == this ) )
+ {
+ doHit ( button, updown, x, y ) ;
+ return TRUE ;
+ }
+
+ return FALSE ;
+}
+
+
+void WaypointList::doHit( int button, int updown, int x, int y )
+{
+ puFrame::doHit(button, updown, x, y);
+ if (updown == PU_DRAG) {
+ handleDrag(x, y);
+ return;
+ }
+
+ if (button != active_mouse_button) {
+ return;
+ }
+
+ if (updown == PU_UP) {
+ puDeactivateWidget();
+ if (_dragging) {
+ doDrop(x, y);
+ return;
+ }
+ } else if (updown == PU_DOWN) {
+ puSetActiveWidget(this, x, y);
+ _mouseDownX = x;
+ _mouseDownY = y;
+ return;
+ }
+
+// update selection
+ int row = rowForY(y - abox.min[1]);
+ if (row >= (int) _model->numWaypoints()) {
+ row = -1; // 'no selection'
+ }
+
+ if (row == getSelected()) {
+ _showLatLon = !_showLatLon;
+ puPostRefresh();
+ return;
+ }
+
+ setSelected(row);
+}
+
+void WaypointList::handleDrag(int x, int y)
+{
+ if (!_dragging) {
+ // don't start drags immediately, require a certain mouse movement first
+ int manhattanLength = abs(x - _mouseDownX) + abs(y - _mouseDownY);
+ if (manhattanLength < DRAG_START_DISTANCE_PX) {
+ return;
+ }
+
+ _dragSourceRow = rowForY(y - abox.min[1]);
+ _dragging = true;
+ _dragScroll = SCROLL_NO;
+ }
+
+ if (y < abox.min[1]) {
+ if (_dragScroll != SCROLL_DOWN) {
+ _dragScroll = SCROLL_DOWN;
+ _dragScrollTime.stamp();
+ }
+ } else if (y > abox.max[1]) {
+ if (_dragScroll != SCROLL_UP) {
+ _dragScroll = SCROLL_UP;
+ _dragScrollTime.stamp();
+ }
+ } else {
+ _dragScroll = SCROLL_NO;
+ _dragTargetRow = rowForY(y - abox.min[1] - (rowHeightPx() / 2));
+ }
+}
+
+void WaypointList::doDrop(int x, int y)
+{
+ _dragging = false;
+ puDeactivateWidget();
+
+ if ((y < abox.min[1]) || (y >= abox.max[1])) {
+ return;
+ }
+
+ if (_dragSourceRow != _dragTargetRow) {
+ _model->moveWaypointToIndex(_dragSourceRow, _dragTargetRow);
+
+ // keep row indexes linged up when moving an item down the list
+ if (_dragSourceRow < _dragTargetRow) {
+ --_dragTargetRow;
+ }
+
+ setSelected(_dragTargetRow);
+ }
+}
+
+void WaypointList::invokeDownCallback(void)
+{
+ _dragging = false;
+ _dragScroll = SCROLL_NO;
+ SG_LOG(SG_GENERAL, SG_INFO, "cancel drag");
+}
+
+int WaypointList::rowForY(int y) const
+{
+ if (!_model) {
+ return -1;
+ }
+
+ // flip y to increase down, not up (as rows do)
+ int flipY = _heightPx - y;
+ int row = (flipY + _scrollPx) / rowHeightPx();
+ return row;
+}
+
+void WaypointList::draw( int dx, int dy )
+{
+ puFrame::draw(dx, dy);
+
+ if (!_model) {
+ return;
+ }
+
+ if (_dragScroll != SCROLL_NO) {
+ doDragScroll();
+ }
+
+ glEnable(GL_SCISSOR_TEST);
+ GLint sx = (int) abox.min[0],
+ sy = abox.min[1];
+ GLsizei w = (GLsizei) abox.max[0] - abox.min[0],
+ h = _heightPx;
+
+ sx += border_thickness;
+ sy += border_thickness;
+ w -= 2 * border_thickness;
+ h -= 2 * border_thickness;
+
+ glScissor(sx + dx, sy + dy, w, h);
+ int row = firstVisibleRow(),
+ final = lastVisibleRow(),
+ rowHeight = rowHeightPx(),
+ y = rowHeight;
+
+ y -= (_scrollPx % rowHeight); // partially draw the first row
+
+ for ( ; row <= final; ++row, y += rowHeight) {
+ drawRow(dx, dy, row, y);
+ } // of row drawing iteration
+
+ glDisable(GL_SCISSOR_TEST);
+
+ if (_dragging) {
+ // draw the insert marker after the rows
+ int insertY = (_dragTargetRow * rowHeight) - _scrollPx;
+ SG_CLAMP_RANGE(insertY, 0, std::min(_heightPx, totalHeightPx()));
+
+ glColor4f(1.0f, 0.5f, 0.0f, 0.8f);
+ glLineWidth(3.0f);
+ glBegin(GL_LINES);
+ glVertex2f(dx + abox.min[0], dy + abox.max[1] - insertY);
+ glVertex2f(dx + abox.max[0], dy + abox.max[1] - insertY);
+ glEnd();
+ }
+}
+
+void WaypointList::drawRow(int dx, int dy, int rowIndex, int y)
+{
+ bool isSelected = (rowIndex == getSelected());
+ bool isCurrent = (rowIndex == _model->currentWaypoint());
+ bool isDragSource = (_dragging && (rowIndex == _dragSourceRow));
+
+ puBox bkgBox = abox;
+ bkgBox.min[1] = abox.max[1] - y;
+ bkgBox.max[1] = bkgBox.min[1] + rowHeightPx();
+
+ puColour currentColor;
+ puSetColor(currentColor, 1.0, 1.0, 0.0, 0.5);
+
+ if (isDragSource) {
+ // draw later, on *top* of text string
+ } else if (isCurrent) {
+ bkgBox.draw(dx, dy, PUSTYLE_PLAIN, ¤tColor, false, 0);
+ } else if (isSelected) { // -PLAIN means selected, apparently
+ bkgBox.draw(dx, dy, -PUSTYLE_PLAIN, colour, false, 0);
+ }
+
+ int xx = dx + abox.min[0] + PUSTR_LGAP;
+ int yy = dy + abox.max[1] - y ;
+ yy += 4; // center text in row height
+
+ // row textual data
+ const SGWayPoint wp(_model->waypointAt(rowIndex));
+ char buffer[128];
+ int count = ::snprintf(buffer, 128, "%03d %-5s", rowIndex, wp.get_id().c_str());
+
+ if (wp.get_name().size() > 0 && (wp.get_name() != wp.get_id())) {
+ // append name if present, and different to id
+ ::snprintf(buffer + count, 128 - count, " (%s)", wp.get_name().c_str());
+ }
+
+ glColor4fv ( colour [ PUCOL_LEGEND ] ) ;
+ drawClippedString(legendFont, buffer, xx, yy, 300);
+
+ if (_showLatLon) {
+ char ns = (wp.get_target_lat() > 0.0) ? 'N' : 'S';
+ char ew = (wp.get_target_lon() > 0.0) ? 'E' : 'W';
+
+ ::snprintf(buffer, 128 - count, "%4.2f%c %4.2f%c",
+ fabs(wp.get_target_lon()), ew, fabs(wp.get_target_lat()), ns);
+ } else {
+ ::snprintf(buffer, 128 - count, "%03.0f %5.1fnm",
+ wp.get_track(), wp.get_distance() * SG_METER_TO_NM);
+ }
+
+ legendFont.drawString(buffer, xx + 300 + PUSTR_LGAP, yy);
+
+ int altFt = (int) wp.get_target_alt() * SG_METER_TO_FEET;
+ if (altFt > -9990) {
+ int altHundredFt = (altFt + 50) / 100; // round to nearest 100ft
+ if (altHundredFt < 100) {
+ count = ::snprintf(buffer, 128, "%d'", altHundredFt * 100);
+ } else { // display as a flight-level
+ count = ::snprintf(buffer, 128, "FL%d", altHundredFt);
+ }
+
+ legendFont.drawString(buffer, xx + 400 + PUSTR_LGAP, yy);
+ } // of valid wp altitude
+
+ if (isDragSource) {
+ puSetColor(currentColor, 1.0, 0.5, 0.0, 0.5);
+ bkgBox.draw(dx, dy, PUSTYLE_PLAIN, ¤tColor, false, 0);
+ }
+}
+
+const double SCROLL_PX_SEC = 200.0;
+
+void WaypointList::doDragScroll()
+{
+ double dt = (SGTimeStamp::now() - _dragScrollTime).toSecs();
+ _dragScrollTime.stamp();
+ int deltaPx = (int)(dt * SCROLL_PX_SEC);
+
+ if (_dragScroll == SCROLL_UP) {
+ _scrollPx = _scrollPx - deltaPx;
+ SG_CLAMP_RANGE(_scrollPx, 0, scrollRangePx());
+ _dragTargetRow = firstVisibleRow();
+ } else {
+ _scrollPx = _scrollPx + deltaPx;
+ SG_CLAMP_RANGE(_scrollPx, 0, scrollRangePx());
+ _dragTargetRow = lastFullyVisibleRow() + 1;
+ }
+
+ if (_scrollCallback) {
+ (*_scrollCallback)();
+ }
+}
+
+int WaypointList::getSelected()
+{
+ return getIntegerValue();
+}
+
+void WaypointList::setSelected(int rowIndex)
+{
+ if (rowIndex == getSelected()) {
+ return;
+ }
+
+ setValue(rowIndex);
+ invokeCallback();
+ if (rowIndex == -1) {
+ return;
+ }
+
+ ensureRowVisible(rowIndex);
+}
+
+void WaypointList::ensureRowVisible(int rowIndex)
+{
+ if ((rowIndex >= firstFullyVisibleRow()) && (rowIndex <= lastFullyVisibleRow())) {
+ return; // already visible, fine
+ }
+
+ // ideal position would place the desired row in the middle of the
+ // visible section - hence subtract half the visible height.
+ int targetScrollPx = (rowIndex * rowHeightPx()) - (_heightPx / 2);
+
+ // clamp the scroll value to something valid
+ SG_CLAMP_RANGE(targetScrollPx, 0, scrollRangePx());
+ _scrollPx = targetScrollPx;
+
+ puPostRefresh();
+ if (_scrollCallback) { // keep scroll observers in sync
+ (*_scrollCallback)();
+ }
+}
+
+unsigned int WaypointList::numWaypoints() const
+{
+ if (!_model) {
+ return 0;
+ }
+
+ return _model->numWaypoints();
+}
+
+bool WaypointList::wantsVScroll() const
+{
+ return totalHeightPx() > _heightPx;
+}
+
+float WaypointList::getVScrollPercent() const
+{
+ float scrollRange = scrollRangePx();
+ if (scrollRange < 1.0f) {
+ return 0.0;
+ }
+
+ return _scrollPx / scrollRange;
+}
+
+float WaypointList::getVScrollThumbPercent() const
+{
+ return _heightPx / (float) totalHeightPx();
+}
+
+void WaypointList::setVScrollPercent(float perc)
+{
+ float scrollRange = scrollRangePx();
+ _scrollPx = (int)(scrollRange * perc);
+}
+
+int WaypointList::firstVisibleRow() const
+{
+ return _scrollPx / rowHeightPx();
+}
+
+int WaypointList::firstFullyVisibleRow() const
+{
+ int rh = rowHeightPx();
+ return (_scrollPx + rh - 1) / rh;
+}
+
+int WaypointList::numVisibleRows() const
+{
+ int rh = rowHeightPx();
+ int topOffset = _scrollPx % rh; // pixels of first visible row
+ return (_heightPx - topOffset + rh - 1) / rh;
+
+}
+
+int WaypointList::numFullyVisibleRows() const
+{
+ int rh = rowHeightPx();
+ int topOffset = _scrollPx % rh; // pixels of first visible row
+ return (_heightPx - topOffset) / rh;
+}
+
+int WaypointList::rowHeightPx() const
+{
+ return legendFont.getStringHeight() + PUSTR_BGAP;
+}
+
+int WaypointList::scrollRangePx() const
+{
+ return std::max(0, totalHeightPx() - _heightPx);
+}
+
+int WaypointList::totalHeightPx() const
+{
+ if (!_model) {
+ return 0;
+ }
+
+ return (int) _model->numWaypoints() * rowHeightPx();
+}
+
+int WaypointList::lastFullyVisibleRow() const
+{
+ int row = firstFullyVisibleRow() + numFullyVisibleRows();
+ return std::min(row, (int) _model->numWaypoints() - 1);
+}
+
+int WaypointList::lastVisibleRow() const
+{
+ int row = firstVisibleRow() + numVisibleRows();
+ return std::min(row, (int) _model->numWaypoints() - 1);
+}
+
+void WaypointList::setModel(Model* model)
+{
+ if (_model) {
+ delete _model;
+ }
+
+ _model = model;
+ _model->setUpdateCallback(make_callback(this, &WaypointList::modelUpdateCallback));
+
+ puPostRefresh();
+}
+
+int WaypointList::checkKey (int key, int updown )
+{
+ if ((updown == PU_UP) || !isVisible () || !isActive () || (window != puGetWindow())) {
+ return FALSE ;
+ }
+
+ switch (key)
+ {
+ case PU_KEY_HOME:
+ setSelected(0);
+ break;
+
+ case PU_KEY_END:
+ setSelected(_model->numWaypoints() - 1);
+ break ;
+
+ case PU_KEY_UP :
+ case PU_KEY_PAGE_UP :
+ if (getSelected() >= 0) {
+ setSelected(getSelected() - 1);
+ }
+ break ;
+
+ case PU_KEY_DOWN :
+ case PU_KEY_PAGE_DOWN : {
+ int newSel = getSelected() + 1;
+ if (newSel >= (int) _model->numWaypoints()) {
+ setSelected(-1);
+ } else {
+ setSelected(newSel);
+ }
+ break ;
+ }
+
+ case '-':
+ if (getSelected() >= 0) {
+ int newAlt = wayptAltFtHundreds(getSelected()) - 10;
+ if (newAlt < 0) {
+ _model->setWaypointTargetAltitudeFt(getSelected(), -9999);
+ } else {
+ _model->setWaypointTargetAltitudeFt(getSelected(), newAlt * 100);
+ }
+ }
+ break;
+
+ case '=':
+ if (getSelected() >= 0) {
+ int newAlt = wayptAltFtHundreds(getSelected()) + 10;
+ if (newAlt < 0) {
+ _model->setWaypointTargetAltitudeFt(getSelected(), 0);
+ } else {
+ _model->setWaypointTargetAltitudeFt(getSelected(), newAlt * 100);
+ }
+ }
+ break;
+
+ case 0x7f: // delete
+ if (getSelected() >= 0) {
+ int index = getSelected();
+ _model->deleteAt(index);
+ setSelected(index - 1);
+ }
+ break;
+
+ default :
+ return FALSE;
+ }
+
+ return TRUE ;
+}
+
+int WaypointList::wayptAltFtHundreds(int index) const
+{
+ double alt = _model->waypointAt(index).get_target_alt();
+ if (alt < -9990.0) {
+ return -9999;
+ }
+
+ int altFt = (int) alt * SG_METER_TO_FEET;
+ return (altFt + 50) / 100; // round to nearest 100ft
+}
+
+void WaypointList::modelUpdateCallback()
+{
+ // local stuff
+
+ if (_updateCallback) {
+ (*_updateCallback)();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+static void handle_scrollbar(puObject* scrollbar)
+{
+ ScrolledWaypointList* self = (ScrolledWaypointList*)scrollbar->getUserData();
+ self->setScrollPercent(scrollbar->getFloatValue());
+}
+
+static void waypointListCb(puObject* wpl)
+{
+ ScrolledWaypointList* self = (ScrolledWaypointList*)wpl->getUserData();
+ self->setValue(wpl->getIntegerValue());
+ self->invokeCallback();
+}
+
+ScrolledWaypointList::ScrolledWaypointList(int x, int y, int width, int height) :
+ puGroup(x,y),
+ _scrollWidth(16)
+{
+ // ensure our type is compound, so fgPopup::applySize doesn't descend into
+ // us, and try to cast our children's user-data to GUIInfo*.
+ type |= PUCLASS_LIST;
+
+ init(width, height);
+}
+
+void ScrolledWaypointList::setValue(float v)
+{
+ puGroup::setValue(v);
+ _list->setValue(v);
+}
+
+void ScrolledWaypointList::setValue(int v)
+{
+ puGroup::setValue(v);
+ _list->setValue(v);
+}
+
+void ScrolledWaypointList::init(int w, int h)
+{
+ _list = new WaypointList(0, 0, w, h);
+ _list->setUpdateCallback(make_callback(this, &ScrolledWaypointList::modelUpdated));
+ _hasVScroll = _list->wantsVScroll();
+ _list->setUserData(this);
+ _list->setCallback(waypointListCb);
+
+ _list->setScrollCallback(make_callback(this, &ScrolledWaypointList::updateScroll));
+
+ _scrollbar = new puaScrollBar(w - _scrollWidth, 0, h,
+ 1 /*arrow*/, 1 /* vertical */, _scrollWidth);
+ _scrollbar->setMinValue(0.0);
+ _scrollbar->setMaxValue(1.0);
+ _scrollbar->setUserData(this);
+ _scrollbar->setCallback(handle_scrollbar);
+ close(); // close the group
+
+ setSize(w, h);
+}
+
+void ScrolledWaypointList::modelUpdated()
+{
+ int w, h;
+ getSize(&w, &h);
+ updateWantsScroll(w, h);
+}
+
+void ScrolledWaypointList::setScrollPercent(float v)
+{
+ // slider's min is the bottom, so invert the value
+ _list->setVScrollPercent(1.0f - v);
+}
+
+void ScrolledWaypointList::setSize(int w, int h)
+{
+ updateWantsScroll(w, h);
+}
+
+void ScrolledWaypointList::updateWantsScroll(int w, int h)
+{
+ _hasVScroll = _list->wantsVScroll();
+
+ if (_hasVScroll) {
+ _scrollbar->reveal();
+ _scrollbar->setPosition(w - _scrollWidth, 0);
+ _scrollbar->setSize(_scrollWidth, h);
+ _list->setSize(w - _scrollWidth, h);
+ updateScroll();
+ } else {
+ _scrollbar->hide();
+ _list->setSize(w, h);
+ }
+}
+
+void ScrolledWaypointList::updateScroll()
+{
+ // _scrollbar->setMaxValue(_list->numWaypoints());
+ _scrollbar->setValue(1.0f - _list->getVScrollPercent());
+ _scrollbar->setSliderFraction(_list->getVScrollThumbPercent());
+}
+
--- /dev/null
+/**
+ * WaypointList.hxx - scrolling list of waypoints, with special formatting
+ */
+
+#ifndef GUI_WAYPOINT_LIST_HXX
+#define GUI_WAYPOINT_LIST_HXX
+
+#include <simgear/compiler.h>
+#include <simgear/timing/timestamp.hxx>
+
+#include <plib/pu.h>
+
+#include "dialog.hxx" // for GUI_ID
+
+// forward decls
+class puaScrollBar;
+class SGWayPoint;
+class SGCallback;
+
+class WaypointList : public puFrame, public GUI_ID
+{
+public:
+ WaypointList(int x, int y, int width, int height);
+ virtual ~WaypointList();
+
+ virtual void setSize(int width, int height);
+ virtual int checkHit ( int button, int updown, int x, int y);
+ virtual void doHit( int button, int updown, int x, int y ) ;
+ virtual void draw( int dx, int dy ) ;
+ virtual int checkKey(int key, int updown);
+ virtual void invokeDownCallback (void);
+
+ void setSelected(int rowIndex);
+ int getSelected();
+
+ /**
+ * Do we want a vertical scrollbar (or similar)
+ */
+ bool wantsVScroll() const;
+
+ /**
+ * Get scrollbar position as a percentage of total range.
+ * returns negative number if scrolling is not possible
+ */
+ float getVScrollPercent() const;
+
+ /**
+ *
+ */
+ void setVScrollPercent(float perc);
+
+ /**
+ * Get required thumb size as percentage of total height
+ */
+ float getVScrollThumbPercent() const;
+
+ int numVisibleRows() const;
+
+ void ensureRowVisible(int row);
+
+ void setUpdateCallback(SGCallback* cb);
+ void setScrollCallback(SGCallback* cb);
+
+ /**
+ * Abstract interface for waypoint source
+ */
+ class Model
+ {
+ public:
+ virtual ~Model() { }
+
+ virtual unsigned int numWaypoints() const = 0;
+ virtual int currentWaypoint() const = 0;
+ virtual SGWayPoint waypointAt(unsigned int index) const = 0;
+
+ // update notifications
+ virtual void setUpdateCallback(SGCallback* cb) = 0;
+
+ // editing operations
+ virtual void deleteAt(unsigned int index) = 0;
+ virtual void setWaypointTargetAltitudeFt(unsigned int index, int altFt) = 0;
+ virtual void moveWaypointToIndex(unsigned int srcIndex, unsigned int dstIndex) = 0;
+ };
+
+ void setModel(Model* model);
+
+ unsigned int numWaypoints() const;
+protected:
+
+private:
+ void drawRow(int dx, int dy, int rowIndex, int yOrigin);
+
+ void handleDrag(int x, int y);
+ void doDrop(int x, int y);
+ void doDragScroll();
+
+ /**
+ * Pixel height of a row, including padding
+ */
+ int rowHeightPx() const;
+
+ /**
+ * model row corresponding to an on-screen y-value
+ */
+ int rowForY(int y) const;
+
+ /**
+ * reutrn rowheight * total number of rows, i.e the height we'd
+ * need to be to show every row without scrolling
+ */
+ int totalHeightPx() const;
+
+ /**
+ * Pixel scroll range, based on widget height and number of rows
+ */
+ int scrollRangePx() const;
+
+ int firstVisibleRow() const;
+ int lastVisibleRow() const;
+
+ int numFullyVisibleRows() const;
+ int firstFullyVisibleRow() const;
+ int lastFullyVisibleRow() const;
+
+ int wayptAltFtHundreds(int index) const;
+
+ void modelUpdateCallback();
+
+ int _scrollPx; // scroll ammount (in pixels)
+ int _heightPx;
+
+ bool _dragging;
+ int _dragSourceRow;
+ int _dragTargetRow;
+ int _mouseDownX, _mouseDownY;
+
+ int _dragScroll;
+ SGTimeStamp _dragScrollTime;
+
+ bool _showLatLon;
+ Model* _model;
+ SGCallback* _updateCallback;
+ SGCallback* _scrollCallback;
+};
+
+class ScrolledWaypointList : public puGroup
+{
+public:
+ ScrolledWaypointList(int x, int y, int width, int height);
+
+ virtual void setSize(int width, int height);
+
+ void setScrollPercent(float v);
+
+ virtual void setValue(float v);
+ virtual void setValue(int v);
+private:
+ void init(int w, int h);
+
+ void updateScroll();
+ void updateWantsScroll(int w, int h);
+
+ void modelUpdated();
+
+ puaScrollBar* _scrollbar;
+ WaypointList* _list;
+ int _scrollWidth;
+ bool _hasVScroll;
+};
+
+#endif // GUI_WAYPOINT_LIST_HXX
#include "AirportList.hxx"
#include "property_list.hxx"
#include "layout.hxx"
-
+#include "WaypointList.hxx"
enum format_type { f_INVALID, f_INT, f_LONG, f_FLOAT, f_DOUBLE, f_STRING };
static const int FORMAT_BUFSIZE = 255;
getFirstChild()->setSize(w, h); // dialog background puFrame
}
} else {
- setPosition(x + _dlgX - _startX, y + _dlgY - _startY);
- }
+ int posX = x + _dlgX - _startX,
+ posY = y + _dlgY - _startY;
+ setPosition(posX, posY);
+
+ GUIInfo *info = (GUIInfo *)getUserData();
+ if (info && info->node) {
+ info->node->setIntValue("x", posX);
+ info->node->setIntValue("y", posY);
+ }
+ } // re-positioning
} else if (_dragging) {
fgSetMouseCursor(_start_cursor);
puObject *widget = _propertyObjects[i]->object;
int widgetType = widget->getType();
- if ((widgetType & PUCLASS_LIST) && (dynamic_cast<GUI_ID *>(widget)->id & FGCLASS_LIST)) {
- fgList *pl = static_cast<fgList*>(widget);
- pl->update();
+ if (widgetType & PUCLASS_LIST) {
+ GUI_ID* gui_id = dynamic_cast<GUI_ID *>(widget);
+ if (gui_id && (gui_id->id & FGCLASS_LIST)) {
+ fgList *pl = static_cast<fgList*>(widget);
+ pl->update();
+ } else {
+ copy_to_pui(_propertyObjects[i]->node, widget);
+ }
} else if (widgetType & PUCLASS_COMBOBOX) {
fgComboBox* combo = static_cast<fgComboBox*>(widget);
combo->update();
setupObject(obj, props);
setColor(obj, props, EDITFIELD);
return obj;
+ } else if (type == "waypointlist") {
+ ScrolledWaypointList* obj = new ScrolledWaypointList(x, y, width, height);
+ setupObject(obj, props);
+ return obj;
} else {
return 0;
}
#define FGCLASS_LIST 0x00000001
#define FGCLASS_AIRPORTLIST 0x00000002
#define FGCLASS_PROPERTYLIST 0x00000004
+#define FGCLASS_WAYPOINTLIST 0x00000008
+
class GUI_ID { public: GUI_ID(int id) : id(id) {} virtual ~GUI_ID() {} int id; };
// Delete all the character arrays
// we were forced to keep around for
// plib.
- SG_LOG(SG_GENERAL, SG_INFO, "Deleting char arrays");
+ SG_LOG(SG_GENERAL, SG_BULK, "Deleting char arrays");
for (i = 0; i < _char_arrays.size(); i++) {
for (int j = 0; _char_arrays[i][j] != 0; j++)
free(_char_arrays[i][j]); // added with strdup
// Delete all the callback arrays
// we were forced to keep around for
// plib.
- SG_LOG(SG_GENERAL, SG_INFO, "Deleting callback arrays");
+ SG_LOG(SG_GENERAL, SG_BULK, "Deleting callback arrays");
for (i = 0; i < _callback_arrays.size(); i++)
delete[] _callback_arrays[i];
// Delete all those bindings
- SG_LOG(SG_GENERAL, SG_INFO, "Deleting bindings");
+ SG_LOG(SG_GENERAL, SG_BULK, "Deleting bindings");
map<string,vector<SGBinding *> >::iterator it;
for (it = _bindings.begin(); it != _bindings.end(); it++) {
- SG_LOG(SG_GENERAL, SG_INFO, "Deleting bindings for " << it->first);
+ SG_LOG(SG_GENERAL, SG_BULK, "Deleting bindings for " << it->first);
for ( i = 0; i < it->second.size(); i++ )
delete it->second[i];
}
- SG_LOG(SG_GENERAL, SG_INFO, "Done.");
+ SG_LOG(SG_GENERAL, SG_BULK, "Done.");
}
void
bool
FGMenuBar::enable_item(const SGPropertyNode * node, bool state)
{
- const char *path = node->getPath();
+ string path = node->getPath();
if (_objects.find(path) == _objects.end()) {
SG_LOG(SG_GENERAL, SG_ALERT, "Trying to enable/disable "
"non-existent menu item for node `" << path << '\'');
#include <simgear/structure/exception.hxx>
#include <simgear/props/props_io.hxx>
+#include <boost/algorithm/string/case_conv.hpp>
+
#include <Main/fg_props.hxx>
#include "menubar.hxx"
struct FGFontCache::fnt *
FGFontCache::getfnt(const char *name, float size, float slant)
{
- string fontName(name);
+ string fontName = boost::to_lower_copy(string(name));
FntParams fntParams(fontName, size, slant);
PuFontMap::iterator i = _puFonts.find(fntParams);
- if (i != _puFonts.end())
+ if (i != _puFonts.end()) {
+ // found in the puFonts map, all done
return i->second;
+ }
+
// fntTexFont s are all preloaded into the _texFonts map
TexFontMap::iterator texi = _texFonts.find(fontName);
- fntTexFont* texfont = 0;
- puFont* pufont = 0;
+ fntTexFont* texfont = NULL;
+ puFont* pufont = NULL;
if (texi != _texFonts.end()) {
texfont = texi->second;
} else {
+ // check the built-in PUI fonts (in guifonts array)
const GuiFont* guifont = std::find_if(&guifonts[0], guifontsEnd,
GuiFont::Predicate(name));
if (guifont != guifontsEnd) {
pufont = guifont->font;
}
}
+
fnt* f = new fnt;
if (pufont) {
f->pufont = pufont;
void FGFontCache::init()
{
- if (!_initialized) {
- char *envp = ::getenv("FG_FONTS");
- if (envp != NULL) {
- _path.set(envp);
- } else {
- _path.set(globals->get_fg_root());
- _path.append("Fonts");
- }
- _initialized = true;
+ if (_initialized) {
+ return;
}
+
+ char *envp = ::getenv("FG_FONTS");
+ if (envp != NULL) {
+ _path.set(envp);
+ } else {
+ _path.set(globals->get_fg_root());
+ _path.append("Fonts");
+ }
+ _initialized = true;
}
SGPath
path = SGPath(_path);
path.append("Helvetica.txf");
-
+ SG_LOG(SG_GENERAL, SG_WARN, "Unknown font name '" << name << "', defaulting to Helvetica");
return path;
}
path.append(dirEntry->d_name);
if (path.extension() == fontext) {
fntTexFont* f = new fntTexFont;
- if (f->load((char *)path.c_str()))
- _texFonts[string(dirEntry->d_name)] = f;
- else
+ if (f->load((char *)path.c_str())) {
+ // convert font names in the map to lowercase for matching
+ string fontName = boost::to_lower_copy(string(dirEntry->d_name));
+ _texFonts[fontName] = f;
+ } else
delete f;
}
}
#ifndef _PROPERTY_LIST_HXX
#define _PROPERTY_LIST_HXX
+#include <string>
#include <plib/puAux.h>
#include <simgear/props/props.hxx>
void toggleVerbosity() { _verbose = !_verbose; }
// overridden plib pui methods
- virtual char *getStringValue(void) { return (char *)(_return ? _return->getPath(true) : ""); }
+ virtual char *getStringValue(void)
+ {
+ _return_path.clear();
+ if (_return)
+ _return_path = _return->getPath(true);
+ return const_cast<char*>(_return_path.c_str());
+ }
//virtual char *getListStringValue() { return (char *)(_return ? _return->getPath(true) : ""); }
virtual void setValue(const char *);
bool _dot_files; // . and .. pseudo-dirs currently shown?
bool _verbose; // show SGPropertyNode flags
+ std::string _return_path;
};
#define NOMINMAX 1
+#define HAVE_CULLSETTINGS_CLEAR_MASK 1
+
#ifndef ENABLE_AUDIO_SUPPORT
#define ENABLE_AUDIO_SUPPORT
#endif
void draw_stipple_line(float x1, float y1, float x2, float y2);
void draw_text(float x, float y, const char *msg, int align = 0, int digit = 0);
void draw_circle(float x1, float y1, float r) const;
+ void draw_arc(float x1, float y1, float t0, float t1, float r) const;
void draw_bullet(float, float, float);
HUD *_hud;
private:
SGSharedPtr<SGCondition> _active_condition; // stadiametric (true) or standby (false)
+ SGSharedPtr<SGCondition> _tachy_condition; // tachymetric (true) or standby (false)
+ SGSharedPtr<SGCondition> _align_condition; // tachymetric (true) or standby (false)
+
Input _diameter; // inner/outer radius relation
+ Input _pitch;
+ Input _yaw;
+ Input _speed;
+ Input _range;
+ Input _t0;
+ Input _t1;
+ Input _offset_x;
+ Input _offset_y;
+
float _bullet_size;
float _inner_radius;
+ float _compression;
+ float _limit_x;
+ float _limit_y;
+
};
glEnd();
}
+void HUD::Item::draw_arc(float xoffs, float yoffs, float t0, float t1, float r) const
+{
+ glBegin(GL_LINE_STRIP);
+ float step = SG_PI / r;
+ t0 = t0 * SG_DEGREES_TO_RADIANS;
+ t1 = t1 * SG_DEGREES_TO_RADIANS;
+
+ for (float alpha = t0; alpha < t1; alpha += step) {
+ float x = r * cos(alpha);
+ float y = r * sin(alpha);
+ glVertex2f(x + xoffs, y + yoffs);
+ }
+ glEnd();
+}
void HUD::Item::draw_bullet(float x, float y, float size)
{
#include "HUD.hxx"
-
// MIL-STD-1787B aiming reticle
HUD::AimingReticle::AimingReticle(HUD *hud, const SGPropertyNode *n, float x, float y) :
- Item(hud, n, x, y),
- _active_condition(0),
- _diameter(n->getNode("diameter-input", false)),
- _bullet_size(_w / 6.0),
- _inner_radius(_w / 2.0)
+Item(hud, n, x, y),
+_active_condition(0),
+_tachy_condition(0),
+_align_condition(0),
+_diameter(n->getNode("diameter-input", false)),
+_pitch(n->getNode("pitch-input", false)),
+_yaw(n->getNode("yaw-input", false)),
+_speed(n->getNode("speed-input", false)),
+_range(n->getNode("range-input", false)),
+_t0(n->getNode("arc-start-input", false)),
+_t1(n->getNode("arc-stop-input", false)),
+_offset_x(n->getNode("offset-x-input", false)),
+_offset_y(n->getNode("offset-y-input", false)),
+_bullet_size(_w / 6.0),
+_inner_radius(_w / 2.0),
+_compression(n->getFloatValue("compression-factor")),
+_limit_x(n->getFloatValue("limit-x")),
+_limit_y(n->getFloatValue("limit-y"))
+
{
const SGPropertyNode *node = n->getNode("active-condition");
if (node)
- _active_condition = sgReadCondition(globals->get_props(), node);
+ _active_condition = sgReadCondition(globals->get_props(), node);
+
+ const SGPropertyNode *tnode = n->getNode("tachy-condition");
+ if (tnode)
+ _tachy_condition = sgReadCondition(globals->get_props(), tnode);
+
+ const SGPropertyNode *anode = n->getNode("align-condition");
+ if (anode)
+ _align_condition = sgReadCondition(globals->get_props(), anode);
+
}
void HUD::AimingReticle::draw(void)
{
bool active = _active_condition ? _active_condition->test() : true;
- float diameter = _diameter.isValid() ? _diameter.getFloatValue() : 2.0f; // outer circle
+ bool tachy = _tachy_condition ? _tachy_condition->test() : false;
+ bool align = _align_condition ? _align_condition->test() : false;
- float x = _center_x;
- float y = _center_y;
+ float diameter = _diameter.isValid() ? _diameter.getFloatValue() : 2.0f; // outer circle
+ float x = _center_x + (_offset_x.isValid() ? _offset_x.getFloatValue() : 0);
+ float y = _center_y + (_offset_y.isValid() ? _offset_y.getFloatValue() : 0);
if (active) { // stadiametric (4.2.4.4)
draw_bullet(x, y, _bullet_size);
draw_circle(x, y, _inner_radius);
draw_circle(x, y, diameter * _inner_radius);
+ } else if (tachy){//tachiametric
+ float t0 = _t0.isValid() ? _t0.getFloatValue() : 2.0f; // start arc
+ float t1 = _t1.isValid() ? _t1.getFloatValue() : 2.0f; // stop arc
+ float yaw_value = _yaw.getFloatValue();
+ float pitch_value = _pitch.getFloatValue();
+ float tof_value = _range.getFloatValue()* 3 / _speed.getFloatValue();
+ draw_bullet(x, y, _bullet_size);
+ draw_circle(x, y, _inner_radius);
+ draw_line(x + _inner_radius, y, x + _inner_radius * 3, y);
+ draw_line(x - _inner_radius, y, x - _inner_radius * 3, y);
+ draw_line(x, y + _inner_radius, x, y + _inner_radius * 3);
+ draw_line(x, y - _inner_radius, x, y - _inner_radius * 3);
+
+ if(align){
+ draw_line(x + _limit_x, y + _limit_y, x - _limit_x, y + _limit_y);
+ draw_line(x + _limit_x, y - _limit_y, x - _limit_x, y - _limit_y);
+ draw_line(x + _limit_x, y + _limit_y, x + _limit_x, y - _limit_y);
+ draw_line(x - _limit_x, y + _limit_y, x - _limit_x, y - _limit_y);
+ }
+
+ float limit_offset = diameter * _inner_radius;
+
+ float pos_x = x + (yaw_value * tof_value)
+ * _compression;
+
+ pos_x > x + _limit_x - limit_offset ?
+ pos_x = x + _limit_x - limit_offset : pos_x;
+
+ pos_x < x - _limit_x + limit_offset ?
+ pos_x = x - _limit_x + limit_offset: pos_x;
+
+ float pos_y = y + (pitch_value * tof_value)
+ * _compression;
+
+ pos_y > y + _limit_y - limit_offset ?
+ pos_y = y + _limit_y - limit_offset : pos_y;
+
+ pos_y < y - _limit_y + limit_offset?
+ pos_y = y - _limit_y + limit_offset: pos_y;
+
+ draw_circle(pos_x, pos_y, diameter * _inner_radius);
+
+ draw_arc(x, y, t0, t1, (diameter + 2) * _inner_radius );
} else { // standby (4.2.4.5)
// TODO
}
+
}
#include "kln89_symbols.hxx"
#include <iostream>
+#if ENABLE_ATCDCL
#include <ATCDCL/ATCProjection.hxx>
+#else
+#include <ATC/atcutils.hxx>
+#endif
+
#include <Main/fg_props.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/structure/commands.hxx>
double mapScaleMeters = _mapScale * (_mapScaleUnits == 0 ? SG_NM_TO_METER : 1000);
// TODO - use an aligned projection when either DTK or TK up!
+#if ENABLE_ATCDCL
FGATCAlignedProjection mapProj(SGGeod::fromRad(_gpsLon, _gpsLat), _mapHeading);
-
+#else
+ FGKln89AlignedProjection mapProj(SGGeod::fromRad(_gpsLon, _gpsLat), _mapHeading);
+#endif
double meter_per_pix = (_mapOrientation == 0 ? mapScaleMeters / 20.0f : mapScaleMeters / 29.0f);
-
SGGeod bottomLeft = mapProj.ConvertFromLocal(SGVec3d(gps_max(-57.0 * meter_per_pix, -50000), gps_max((_mapOrientation == 0 ? -20.0 * meter_per_pix : -11.0 * meter_per_pix), -25000), 0.0));
SGGeod topRight = mapProj.ConvertFromLocal(SGVec3d(gps_min(54.0 * meter_per_pix, 50000), gps_min((_mapOrientation == 0 ? 20.0 * meter_per_pix : 29.0 * meter_per_pix), 25000), 0.0));
+
+
+
// Draw Airport labels first (but not one's that are waypoints)
// Draw Airports first (but not one's that are waypoints)
#include "airspeed_indicator.hxx"
#include <Main/fg_props.hxx>
#include <Main/util.hxx>
+#include <Environment/environment_mgr.hxx>
+#include <Environment/environment.hxx>
// A higher number means more responsive.
_name(node->getStringValue("name", "airspeed-indicator")),
_num(node->getIntValue("number", 0)),
_total_pressure(node->getStringValue("total-pressure", "/systems/pitot/total-pressure-inhg")),
- _static_pressure(node->getStringValue("static-pressure", "/systems/static/pressure-inhg"))
+ _static_pressure(node->getStringValue("static-pressure", "/systems/static/pressure-inhg")),
+ _has_overspeed(node->getBoolValue("has-overspeed-indicator",false)),
+ _pressure_alt_source(node->getStringValue("pressure-alt-source", "/instrumentation/altimeter/pressure-alt-ft")),
+ _ias_limit(node->getDoubleValue("ias-limit", 248.0)),
+ _mach_limit(node->getDoubleValue("mach-limit", 0.48)),
+ _alt_threshold(node->getDoubleValue("alt-threshold", 13200))
{
+ _environmentManager = NULL;
}
AirspeedIndicator::~AirspeedIndicator ()
_static_pressure_node = fgGetNode(_static_pressure.c_str(), true);
_density_node = fgGetNode("/environment/density-slugft3", true);
_speed_node = node->getChild("indicated-speed-kt", 0, true);
+ _tas_node = node->getChild("true-speed-kt", 0, true);
+ _mach_node = node->getChild("indicated-mach", 0, true);
+
+ // overspeed-indicator properties
+ if (_has_overspeed) {
+ _ias_limit_node = node->getNode("ias-limit",0, true);
+ _mach_limit_node = node->getNode("mach-limit",0, true);
+ _alt_threshold_node = node->getNode("alt-threshold",0, true);
+
+ if (!_ias_limit_node->hasValue()) {
+ _ias_limit_node->setDoubleValue(_ias_limit);
+ }
+
+ if (!_mach_limit_node->hasValue()) {
+ _mach_limit_node->setDoubleValue(_mach_limit);
+ }
+
+ if (!_alt_threshold_node->hasValue()) {
+ _alt_threshold_node->setDoubleValue(_alt_threshold);
+ }
+
+ _airspeed_limit = node->getChild("airspeed-limit-kt", 0, true);
+ _pressure_alt = fgGetNode(_pressure_alt_source.c_str(), true);
+ }
+
+ _environmentManager = (FGEnvironmentMgr*) globals->get_subsystem("environment");
}
#ifndef FPSTOKTS
void
AirspeedIndicator::update (double dt)
{
- if (_serviceable_node->getBoolValue()) {
- double pt = _total_pressure_node->getDoubleValue() * INHGTOPSF;
- double p = _static_pressure_node->getDoubleValue() * INHGTOPSF;
- double r = _density_node->getDoubleValue();
- double q = ( pt - p ); // dynamic pressure
-
- // Now, reverse the equation (normalize dynamic pressure to
- // avoid "nan" results from sqrt)
- if ( q < 0 ) { q = 0.0; }
- double v_fps = sqrt((2 * q) / r);
-
- // Publish the indicated airspeed
- double last_speed_kt = _speed_node->getDoubleValue();
- double current_speed_kt = v_fps * FPSTOKTS;
- _speed_node->setDoubleValue(fgGetLowPass(last_speed_kt,
- current_speed_kt,
- dt * RESPONSIVENESS));
+ if (!_serviceable_node->getBoolValue()) {
+ return;
+ }
+
+ double pt = _total_pressure_node->getDoubleValue() * INHGTOPSF;
+ double p = _static_pressure_node->getDoubleValue() * INHGTOPSF;
+ double r = _density_node->getDoubleValue();
+ double q = ( pt - p ); // dynamic pressure
+
+ // Now, reverse the equation (normalize dynamic pressure to
+ // avoid "nan" results from sqrt)
+ if ( q < 0 ) { q = 0.0; }
+ double v_fps = sqrt((2 * q) / r);
+
+ // Publish the indicated airspeed
+ double last_speed_kt = _speed_node->getDoubleValue();
+ double current_speed_kt = v_fps * FPSTOKTS;
+ double filtered_speed = fgGetLowPass(last_speed_kt,
+ current_speed_kt,
+ dt * RESPONSIVENESS);
+ _speed_node->setDoubleValue(filtered_speed);
+ computeMach(filtered_speed);
+
+ if (!_has_overspeed) {
+ return;
+ }
+
+ double lmt = _ias_limit_node->getDoubleValue();
+ if (_pressure_alt->getDoubleValue() > _alt_threshold_node->getDoubleValue()) {
+ double mmo = _mach_limit_node->getDoubleValue();
+ lmt = (filtered_speed/_mach_node->getDoubleValue())* mmo;
}
+
+ _airspeed_limit->setDoubleValue(lmt);
+}
+
+void
+AirspeedIndicator::computeMach(double ias)
+{
+ if (!_environmentManager) {
+ return;
+ }
+
+ FGEnvironment env(_environmentManager->getEnvironment());
+
+ // derived from http://williams.best.vwh.net/avform.htm#Mach
+ // names here are picked to be consistent with those formulae!
+
+ double oatK = env.get_temperature_degc() + 273.15; // OAT in Kelvin
+ double CS = 38.967854 * sqrt(oatK); // speed-of-sound in knots at altitude
+ double CS_0 = 661.4786; // speed-of-sound in knots at sea-level
+ double P_0 = env.get_pressure_sea_level_inhg();
+ double P = _static_pressure_node->getDoubleValue();
+
+ double DP = P_0 * (pow(1 + 0.2*pow(ias/CS_0, 2), 3.5) - 1);
+ double mach = pow(5 * ( pow(DP/P + 1, 2.0/7.0) -1) , 0.5);
+
+ // publish Mach and TAS
+ _mach_node->setDoubleValue(mach);
+ _tas_node->setDoubleValue(CS * mach);
}
// end of airspeed_indicator.cxx
#include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
+// forward decls
+class FGEnvironmentMgr;
/**
* Model an airspeed indicator tied to the pitot and static ports.
virtual void update (double dt);
private:
+ void computeMach(double ias);
string _name;
unsigned int _num;
string _total_pressure;
string _static_pressure;
+ bool _has_overspeed;
+ string _pressure_alt_source;
+ double _ias_limit;
+ double _mach_limit;
+ double _alt_threshold;
+
+ SGPropertyNode_ptr _ias_limit_node;
+ SGPropertyNode_ptr _mach_limit_node;
+ SGPropertyNode_ptr _alt_threshold_node;
SGPropertyNode_ptr _serviceable_node;
SGPropertyNode_ptr _total_pressure_node;
SGPropertyNode_ptr _static_pressure_node;
SGPropertyNode_ptr _density_node;
SGPropertyNode_ptr _speed_node;
+ SGPropertyNode_ptr _airspeed_limit;
+ SGPropertyNode_ptr _pressure_alt;
+ SGPropertyNode_ptr _mach_node;
+ SGPropertyNode_ptr _tas_node;
+ FGEnvironmentMgr* _environmentManager;
};
#endif // __INSTRUMENTS_AIRSPEED_INDICATOR_HXX
_time_before_search_sec = 0;
_last_frequency_mhz = frequency_mhz;
}
+ _frequency_node->setDoubleValue(frequency_mhz);
// Get the aircraft position
double longitude_rad =
gs_deflection_deg_node->setDoubleValue(0.0);
gs_deflection_norm_node->setDoubleValue(0.0);
gs_inrange_node->setBoolValue( false );
+ loc_node->setBoolValue( false );
+ has_gs_node->setBoolValue(false);
to_flag_node->setBoolValue( false );
from_flag_node->setBoolValue( false );
_gs = NULL;
_dme = NULL;
nav_id_node->setStringValue("");
-
+ loc_node->setBoolValue(false);
+ has_gs_node->setBoolValue(false);
+
_sgr->remove( nav_fx_name );
_sgr->remove( dme_fx_name );
}
double FGNavRadio::localizerWidth(FGNavRecord* aLOC)
{
FGRunway* rwy = aLOC->runway();
- assert(rwy);
+ if (!rwy) {
+ return 6.0; // no runway associated, return default value
+ }
+
SGVec3d thresholdCart(SGVec3d::fromGeod(rwy->threshold()));
double axisLength = dist(aLOC->cart(), thresholdCart);
camera->setProjectionMatrix(projectionMatrix);
camera->setCullMask(camera->getCullMask()
| simgear::BACKGROUND_BIT);
- camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
farCamera->setNodeMask(0);
} else {
Matrix nearProj, farProj;
SP_FDM_LIBS =
endif
+if ENABLE_ATCDCL
+ATCDCL_LIBS = $(top_builddir)/src/ATCDCL/libATCDCL.a
+else
+ATCDCL_LIBS =
+endif
+
+
if WITH_EVENTINPUT
EVENT_LIBS = $(eventinput_LIBS)
else
fgfs_LDADD = \
libMain.a \
$(top_builddir)/src/Aircraft/libAircraft.a \
- $(top_builddir)/src/ATCDCL/libATCDCL.a \
+ $(ATCDCL_LIBS) \
$(top_builddir)/src/Cockpit/libCockpit.a \
$(top_builddir)/src/Cockpit/built_in/libBuilt_in.a \
$(top_builddir)/src/FDM/libFlight.a \
* boundary and aloft environment layers.
*/
static bool
-do_set_sea_level_degc (const SGPropertyNode * arg)
+do_set_sea_level_degc ( double temp_sea_level_degc)
{
- double temp_sea_level_degc = arg->getDoubleValue("temp-degc", 15.0);
-
SGPropertyNode *node, *child;
// boundary layers
return true;
}
+static bool
+do_set_sea_level_degc (const SGPropertyNode * arg)
+{
+ return do_set_sea_level_degc( arg->getDoubleValue("temp-degc", 15.0) );
+}
+
/**
* Set the outside air temperature at the "current" altitude by first
static bool
do_set_oat_degc (const SGPropertyNode * arg)
{
- const string &temp_str = arg->getStringValue("temp-degc", "15.0");
-
+ double oat_degc = arg->getDoubleValue("temp-degc", 15.0);
// check for an altitude specified in the arguments, otherwise use
// current aircraft altitude.
const SGPropertyNode *altitude_ft = arg->getChild("altitude-ft");
FGEnvironment dummy; // instantiate a dummy so we can leech a method
dummy.set_elevation_ft( altitude_ft->getDoubleValue() );
- dummy.set_temperature_degc( atof( temp_str.c_str() ) );
- double temp_sea_level_degc = dummy.get_temperature_sea_level_degc();
-
- //cout << "Altitude = " << altitude_ft->getDoubleValue() << endl;
- //cout << "Temp at alt (C) = " << atof( temp_str.c_str() ) << endl;
- //cout << "Temp sea level (C) = " << temp_sea_level_degc << endl;
-
- SGPropertyNode *node, *child;
-
- // boundary layers
- node = fgGetNode( "/environment/config/boundary" );
- if ( node != NULL ) {
- int i = 0;
- while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
- child->setDoubleValue( "temperature-sea-level-degc",
- temp_sea_level_degc );
- ++i;
- }
- }
-
- // aloft layers
- node = fgGetNode( "/environment/config/aloft" );
- if ( node != NULL ) {
- int i = 0;
- while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
- child->setDoubleValue( "temperature-sea-level-degc",
- temp_sea_level_degc );
- ++i;
- }
- }
-
- return true;
+ dummy.set_temperature_degc( oat_degc );
+ return do_set_sea_level_degc( dummy.get_temperature_sea_level_degc());
}
/**
* boundary and aloft environment layers.
*/
static bool
-do_set_dewpoint_sea_level_degc (const SGPropertyNode * arg)
+do_set_dewpoint_sea_level_degc (double dewpoint_sea_level_degc)
{
- double dewpoint_sea_level_degc = arg->getDoubleValue("dewpoint-degc", 5.0);
SGPropertyNode *node, *child;
return true;
}
+static bool
+do_set_dewpoint_sea_level_degc (const SGPropertyNode * arg)
+{
+ return do_set_dewpoint_sea_level_degc(arg->getDoubleValue("dewpoint-degc", 5.0));
+}
/**
* Set the outside air dewpoint at the "current" altitude by first
static bool
do_set_dewpoint_degc (const SGPropertyNode * arg)
{
- const string &dewpoint_str = arg->getStringValue("dewpoint-degc", "5.0");
+ double dewpoint_degc = arg->getDoubleValue("dewpoint-degc", 5.0);
// check for an altitude specified in the arguments, otherwise use
// current aircraft altitude.
FGEnvironment dummy; // instantiate a dummy so we can leech a method
dummy.set_elevation_ft( altitude_ft->getDoubleValue() );
- dummy.set_dewpoint_degc( atof( dewpoint_str.c_str() ) );
- double dewpoint_sea_level_degc = dummy.get_dewpoint_sea_level_degc();
-
- //cout << "Altitude = " << altitude_ft->getDoubleValue() << endl;
- //cout << "Dewpoint at alt (C) = " << atof( dewpoint_str.c_str() ) << endl;
- //cout << "Dewpoint at sea level (C) = " << dewpoint_sea_level_degc << endl;
-
- SGPropertyNode *node, *child;
-
- // boundary layers
- node = fgGetNode( "/environment/config/boundary" );
- if ( node != NULL ) {
- int i = 0;
- while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
- child->setDoubleValue( "dewpoint-sea-level-degc",
- dewpoint_sea_level_degc );
- ++i;
- }
- }
-
- // aloft layers
- node = fgGetNode( "/environment/config/aloft" );
- if ( node != NULL ) {
- int i = 0;
- while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
- child->setDoubleValue( "dewpoint-sea-level-degc",
- dewpoint_sea_level_degc );
- ++i;
- }
- }
-
- return true;
+ dummy.set_dewpoint_degc( dewpoint_degc );
+ return do_set_dewpoint_sea_level_degc(dummy.get_dewpoint_sea_level_degc());
}
/**
void
fgInitCommands ()
{
- SG_LOG(SG_GENERAL, SG_INFO, "Initializing basic built-in commands:");
+ SG_LOG(SG_GENERAL, SG_BULK, "Initializing basic built-in commands:");
for (int i = 0; built_ins[i].name != 0; i++) {
- SG_LOG(SG_GENERAL, SG_INFO, " " << built_ins[i].name);
+ SG_LOG(SG_GENERAL, SG_BULK, " " << built_ins[i].name);
globals->get_commands()->addCommand(built_ins[i].name,
built_ins[i].command);
}
#include <Airports/dynamics.hxx>
#include <AIModel/AIManager.hxx>
-#include <ATCDCL/ATCmgr.hxx>
-#include <ATCDCL/AIMgr.hxx>
+
+#if ENABLE_ATCDCL
+# include <ATCDCL/ATCmgr.hxx>
+# include <ATCDCL/AIMgr.hxx>
+# include "ATCDCL/commlist.hxx"
+#else
+# include "ATC/atcutils.hxx"
+#endif
+
#include <Autopilot/route_mgr.hxx>
#include <Autopilot/xmlauto.hxx>
#include <Autopilot/autobrake.hxx>
#include <Cockpit/cockpit.hxx>
#include <Cockpit/panel.hxx>
#include <Cockpit/panel_io.hxx>
-#ifdef ENABLE_SP_FDM
+
+#if ENABLE_SP_FDM
#include <FDM/SP/ADA.hxx>
#include <FDM/SP/ACMS.hxx>
#include <FDM/SP/MagicCarpet.hxx>
#include "renderer.hxx"
#include "viewmgr.hxx"
#include "main.hxx"
-#include "ATCDCL/commlist.hxx"
+
#ifdef __APPLE__
# include <CoreFoundation/CoreFoundation.h>
// Initialise the frequency search map BEFORE reading
// the airport database:
+
+
+
current_commlist = new FGCommList;
current_commlist->init( globals->get_fg_root() );
fgAirportDBLoad( aptdb.str(), current_commlist, p_metar.str() );
// Initialize the XML Autopilot subsystem.
////////////////////////////////////////////////////////////////////
- globals->add_subsystem( "xml-autopilot", new FGXMLAutopilot );
+ globals->add_subsystem( "xml-autopilot", new FGXMLAutopilotGroup );
globals->add_subsystem( "route-manager", new FGRouteMgr );
globals->add_subsystem( "autobrake", new FGAutoBrake );
// Initialise the ATC Manager
////////////////////////////////////////////////////////////////////
+#if ENABLE_ATCDCL
SG_LOG(SG_GENERAL, SG_INFO, " ATC Manager");
globals->set_ATC_mgr(new FGATCMgr);
globals->get_ATC_mgr()->init();
-
+
////////////////////////////////////////////////////////////////////
// Initialise the AI Manager
////////////////////////////////////////////////////////////////////
SG_LOG(SG_GENERAL, SG_INFO, " AI Manager");
globals->set_AI_mgr(new FGAIMgr);
globals->get_AI_mgr()->init();
-
+#endif
////////////////////////////////////////////////////////////////////
// Initialise the AI Model Manager
////////////////////////////////////////////////////////////////////
return magheading;
}
+/**
+ * Return the current track in degrees.
+ */
+static double
+getTrackMag ()
+{
+ double magtrack;
+ magtrack = current_aircraft.fdm_state->get_Track() - getMagVar();
+ if (magtrack < 0) magtrack += 360;
+ return magtrack;
+}
+
static long
getWarp ()
{
// Orientation
fgTie("/orientation/heading-magnetic-deg", getHeadingMag);
+ fgTie("/orientation/track-magnetic-deg", getTrackMag);
fgTie("/environment/magnetic-variation-deg", getMagVar);
fgTie("/environment/magnetic-dip-deg", getMagDip);
// Orientation
fgUntie("/orientation/heading-magnetic-deg");
+ fgUntie("/orientation/track-magnetic-deg");
// Environment
fgUntie("/environment/magnetic-variation-deg");
fgGetNode(entry->getStringValue("property"), true);
log.nodes.push_back(node);
(*log.output) << log.delimiter
- << entry->getStringValue("title", node->getPath());
+ << entry->getStringValue("title", node->getPath().c_str());
}
(*log.output) << endl;
}
+if ENABLE_ATCDCL
+ATCDCL_DIR = ATCDCL
+else
+ATCDCL_DIR =
+endif
+
+
SUBDIRS = \
Include \
Aircraft \
Airports \
ATC \
- ATCDCL \
+ $(ATCDCL_DIR) \
Autopilot \
Cockpit \
Environment \
{1100, "sim/model/variant", simgear::props::INT},
{1101, "sim/model/livery/file", simgear::props::STRING},
+ {1200, "environment/wildfire/data", simgear::props::STRING},
+
{10001, "sim/multiplay/transmission-freq-hz", simgear::props::STRING},
{10002, "sim/multiplay/chat", simgear::props::STRING},
mServer.set(txAddress.c_str(), txPort);
if (strncmp (mServer.getHost(), "0.0.0.0", 8) == 0) {
mHaveServer = false;
- SG_LOG(SG_NETWORK, SG_ALERT,
+ SG_LOG(SG_NETWORK, SG_DEBUG,
"FGMultiplayMgr - could not resolve '"
<< txAddress << "', Multiplayermode disabled");
} else {
rxPort = txPort;
}
if (rxPort <= 0) {
- SG_LOG(SG_NETWORK, SG_ALERT,
+ SG_LOG(SG_NETWORK, SG_DEBUG,
"FGMultiplayMgr - No receiver port, Multiplayermode disabled");
return (false);
}
// A memory leak was reported here by valgrind
mSocket = new netSocket();
if (!mSocket->open(false)) {
- SG_LOG( SG_NETWORK, SG_ALERT,
+ SG_LOG( SG_NETWORK, SG_DEBUG,
"FGMultiplayMgr::init - Failed to create data socket" );
return false;
}
mSocket->setBlocking(false);
if (mSocket->bind(rxAddress.c_str(), rxPort) != 0) {
perror("bind");
- SG_LOG( SG_NETWORK, SG_ALERT,
+ SG_LOG( SG_NETWORK, SG_DEBUG,
"FGMultiplayMgr::Open - Failed to bind receive socket" );
return false;
}
if ((! mInitialised) || (! mHaveServer))
return;
if (! mHaveServer) {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::SendMyPosition - no server");
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition - no server");
return;
}
break;
}
if (bytes <= static_cast<ssize_t>(sizeof(T_MsgHdr))) {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "received message with insufficient data" );
break;
}
MsgHdr->ReplyPort = XDR_decode_uint32 (MsgHdr->ReplyPort);
MsgHdr->Callsign[MAX_CALLSIGN_LEN -1] = '\0';
if (MsgHdr->Magic != MSG_MAGIC) {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "message has invalid magic number!" );
break;
}
if (MsgHdr->Version != PROTO_VER) {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "message has invalid protocoll number!" );
break;
}
if (MsgHdr->MsgLen != bytes) {
- SG_LOG(SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "message from " << MsgHdr->Callsign << " has invalid length!");
break;
}
case OLD_POS_DATA_ID:
break;
default:
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "Unknown message Id received: " << MsgHdr->MsgId );
break;
}
{
const T_MsgHdr* MsgHdr = Msg.msgHdr();
if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "Position message received with insufficient data" );
return;
}
default:
pData->float_value = XDR_decode_float(*xdr);
- SG_LOG(SG_NETWORK, SG_ALERT, "Unknown Prop type " << pData->id << " " << pData->type);
+ SG_LOG(SG_NETWORK, SG_DEBUG, "Unknown Prop type " << pData->id << " " << pData->type);
xdr++;
break;
}
{
const T_MsgHdr* MsgHdr = Msg.msgHdr();
if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1) {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "Chat message received with insufficient data" );
return;
}
case FG_DOUBLE:
val = _out_message[i].offset +
- _out_message[i].prop->getFloatValue() * _out_message[i].factor;
- snprintf(tmp, 255, _out_message[i].format.c_str(), (float)val);
+ _out_message[i].prop->getDoubleValue() * _out_message[i].factor;
+ snprintf(tmp, 255, _out_message[i].format.c_str(), (double)val);
break;
default: // SG_STRING
}
}
} else {
- do {
- if (!binary_mode) {
- length = io->readline( buf, FG_MAX_MSG_SIZE );
- if ( length > 0 ) {
- parse_message();
- }
- } else {
- length = io->read( buf, binary_record_length );
- if ( length == binary_record_length ) {
- parse_message();
- } else if ( length > 0 ) {
- SG_LOG( SG_IO, SG_ALERT,
- "Generic protocol: Received binary "
- "record of unexpected size, expected: "
- << binary_record_length << " but received: "
- << length);
- }
+ if (!binary_mode) {
+ while ((length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
+ parse_message();
}
- } while ( length == binary_record_length );
+ } else {
+ while ((length = io->read( buf, binary_record_length ))
+ == binary_record_length ) {
+ parse_message();
+ }
+
+ if ( length > 0 ) {
+ SG_LOG( SG_IO, SG_ALERT,
+ "Generic protocol: Received binary "
+ "record of unexpected size, expected: "
+ << binary_record_length << " but received: "
+ << length);
+ }
+ }
}
}
return true;
#include <simgear/debug/logstream.hxx>
#include <simgear/io/iochannel.hxx>
+#include <simgear/misc/stdint.hxx>
#include <Aircraft/aircraft.hxx>
#include <Main/fg_props.hxx>
if ( io->get_type() == sgFileType ) {
if ( io->read( (char *)(& buf), length ) == length ) {
SG_LOG( SG_IO, SG_DEBUG, "Success reading data." );
- long int *msg;
- msg = (long int *)buf;
+ int32_t *msg = (int32_t *)buf;
for( int i = 0; i < 4; ++i )
{
axis[i] = ((double)msg[i] / 2147483647.0);
} else {
while ( io->read( (char *)(& buf), length ) == length ) {
SG_LOG( SG_IO, SG_DEBUG, "Success reading data." );
- long int *msg;
- msg = (long int *)buf;
+ int32_t *msg = (int32_t *)buf;
SG_LOG( SG_IO, SG_DEBUG, "ax0 = " << msg[0] << " ax1 = "
<< msg[1] << "ax2 = " << msg[2] << "ax3 = " << msg[3]);
for( int i = 0; i < 4; ++i )
const char* src = n->getStringValue("script");
if(!n->hasChild("script")) src = 0; // Hrm...
if(src)
- createModule(module, n->getPath(), src, strlen(src));
+ createModule(module, n->getPath().c_str(), src, strlen(src));
if(!file_specified && !src)
SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
{
const char* nasal = arg->getStringValue("script");
const char* moduleName = arg->getStringValue("module");
- naRef code = parse(arg->getPath(true), nasal, strlen(nasal));
+ naRef code = parse(arg->getPath(true).c_str(), nasal, strlen(nasal));
if(naIsNil(code)) return false;
// Commands can be run "in" a module. Make sure that module
#include "SchedFlight.hxx"
#include "TrafficMgr.hxx"
-using std::sort;
-
/******************************************************************************
* the FGAISchedule class contains data members and code to maintain a
* schedule of Flights for an articically controlled aircraft.
AIManagerRef = 0;
heavy = false;
- lat = 0;
- lon = 0;
radius = 0;
groundOffset = 0;
distanceToUser = 0;
airline = arln;
m_class = mclass;
flightType = fltpe;
- lat = 0;
- lon = 0;
radius = rad;
groundOffset = grnd;
distanceToUser = 0;
heavy = other.heavy;
flightIdentifier = other.flightIdentifier;
flights = other.flights;
- lat = other.lat;
- lon = other.lon;
AIManagerRef = other.AIManagerRef;
acType = other.acType;
airline = other.airline;
return true;
}
-bool FGAISchedule::update(time_t now)
+bool FGAISchedule::update(time_t now, const SGVec3d& userCart)
{
- FGAirport *dep;
- FGAirport *arr;
- double angle;
-
- FGAIManager *aimgr;
- string airport;
-
- double speed;
-
+ if (!fgGetBool("/sim/traffic-manager/enabled"))
+ return true;
+
time_t
totalTimeEnroute,
elapsedTimeEnroute,
- remainingTimeEnroute, deptime = 0;
- double
- userLatitude,
- userLongitude;
-
- SGVec3d newPos(0, 0, 0);
-
-
- if (fgGetBool("/sim/traffic-manager/enabled") == false)
- return true;
+ remainingTimeEnroute,
+ deptime = 0;
- aimgr = (FGAIManager *) globals-> get_subsystem("ai_model");
- // Out-of-work aircraft seeks employment. Willing to work irregular hours ...
- //cerr << "About to find a flight " << endl;
- if (flights.empty()) {
- //execute this loop at least once.
- SG_LOG(SG_GENERAL, SG_BULK, "Scheduling for : " << modelPath << " " << registration << " " << homePort);
- FGScheduledFlight *flight = 0;
- do {
- flight = findAvailableFlight(currentDestination, flightIdentifier);
- if (flight) {
- currentDestination = flight->getArrivalAirport()->getId();
- time_t arr, dep;
- dep = flight->getDepartureTime();
- arr = flight->getArrivalTime();
- string depT = asctime(gmtime(&dep));
- string arrT = asctime(gmtime(&arr));
-
- depT = depT.substr(0,24);
- arrT = arrT.substr(0,24);
- SG_LOG(SG_GENERAL, SG_BULK, " " << flight->getCallSign() << ":"
- << " " << flight->getDepartureAirport()->getId() << ":"
- << " " << depT << ":"
- << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
- << " " << arrT << ":");
- flights.push_back(flight);
- }
- } while ((currentDestination != homePort) && (flight != 0));
- SG_LOG(SG_GENERAL, SG_BULK, cerr << " Done " << endl);
- }
- //cerr << " Done " << endl;
- // No flights available for this aircraft
- if (flights.size() == 0) {
+ scheduleFlights();
+ if (flights.empty()) { // No flights available for this aircraft
return false;
}
+
// Sort all the scheduled flights according to scheduled departure time.
// Because this is done at every update, we only need to check the status
// of the first listed flight.
//sort(flights.begin(), flights.end(), compareScheduledFlights);
- if (firstRun) {
+
+ if (firstRun) {
if (fgGetBool("/sim/traffic-manager/instantaneous-action") == true) {
deptime = now + rand() % 300; // Wait up to 5 minutes until traffic starts moving to prevent too many aircraft
// from cluttering the gate areas.
- cerr << "Scheduling " << registration << " for instantaneous action flight " << endl;
}
firstRun = false;
}
+
+ FGScheduledFlight* flight = flights.front();
if (!deptime)
- deptime = (*flights.begin())->getDepartureTime();
- FGScheduledFlightVecIterator i = flights.begin();
- SG_LOG (SG_GENERAL, SG_DEBUG,"Traffic Manager: Processing registration " << registration << " with callsign " << (*i)->getCallSign());
- if (AIManagerRef)
- {
- // Check if this aircraft has been released.
- FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
- if (tmgr->isReleased(AIManagerRef))
- AIManagerRef = 0;
+ deptime = flight->getDepartureTime();
+
+ if (AIManagerRef) {
+ // Check if this aircraft has been released.
+ FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
+ if (tmgr->isReleased(AIManagerRef)) {
+ AIManagerRef = NULL;
+ } else {
+ return true; // in visual range, let the AIManager handle it
}
-
- if (!AIManagerRef)
- {
- userLatitude = fgGetDouble("/position/latitude-deg");
- userLongitude = fgGetDouble("/position/longitude-deg");
-
- //cerr << "Estimated minimum distance to user: " << distanceToUser << endl;
- // This flight entry is entirely in the past, do we need to
- // push it forward in time to the next scheduled departure.
- if (((*i)->getDepartureTime() < now) && ((*i)->getArrivalTime() < now))
- {
- SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic Manager: Flight is in the Past");
- //cerr << modelPath << " " << registration << ": Flights from the past belong to the past :-)" << endl;
- //exit(1);
- // Don't just update: check whether we need to load a new leg. etc.
- // This update occurs for distant aircraft, so we can update the current leg
- // and detach it from the current list of aircraft.
- (*i)->update();
- i = flights.erase(i);
+ }
+
+ // This flight entry is entirely in the past, do we need to
+ // push it forward in time to the next scheduled departure.
+ if (flight->getArrivalTime() < now) {
+ SG_LOG (SG_GENERAL, SG_BULK, "Traffic Manager: Flight is in the Past");
+ // Don't just update: check whether we need to load a new leg. etc.
+ // This update occurs for distant aircraft, so we can update the current leg
+ // and detach it from the current list of aircraft.
+ flight->update();
+ flights.erase(flights.begin()); // pop_front(), effectively
return true;
}
-
- // Departure time in the past and arrival time in the future.
- // This flight is in progress, so we need to calculate it's
- // approximate position and -if in range- create an AIAircraft
- // object for it.
- //if ((i->getDepartureTime() < now) && (i->getArrivalTime() > now))
+
+ FGAirport* dep = flight->getDepartureAirport();
+ FGAirport* arr = flight->getArrivalAirport();
+ if (!dep || !arr) {
+ return false;
+ }
+
+ double speed = 450.0;
+ if (dep != arr) {
+ totalTimeEnroute = flight->getArrivalTime() - flight->getDepartureTime();
+ if (flight->getDepartureTime() < now) {
+ elapsedTimeEnroute = now - flight->getDepartureTime();
+ remainingTimeEnroute = totalTimeEnroute - elapsedTimeEnroute;
+ double x = elapsedTimeEnroute / (double) totalTimeEnroute;
- // Part of this flight is in the future.
- if ((*i)->getArrivalTime() > now)
- {
-
- dep = (*i)->getDepartureAirport();
- arr = (*i)->getArrivalAirport ();
- if (!(dep && arr))
- return false;
-
- if (dep != arr) {
- SGVec3d a = SGVec3d::fromGeoc(SGGeoc::fromDegM(dep->getLongitude(),
- dep->getLatitude(), 1));
- SGVec3d b = SGVec3d::fromGeoc(SGGeoc::fromDegM(arr->getLongitude(),
- arr->getLatitude(), 1));
- SGVec3d _cross = cross(b, a);
-
- angle = sgACos(dot(a, b));
-
- // Okay, at this point we have the angle between departure and
- // arrival airport, in degrees. From here we can interpolate the
- // position of the aircraft by calculating the ratio between
- // total time enroute and elapsed time enroute.
-
- totalTimeEnroute = (*i)->getArrivalTime() - (*i)->getDepartureTime();
- if (now > (*i)->getDepartureTime())
- {
- //err << "Lat = " << lat << ", lon = " << lon << endl;
- //cerr << "Time diff: " << now-i->getDepartureTime() << endl;
- elapsedTimeEnroute = now - (*i)->getDepartureTime();
- remainingTimeEnroute = (*i)->getArrivalTime() - now;
- SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic Manager: Flight is in progress.");
- }
- else
- {
- lat = dep->getLatitude();
- lon = dep->getLongitude();
- elapsedTimeEnroute = 0;
- remainingTimeEnroute = totalTimeEnroute;
- SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic Manager: Flight is pending.");
- }
- angle *= ( (double) elapsedTimeEnroute/ (double) totalTimeEnroute);
- //cout << "a = " << a[0] << " " << a[1] << " " << a[2]
- // << "b = " << b[0] << " " << b[1] << " " << b[2] << endl;
- sgdMat4 matrix;
- sgdMakeRotMat4(matrix, angle, _cross.data());
- for(int j = 0; j < 3; j++) {
- for (int k = 0; k<3; k++) {
- newPos[j] += matrix[j][k]*a[k];
- }
- }
- }
- SGGeod current;
- if ((now > (*i)->getDepartureTime() && (dep != arr))) {
- current = SGGeod::fromCart(newPos);
- speed = SGGeodesy::distanceNm(current, arr->geod()) /
- ((double) remainingTimeEnroute/3600.0);
- } else {
- current = dep->geod();
- speed = 450;
- }
- SGGeod user = SGGeod::fromDegM(userLongitude, userLatitude, (*i)->getCruiseAlt());
-
- distanceToUser = SGGeodesy::distanceNm(current, user);
-
- // If distance between user and simulated aircaft is less
- // then 500nm, create this flight. At jet speeds 500 nm is roughly
- // one hour flight time, so that would be a good approximate point
- // to start a more detailed simulation of this aircraft.
- SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic manager: " << registration << " is scheduled for a flight from "
+ // current pos is based on great-circle course between departure/arrival,
+ // with percentage of distance travelled, based upon percentage of time
+ // enroute elapsed.
+ double course, az2, distanceM;
+ SGGeodesy::inverse(dep->geod(), arr->geod(), course, az2, distanceM);
+ double coveredDistance = distanceM * x;
+
+ SGGeodesy::direct(dep->geod(), course, coveredDistance, position, az2);
+
+ SG_LOG (SG_GENERAL, SG_BULK, "Traffic Manager: Flight is in progress, %=" << x);
+ speed = ((distanceM - coveredDistance) * SG_METER_TO_NM) / 3600.0;
+ } else {
+ // not departed yet
+ remainingTimeEnroute = totalTimeEnroute;
+ elapsedTimeEnroute = 0;
+ position = dep->geod();
+ SG_LOG (SG_GENERAL, SG_BULK, "Traffic Manager: Flight is pending, departure in "
+ << flight->getDepartureTime() - now << " seconds ");
+ }
+ } else {
+ // departure / arrival coincident
+ remainingTimeEnroute = totalTimeEnroute = 0.0;
+ elapsedTimeEnroute = 0;
+ position = dep->geod();
+ }
+
+ // cartesian calculations are more numerically stable over the (potentially)
+ // large distances involved here: see bug #80
+ distanceToUser = dist(userCart, SGVec3d::fromGeod(position)) * SG_METER_TO_NM;
+
+ // If distance between user and simulated aircaft is less
+ // then 500nm, create this flight. At jet speeds 500 nm is roughly
+ // one hour flight time, so that would be a good approximate point
+ // to start a more detailed simulation of this aircraft.
+ SG_LOG (SG_GENERAL, SG_BULK, "Traffic manager: " << registration << " is scheduled for a flight from "
<< dep->getId() << " to " << arr->getId() << ". Current distance to user: "
<< distanceToUser);
- if (distanceToUser < TRAFFICTOAIDISTTOSTART)
- {
- string flightPlanName = dep->getId() + string("-") + arr->getId() +
- string(".xml");
- SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic manager: Creating AIModel");
- //int alt;
- //if ((i->getDepartureTime() < now))
- //{
- // alt = i->getCruiseAlt() *100;
- // }
- //else
- //{
- // alt = dep->_elevation+19;
- // }
-
- // Only allow traffic to be created when the model path (or the AI version of mp) exists
- SGPath mp(globals->get_fg_root());
- SGPath mp_ai = mp;
-
- mp.append(modelPath);
- mp_ai.append("AI");
- mp_ai.append(modelPath);
-
- if (mp.exists() || mp_ai.exists())
- {
- FGAIAircraft *aircraft = new FGAIAircraft(this);
- aircraft->setPerformance(m_class); //"jet_transport";
- aircraft->setCompany(airline); //i->getAirline();
- aircraft->setAcType(acType); //i->getAcType();
- aircraft->setPath(modelPath.c_str());
- //aircraft->setFlightPlan(flightPlanName);
- aircraft->setLatitude(lat);
- aircraft->setLongitude(lon);
- aircraft->setAltitude((*i)->getCruiseAlt()*100); // convert from FL to feet
- aircraft->setSpeed(speed);
- aircraft->setBank(0);
+ if (distanceToUser >= TRAFFICTOAIDISTTOSTART) {
+ return true; // out of visual range, for the moment.
+ }
+
+ return createAIAircraft(flight, speed, deptime);
+}
+
+bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime)
+{
+ FGAirport* dep = flight->getDepartureAirport();
+ FGAirport* arr = flight->getArrivalAirport();
+ string flightPlanName = dep->getId() + "-" + arr->getId() + ".xml";
+ SG_LOG(SG_GENERAL, SG_INFO, "Traffic manager: Creating AIModel from:" << flightPlanName);
+
+ // Only allow traffic to be created when the model path (or the AI version of mp) exists
+ SGPath mp(globals->get_fg_root());
+ SGPath mp_ai = mp;
+
+ mp.append(modelPath);
+ mp_ai.append("AI");
+ mp_ai.append(modelPath);
+
+ if (!mp.exists() && !mp_ai.exists()) {
+ SG_LOG(SG_INPUT, SG_WARN, "TrafficManager: Could not load model " << mp.str());
+ return true;
+ }
+
+ FGAIAircraft *aircraft = new FGAIAircraft(this);
+ aircraft->setPerformance(m_class); //"jet_transport";
+ aircraft->setCompany(airline); //i->getAirline();
+ aircraft->setAcType(acType); //i->getAcType();
+ aircraft->setPath(modelPath.c_str());
+ //aircraft->setFlightPlan(flightPlanName);
+ aircraft->setLatitude(position.getLatitudeDeg());
+ aircraft->setLongitude(position.getLongitudeDeg());
+ aircraft->setAltitude(flight->getCruiseAlt()*100); // convert from FL to feet
+ aircraft->setSpeed(speedKnots);
+ aircraft->setBank(0);
- courseToDest = SGGeodesy::courseDeg(current, arr->geod());
- aircraft->SetFlightPlan(new FGAIFlightPlan(aircraft, flightPlanName, courseToDest, deptime,
- dep, arr,true, radius,
- (*i)->getCruiseAlt()*100,
- lat, lon, speed, flightType, acType,
+ courseToDest = SGGeodesy::courseDeg(position, arr->geod());
+ aircraft->SetFlightPlan(new FGAIFlightPlan(aircraft, flightPlanName, courseToDest, deptime,
+ dep, arr, true, radius,
+ flight->getCruiseAlt()*100,
+ position.getLatitudeDeg(),
+ position.getLongitudeDeg(),
+ speedKnots, flightType, acType,
airline));
- aimgr->attach(aircraft);
-
-
- AIManagerRef = aircraft->getID();
- //cerr << "Class: " << m_class << ". acType: " << acType << ". Airline: " << airline << ". Speed = " << speed << ". From " << dep->getId() << " to " << arr->getId() << ". Time Fraction = " << (remainingTimeEnroute/(double) totalTimeEnroute) << endl;
- //cerr << "Latitude : " << lat << ". Longitude : " << lon << endl;
- //cerr << "Dep : " << dep->getLatitude()<< ", "<< dep->getLongitude() << endl;
- //cerr << "Arr : " << arr->getLatitude()<< ", "<< arr->getLongitude() << endl;
- //cerr << "Time remaining = " << (remainingTimeEnroute/3600.0) << endl;
- //cerr << "Total time = " << (totalTimeEnroute/3600.0) << endl;
- //cerr << "Distance remaining = " << distanceToDest*SG_METER_TO_NM << endl;
- }
- else
- {
- SG_LOG(SG_INPUT, SG_WARN, "TrafficManager: Could not load model " << mp.str());
- }
- }
- return true;
- }
+
+
+ FGAIManager* aimgr = (FGAIManager *) globals-> get_subsystem("ai_model");
+ aimgr->attach(aircraft);
+ AIManagerRef = aircraft->getID();
+ return true;
+}
- // Both departure and arrival time are in the future, so this
- // the aircraft is parked at the departure airport.
- // Currently this status is mostly ignored, but in future
- // versions, code should go here that -if within user range-
- // positions these aircraft at parking locations at the airport.
- if (((*i)->getDepartureTime() > now) && ((*i)->getArrivalTime() > now))
- {
- dep = (*i)->getDepartureAirport();
- return true;
- }
+void FGAISchedule::scheduleFlights()
+{
+ if (!flights.empty()) {
+ return;
+ }
+
+ SG_LOG(SG_GENERAL, SG_BULK, "Scheduling for : " << modelPath << " " << registration << " " << homePort);
+ FGScheduledFlight *flight = NULL;
+ do {
+ flight = findAvailableFlight(currentDestination, flightIdentifier);
+ if (!flight) {
+ break;
}
- //cerr << "Traffic schedule got to beyond last clause" << endl;
- // EMH: prevent a warning, should this be 'true' instead?
- // DT: YES. Originally, this code couldn't be reached, but
- // when the "if(!(AIManagerManager))" clause is false we
- // fall through right to the end. This is a valid flow.
- // the actual value is pretty innocent, only it triggers
- // warning in TrafficManager::update().
- // (which was added as a sanity check for myself in the first place. :-)
- return true;
+
+ currentDestination = flight->getArrivalAirport()->getId();
+
+ time_t arr, dep;
+ dep = flight->getDepartureTime();
+ arr = flight->getArrivalTime();
+ string depT = asctime(gmtime(&dep));
+ string arrT = asctime(gmtime(&arr));
+
+ depT = depT.substr(0,24);
+ arrT = arrT.substr(0,24);
+ SG_LOG(SG_GENERAL, SG_BULK, " " << flight->getCallSign() << ":"
+ << " " << flight->getDepartureAirport()->getId() << ":"
+ << " " << depT << ":"
+ << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
+ << " " << arrT << ":");
+
+ flights.push_back(flight);
+ } while (currentDestination != homePort);
+ SG_LOG(SG_GENERAL, SG_BULK, " Done ");
}
-
bool FGAISchedule::next()
{
- FGScheduledFlightVecIterator i = flights.begin();
- (*i)->release();
- //FIXME: remove first entry,
- // load new flights until back at home airport
- // Lock loaded flights
- //sort(flights.begin(), flights.end(), compareScheduledFlights);
- // until that time
- i = flights.erase(i);
- //cerr << "Next: scheduling for : " << modelPath << " " << registration << endl;
+ if (!flights.empty()) {
+ flights.front()->release();
+ flights.erase(flights.begin());
+ }
+
FGScheduledFlight *flight = findAvailableFlight(currentDestination, flightIdentifier);
- if (flight) {
- currentDestination = flight->getArrivalAirport()->getId();
- time_t arr, dep;
- dep = flight->getDepartureTime();
- arr = flight->getArrivalTime();
- string depT = asctime(gmtime(&dep));
- string arrT = asctime(gmtime(&arr));
-
- depT = depT.substr(0,24);
- arrT = arrT.substr(0,24);
- //cerr << " " << flight->getCallSign() << ":"
- // << " " << flight->getDepartureAirport()->getId() << ":"
- // << " " << depT << ":"
- // << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
- // << " " << arrT << ":" << endl;
-
- flights.push_back(flight);
- return true;
- } else {
- return false;
+ if (!flight) {
+ return false;
}
- //cerr << "FGAISchedule :: next needs updating" << endl;
- //exit(1);
+
+ currentDestination = flight->getArrivalAirport()->getId();
+/*
+ time_t arr, dep;
+ dep = flight->getDepartureTime();
+ arr = flight->getArrivalTime();
+ string depT = asctime(gmtime(&dep));
+ string arrT = asctime(gmtime(&arr));
+
+ depT = depT.substr(0,24);
+ arrT = arrT.substr(0,24);
+ //cerr << " " << flight->getCallSign() << ":"
+ // << " " << flight->getDepartureAirport()->getId() << ":"
+ // << " " << depT << ":"
+ // << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
+ // << " " << arrT << ":" << endl;
+*/
+ flights.push_back(flight);
+ return true;
}
FGScheduledFlight* FGAISchedule::findAvailableFlight (const string ¤tDestination,
//sort(fltBegin, fltEnd, compareScheduledFlights);
//cerr << counter++ << endl;
}
- sort(fltBegin, fltEnd, compareScheduledFlights);
+ std::sort(fltBegin, fltEnd, compareScheduledFlights);
for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
//bool valid = true;
counter++;
// is departure port valid?
// is arrival port valid?
//cerr << "Ack no flight found: " << endl;
- return 0;
+ return NULL;
}
double FGAISchedule::getSpeed()
string currentDestination;
bool heavy;
FGScheduledFlightVec flights;
- float lat;
- float lon;
+ SGGeod position;
double radius;
double groundOffset;
double distanceToUser;
bool firstRun;
double courseToDest;
-
+ void scheduleFlights();
+
+ /**
+ * Transition this schedule from distant mode to AI mode;
+ * create the AIAircraft (and flight plan) and register with the AIManager
+ */
+ bool createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime);
+
public:
FGAISchedule(); // constructor
FGAISchedule(string model,
~FGAISchedule(); //destructor
- bool update(time_t now);
+ bool update(time_t now, const SGVec3d& userCart);
bool init();
double getSpeed ();
if (scheduledAircraft.size() == 0) {
return;
}
+
+ SGVec3d userCart = SGVec3d::fromGeod(SGGeod::fromDeg(
+ fgGetDouble("/position/longitude-deg"),
+ fgGetDouble("/position/latitude-deg")));
+
if(currAircraft == scheduledAircraft.end())
{
currAircraft = scheduledAircraft.begin();
}
- if (!((*currAircraft)->update(now)))
+ if (!((*currAircraft)->update(now, userCart)))
{
// NOTE: With traffic manager II, this statement below is no longer true
// after proper initialization, we shouldnt get here.
-DIST_SUBDIRS = GPSsmooth TerraSync Modeller js_server fgadmin xmlgrep propmerge
+DIST_SUBDIRS = GPSsmooth TerraSync Modeller js_server fgadmin xmlgrep propmerge fgviewer
SUBDIRS = GPSsmooth TerraSync Modeller js_server propmerge fgviewer
}
sz = strlen(opt);
- if (!strncmp(opt, "-help", sz)) {
- printf("usage:\n normalmap [-c=contrast] [-b=brightness]");
- printf(" --t=file [--o=file]\n");
- exit(0);
- }
if (!strncmp(opt, "-contrast", sz)) {
contrast = atof(arg);
return ret;
normalmap_file = strdup(arg);
return ret;
}
+ if (!strncmp(opt, "-help", sz)) {
+ printf("usage:\n normalmap [-c=contrast] [-b=brightness]");
+ printf(" --i=file [--o=file]\n\n");
+ exit(0);
+ }
return 1;
}
if ( !texture_file )
{
- printf("Error: texture file not specified\n");
+ printf("Error: texture file not specified.\n");
+ printf("usage:\n normalmap [-c=contrast] [-b=brightness]");
+ printf(" --i=file [--o=file]\n\n");
return -1;
}
if ( !texture.texture() )
{
printf("Error: unable to process input file: %s\n", texture_file);
- printf(" (%s)\n", texture.err_str());
+ printf(" (%s)\n\n", texture.err_str());
return -2;
}
int yp1 = (y < (texture_height-1)) ? y+1 : 0;
int posxp1 = (xp1 + ytw)*num_colors;
int posyp1 = (x + yp1*texture_width)*num_colors;
+ float fx,fy;
GLubyte c = texture_data[dpos];
GLubyte cx1 = texture_data[posxp1];
map[mpos+3] = a;
}
- map[mpos+0] = (128+(cx1-c)/2);
- map[mpos+1] = (128+(cy1-c)/2);
+ fx = asin((c/256.0-cx1/256.0))/1.57079633;
+ fy = asin((cy1/256.0-c/256.0))/1.57079633;
+
+ map[mpos+0] = (GLuint)(fx*256.0)-128;
+ map[mpos+1] = (GLuint)(fy*256.0)-128;
map[mpos+2] = 127+int(brightness*128); // 255-c/2;
mpos += colors;
#ifdef __MINGW32__
#include <time.h>
#include <unistd.h>
+#elif defined(_MSC_VER)
+#include <io.h>
#endif
#include <stdlib.h> // atoi() atof() abs() system()
+#include <signal.h> // signal()
#include <simgear/compiler.h>
}
-std::deque<std::string> waitingTiles;
-typedef std::map<std::string,time_t> CompletedTiles;
+deque<string> waitingTiles;
+typedef map<string,time_t> CompletedTiles;
CompletedTiles completedTiles;
+netSocket theSocket;
#ifdef HAVE_SVN_CLIENT_H
}
}
+#ifdef _MSC_VER
+typedef void (__cdecl * sighandler_t)(int);
+#elif defined( __APPLE__ )
+typedef sig_t sighandler_t;
+#endif
+
+bool terminating = false;
+sighandler_t prior_signal_handlers[32];
+int termination_triggering_signals[] = {
+#ifndef _MSC_VER
+ SIGHUP, SIGINT, SIGQUIT, SIGKILL,
+#else
+ SIGINT, SIGILL, SIGFPE, SIGSEGV, SIGTERM, SIGBREAK, SIGABRT,
+#endif
+ 0}; // zero terminated
+
+void terminate_request_handler(int param) {
+ char msg[] = "\nReceived signal XX, intend to exit soon.\n"
+ "repeat the signal to force immediate termination.\n";
+ msg[17] = '0' + param / 10;
+ msg[18] = '0' + param % 10;
+ write(1, msg, sizeof(msg) - 1);
+ terminating = true;
+ signal(param, prior_signal_handlers[param]);
+ theSocket.close();
+}
+
const int nowhere = -9999;
+
// parse message
static void parse_message( const string &msg, int *lat, int *lon ) {
double dlat, dlon;
string::size_type pos = text.find( "$GPGGA" );
if ( pos == string::npos )
{
- *lat = -9999.0;
- *lon = -9999.0;
+ *lat = nowhere;
+ *lon = nowhere;
return;
}
string tmp = text.substr( pos + 7 );
void getWaitingTile() {
while ( !waitingTiles.empty() ) {
- CompletedTiles::iterator ii = completedTiles.find( waitingTiles.front() );
+ CompletedTiles::iterator ii =
+ completedTiles.find( waitingTiles.front() );
time_t now = time(0);
if ( ii == completedTiles.end() || ii->second + 600 < now ) {
sync_tree(waitingTiles.front().c_str());
int main( int argc, char **argv ) {
int port = 5501;
- char host[256] = ""; // accept messages from anyone
+ char host[256] = "localhost";
bool testing = false;
bool do_checkout(true);
int verbose(0);
// Must call this before any other net stuff
netInit( &argc,argv );
- netSocket s;
-
- if ( ! s.open( false ) ) { // open a UDP socket
+ if ( ! theSocket.open( false ) ) { // open a UDP socket
printf("error opening socket\n");
return -1;
}
- if ( s.bind( host, port ) == -1 ) {
+ if ( theSocket.bind( host, port ) == -1 ) {
printf("error binding to port %d\n", port);
return -1;
}
}
- while ( true ) { // main loop
+ for (int* sigp=termination_triggering_signals; *sigp; sigp++) {
+ prior_signal_handlers[*sigp] =
+ signal(*sigp, *terminate_request_handler);
+ if (verbose) cout << "Intercepted signal " << *sigp << endl;
+ }
+
+ while ( !terminating ) {
+ // main loop
recv_msg = false;
if ( testing ) {
// No FGFS communications
lon = -123;
recv_msg = (lat != last_lat) || (lon != last_lon);
} else {
- s.setBlocking(waitingTiles.empty());
- len = s.recv(msg, maxlen, 0);
+ if (verbose && waitingTiles.empty()) {
+ cout << "Idle; waiting for FlightGear position\n";
+ }
+ theSocket.setBlocking(waitingTiles.empty());
+ len = theSocket.recv(msg, maxlen, 0);
if (len >= 0) {
msg[len] = '\0';
recv_msg = true;
// Ignore messages where the location does not change
if ( lat != last_lat || lon != last_lon ) {
cout << "pos in msg = " << lat << "," << lon << endl;
- std::deque<std::string> oldRequests;
+ deque<string> oldRequests;
oldRequests.swap( waitingTiles );
int lat_dir, lon_dir, dist;
if ( last_lat == nowhere || last_lon == nowhere ) {
}
else if ( testing ) {
- exit( 0 );
+ terminating = true;
} else
ulSleep( 1 );
- } // while true
+ } // while !terminating
return 0;
}
ax = new float [ numaxes ] ;
activeaxes = numaxes;
- if( numaxes < 4 )
+ if( numaxes > 4 )
{
printf("max 4 axes joysticks supported at the moment, however %i axes were detected\nWill only use the first 4 axes!\n", numaxes);
activeaxes = 4;
js->read( &b, ax );
for ( axis = 0 ; axis < activeaxes ; axis++ )
{
- long axisvalue = (long int)(ax[axis]*2147483647.0);
- printf("axisval=%li\n", axisvalue);
- memcpy(packet+len, &axisvalue, 4);
- len+=4;
+ int32_t axisvalue = (int32_t)(ax[axis]*2147483647.0);
+ printf("axisval=%li\n", (long)axisvalue);
+ memcpy(packet+len, &axisvalue, sizeof(axisvalue));
+ len+=sizeof(axisvalue);
}
// fill emtpy values into packes when less than 4 axes
for( ; axis < 4; axis++ )
{
- long axisvalue = 0;
- memcpy(packet+len, &axisvalue, 4);
- len+=4;
+ int32_t axisvalue = 0;
+ memcpy(packet+len, &axisvalue, sizeof(axisvalue));
+ len+=sizeof(axisvalue);
}
- long int b_l = b;
- memcpy(packet+len, &b_l, 4);
- len+=4;
+ int32_t b_l = b;
+ memcpy(packet+len, &b_l, sizeof(b_l));
+ len+=sizeof(b_l);
- char termstr[5] = "\0\0\r\n";
- memcpy(packet+len, &termstr, 4);
+ const char * termstr = "\0\0\r\n";
+ memcpy(packet+len, termstr, 4);
len += 4;
c.send( packet, len, 0 );