]> git.mxchange.org Git - flightgear.git/commitdiff
Merge branch 'torsten/auto' into next
authorTim Moore <timoore33@gmail.com>
Tue, 9 Mar 2010 09:38:43 +0000 (10:38 +0100)
committerTim Moore <timoore33@gmail.com>
Tue, 9 Mar 2010 09:38:43 +0000 (10:38 +0100)
30 files changed:
Makefile.am
NEWS
README.OSG
configure.ac
projects/VC7.1/FlightGear.vcproj
projects/VC90/FlightGear/FlightGear.vcproj
src/AIModel/AIAircraft.cxx
src/AIModel/AIAircraft.hxx
src/ATC/trafficcontrol.cxx
src/ATC/trafficcontrol.hxx
src/Autopilot/route_mgr.cxx
src/Autopilot/route_mgr.hxx
src/GUI/Makefile.am
src/GUI/WaypointList.cxx [new file with mode: 0644]
src/GUI/WaypointList.hxx [new file with mode: 0644]
src/GUI/dialog.cxx
src/GUI/dialog.hxx
src/Include/config.h-msvc71
src/Instrumentation/HUD/HUD.hxx
src/Instrumentation/HUD/HUD_instrument.cxx
src/Instrumentation/HUD/HUD_misc.cxx
src/Instrumentation/gps.cxx
src/Instrumentation/navradio.cxx
src/Main/CameraGroup.cxx
src/MultiPlayer/multiplaymgr.cxx
src/Network/generic.cxx
src/Traffic/Schedule.cxx
src/Traffic/Schedule.hxx
src/Traffic/TrafficMgr.cxx
utils/TerraSync/terrasync.cxx

index 15ba35f43ce41e9d8290add4cae89e6cbb69a238..c86f1c148db55d173441589ad76e6ec8145c694f 100644 (file)
@@ -52,7 +52,7 @@ data-tar:
        --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 \
diff --git a/NEWS b/NEWS
index 0876a8d1d8cb5be112be1e9c40a381cce5eee48c..6590aeb49a452549b3c8f5a4be04394d96356715 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,80 @@
-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
index 58bc8de871633dc9c4dbd475344f6b07e9211064..b44695ad38cdd8d64eea45ff778b7485682fcb55 100644 (file)
@@ -3,8 +3,11 @@
 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:
 
@@ -25,3 +28,66 @@ ccmake .
 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'.
+
index d1600c1754c0955f3a6179b2b674b007c0bacc88..df8ba3dcb4b4647d7633865b493a8e28a75724fc 100644 (file)
@@ -512,7 +512,7 @@ if test "x$ac_cv_header_simgear_version_h" != "xyes"; then
     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>
 
@@ -521,8 +521,8 @@ AC_TRY_RUN([
 #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() {
@@ -627,88 +627,48 @@ dnl joystick lib
 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)
index 9d0a96735e1ce98161a85c04fa718d5a7d8dc647..388947077cb05e8ad41e840d4dfdeed7569a7999 100755 (executable)
                        <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"
index d85b591de5b5e6fb35e3fdbcc7397a8302504c48..44905801ae85313bd759bca66467485f172d5401 100644 (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"
index eb8469fdece8dd419a721b5c7262caf3ddc9a6af..2bf71b3b18b96b35a66462bce9f0fe2c9e282e97 100644 (file)
@@ -51,9 +51,6 @@ using std::string;
 
 static string tempReg;
 
-class AI_OutOfSight{};
-class FP_Inactive{};
-
 FGAIAircraft::FGAIAircraft(FGAISchedule *ref) : FGAIBase(otAircraft) {
     trafficRef = ref;
     if (trafficRef) {
@@ -148,16 +145,16 @@ void FGAIAircraft::setPerformance(const std::string& acclass) {
 
  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
@@ -739,7 +736,7 @@ void FGAIAircraft::controlSpeed(FGAIFlightPlan::waypoint* curr, FGAIFlightPlan::
 /**
  * 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
@@ -760,16 +757,16 @@ void FGAIAircraft::updatePrimaryTargetValues() {
         }
         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
index 49639799a19de71d367330e0f201e5a64517dfb0..6a53e5a2bf32238b4c61fa6f81e5bfd6a179507e 100644 (file)
@@ -29,7 +29,6 @@
 #include <ATC/trafficcontrol.hxx>
 
 #include <string>
-using std::string;
 
 class PerformanceData;
 
@@ -66,8 +65,8 @@ public:
     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);
@@ -75,8 +74,8 @@ public:
 
     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; };
@@ -88,7 +87,7 @@ public:
     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);
@@ -121,7 +120,9 @@ private:
     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();
@@ -134,9 +135,9 @@ private:
 
     double sign(double x);
 
-    string acType;
-    string company;
-    string transponderCode;
+    std::string acType;
+    std::string company;
+    std::string transponderCode;
 
     int spinCounter;
     double prevSpeed;
index ef5a0a01f8dac2c2d767c1c1b040164a3d4dc093..f9c2fee4e5fc6ef900774085e07a755e1f5fe533 100644 (file)
@@ -45,6 +45,7 @@ FGTrafficRecord::FGTrafficRecord() :
    speed(0), 
    altitude(0), 
    radius(0),
+   frequencyId(0),
    allowTransmission(true) {
 }
 
@@ -354,45 +355,8 @@ void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir m
     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;
@@ -403,6 +367,30 @@ void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir m
     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";
@@ -457,6 +445,10 @@ void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir m
                       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;
@@ -882,18 +874,31 @@ void FGStartupController::update(int id, double lat, double lon, double heading,
              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);
index 027f63d7ff6be2db374f29e4f50d1a5de1b85691..f97e7cf22928549228599d065cade4fd519c0aaa 100644 (file)
@@ -107,6 +107,7 @@ private:
   int id, waitsForId;
   int currentPos;
   int leg;
+  int frequencyId;
   int state;
   bool allowTransmission;
   time_t timer;
@@ -176,6 +177,8 @@ public:
   bool allowTransmissions() { return allowTransmission; };
   void suppressRepeatedTransmissions () { allowTransmission=false; };
   void allowRepeatedTransmissions () { allowTransmission=true; };
+  void nextFrequency() { frequencyId++; };
+  int  getNextFrequency() { return frequencyId; };
 };
 
 typedef vector<FGTrafficRecord> TrafficVector;
@@ -223,7 +226,8 @@ public:
       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;
index d6d65cc208331f6e59f1cb940ffeae4dc518ada9..64263dc25749eedfc0913bef2722a6548293da71 100644 (file)
@@ -172,6 +172,7 @@ void FGRouteMgr::init() {
   wpn->getChild("eta", 0, true);
   
   _route->clear();
+  _route->set_current(0);
   update_mirror();
   
   _pathNode = fgGetNode(RM "file-path", 0, true);
@@ -285,18 +286,33 @@ void FGRouteMgr::setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDi
     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();
@@ -313,10 +329,7 @@ SGWayPoint FGRouteMgr::pop_waypoint( int n ) {
   SGWayPoint wp = _route->get_waypoint(n);
   _route->delete_waypoint(n);
     
-  update_mirror();
-  _edited->fireValueChanged();
-  checkFinished();
-  
+  waypointsChanged();
   return wp;
 }
 
@@ -467,9 +480,15 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop)
       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;
@@ -529,16 +548,6 @@ bool FGRouteMgr::activate()
   }
 
   _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;
@@ -576,11 +585,6 @@ bool FGRouteMgr::checkFinished()
 
 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");
@@ -637,6 +641,16 @@ int FGRouteMgr::currentWaypoint() const
   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());
index b096572591533419f4a429cedde07d820c64240a..a8168924807a5ec582759dfd63becd271d527ce6 100644 (file)
@@ -113,6 +113,12 @@ private:
      */
     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();
     
@@ -188,6 +194,11 @@ public:
      */
     void jumpToIndex(int index);
     
+    /**
+     * 
+     */
+    void setWaypointTargetAltitudeFt(unsigned int index, int altFt);
+    
     void saveRoute();
     void loadRoute();
 };
index 890807f2a9734b5cc12f2d4d673784cbc273d6b6..5f8c98d0bf3539b22e61b90fdd670be5f75a0f40 100644 (file)
@@ -16,14 +16,15 @@ endif
 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
diff --git a/src/GUI/WaypointList.cxx b/src/GUI/WaypointList.cxx
new file mode 100644 (file)
index 0000000..5498076
--- /dev/null
@@ -0,0 +1,771 @@
+
+
+#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, &currentColor, 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, &currentColor, 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());
+}
+
diff --git a/src/GUI/WaypointList.hxx b/src/GUI/WaypointList.hxx
new file mode 100644 (file)
index 0000000..165a1da
--- /dev/null
@@ -0,0 +1,171 @@
+/**
+ * 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
index e20a62b930015edc48be097d61e62b1215a7e781..698268ce92dd16c5da88306ab10be7d7a8dd88d8 100644 (file)
@@ -13,7 +13,7 @@
 #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;
@@ -569,9 +569,14 @@ FGDialog::updateValues (const char *objectName)
     
     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();
@@ -873,6 +878,10 @@ FGDialog::makeObject (SGPropertyNode *props, int parentWidth, int parentHeight)
         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;
     }
index 63aef704b1d2ec3354c7b4261749a2054db43515..e08dbe75ae2c414d3d5edc5b927dda34ea3f2f35 100644 (file)
@@ -22,6 +22,8 @@ using std::vector;
 #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; };
 
 
index 0e7fc3835fc08156cec96f9d349bd5571094294f..0e2c75d62299175cfe9bc8f9cb06c934f63b49d5 100755 (executable)
 
 #define NOMINMAX 1
 
+#define HAVE_CULLSETTINGS_CLEAR_MASK 1
+
 #ifndef ENABLE_AUDIO_SUPPORT
 #define  ENABLE_AUDIO_SUPPORT
 #endif
index fbf4baed34c7fc16333ad7fed9317cafd3175e19..6776495f26478547ca855dc592494a6d93578a01 100644 (file)
@@ -357,6 +357,7 @@ protected:
     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;
@@ -610,9 +611,25 @@ public:
 
 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;
+
 };
 
 
index a6769c60945f0b97408c8964a9f6ff91a9206912..630837581e7771a37fabac9a414cdb53f45157d4 100644 (file)
@@ -118,6 +118,20 @@ void HUD::Item::draw_circle(float xoffs, float yoffs, float r) const
     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)
 {
index 021e22ba2208698c737b15b5f680d2ebf27aa313..7b084c68bf5e403988a95c658b5eac7d3f67093a 100644 (file)
 
 #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),
+_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)),
+_compression(n->getFloatValue("compression-factor")),
+_limit_x(n->getFloatValue("limit-x")),
+_limit_y(n->getFloatValue("limit-y")),
+_active_condition(0),
+_tachy_condition(0),
+_align_condition(0),
+_bullet_size(_w / 6.0),
+_inner_radius(_w / 2.0)
+
 {
     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
     }
+
 }
 
 
index 2c6f36495882344acf8d2b10ddfe32bfaec72c66..2f7455b83820b022ffbe382d2fdf0935b0df7e93 100644 (file)
@@ -1186,7 +1186,9 @@ double GPS::getWP1MagBearing() const
     return -9999.0;
   }
 
-  return _wp1TrueBearing - _magvar_node->getDoubleValue();
+  double magBearing = _wp1TrueBearing - _magvar_node->getDoubleValue();
+  SG_NORMALIZE_RANGE(magBearing, 0.0, 360.0);
+  return magBearing;
 }
 
 double GPS::getWP1CourseDeviation() const
index c1479d89634e02c9f9782720c0adf54a4ec342c7..0740090ddafb26ca2ccb5ef38c2a4d532ddc6940 100644 (file)
@@ -917,7 +917,10 @@ void FGNavRadio::search()
 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);
index 57371b1d87bb4feb49fa2eb5457643282409fa9f..954a591e55d0c41ad761b2772d557aec8eda639b 100644 (file)
@@ -202,7 +202,7 @@ void CameraGroup::update(const osg::Vec3d& position,
                 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;
index 16c0a6e5f53024ea20f1351fac970a22f71419af..fe1f4c399912b3df7c7efb20f2a82f39ddf3aae5 100644 (file)
@@ -158,6 +158,8 @@ FGMultiplayMgr::sIdPropertyList[] = {
   {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},
 
@@ -383,7 +385,7 @@ FGMultiplayMgr::init (void)
     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 {
@@ -393,7 +395,7 @@ FGMultiplayMgr::init (void)
       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);
   }
@@ -408,14 +410,14 @@ FGMultiplayMgr::init (void)
            // 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;
   }
@@ -527,7 +529,7 @@ FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo)
   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;
   }
 
@@ -737,7 +739,7 @@ FGMultiplayMgr::Update(void)
       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;
     }
@@ -752,17 +754,17 @@ FGMultiplayMgr::Update(void)
     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;
     }
@@ -782,7 +784,7 @@ FGMultiplayMgr::Update(void)
     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;
     }
@@ -813,7 +815,7 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
 {
   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;
   }
@@ -928,7 +930,7 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
 
         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;
       }            
@@ -963,7 +965,7 @@ FGMultiplayMgr::ProcessChatMsg(const MsgBuf& Msg,
 {
   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;
   }
index a91f7835eba45a8a2d56d5427b6d20eebfa509ee..423d2be189b28ffec2de641415dc99ab3b1bdcfb 100644 (file)
@@ -240,8 +240,8 @@ bool FGGeneric::gen_message_ascii() {
 
         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
@@ -467,25 +467,24 @@ bool FGGeneric::process() {
                 }
             }
         } 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;
index 309d9340b1b0b9694d494a401b00424070942766..517bb3c2117b5e6e1b0000ec91bcc833672b85e7 100644 (file)
@@ -55,8 +55,6 @@
 #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. 
@@ -67,8 +65,6 @@ FGAISchedule::FGAISchedule()
   AIManagerRef = 0;
 
   heavy = false;
-  lat = 0;
-  lon = 0;
   radius = 0;
   groundOffset = 0;
   distanceToUser = 0;
@@ -110,8 +106,6 @@ FGAISchedule::FGAISchedule(string model,
   airline          = arln;
   m_class          = mclass;
   flightType       = fltpe;
-  lat              = 0;
-  lon              = 0;
   radius           = rad;
   groundOffset     = grnd;
   distanceToUser   = 0;
@@ -134,8 +128,6 @@ FGAISchedule::FGAISchedule(const FGAISchedule &other)
   heavy              = other.heavy;
   flightIdentifier   = other.flightIdentifier;
   flights            = other.flights;
-  lat                = other.lat;
-  lon                = other.lon;
   AIManagerRef       = other.AIManagerRef;
   acType             = other.acType;
   airline            = other.airline;
@@ -186,308 +178,232 @@ bool FGAISchedule::init()
   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 &currentDestination,
@@ -513,7 +429,7 @@ FGScheduledFlight* FGAISchedule::findAvailableFlight (const string &currentDesti
            //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++;
@@ -548,7 +464,7 @@ FGScheduledFlight* FGAISchedule::findAvailableFlight (const string &currentDesti
      // is departure port valid?
      // is arrival port valid?
      //cerr << "Ack no flight found: " << endl;
-     return 0;
+     return NULL;
 }
 
 double FGAISchedule::getSpeed()
index 4dd8d5f001a16eec3f145313ff27c106bc91cbd1..75661bceb5fe51dae5205b2b418d7dde39ad6f68 100644 (file)
@@ -48,8 +48,7 @@ class FGAISchedule
   string currentDestination;
   bool heavy;
   FGScheduledFlightVec flights;
-  float lat;
-  float lon; 
+  SGGeod position;
   double radius;
   double groundOffset;
   double distanceToUser;
@@ -58,7 +57,14 @@ class FGAISchedule
   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, 
@@ -79,7 +85,7 @@ class FGAISchedule
 
   ~FGAISchedule(); //destructor
 
-  bool update(time_t now);
+  bool update(time_t now, const SGVec3d& userCart);
   bool init();
 
   double getSpeed         ();
index 899685e938fc96124f807ec1436f0aad7d7b891c..dc2444fbaa373834b93ffc76f57fd1b74c5ce805 100644 (file)
@@ -142,11 +142,16 @@ void FGTrafficManager::update(double /*dt*/)
   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.
index e586632dda8cd371d52a30e4c95d5ac3f8581bbe..f6c33c9b64c3e393ae6f2749c127dc22d88a43e8 100644 (file)
 #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>
 
@@ -109,9 +112,10 @@ static void usage( const string& prog ) {
 
 }
 
-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
 
@@ -269,9 +273,37 @@ void sync_tree(const char* dir) {
     }
 }
 
+#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;
@@ -281,8 +313,8 @@ static void parse_message( const string &msg, int *lat, int *lon ) {
     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 );
@@ -408,7 +440,8 @@ static void sync_areas( int lat, int lon, int lat_dir, int lon_dir ) {
 
 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());
@@ -422,7 +455,7 @@ void getWaitingTile() {
 
 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);
@@ -486,14 +519,12 @@ int main( int argc, char **argv ) {
     // 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;
     }
@@ -524,7 +555,14 @@ int main( int argc, char **argv ) {
     }
 
 
-    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
@@ -532,8 +570,11 @@ int main( int argc, char **argv ) {
             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;
@@ -546,7 +587,7 @@ int main( int argc, char **argv ) {
              // 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 ) {
@@ -584,11 +625,11 @@ int main( int argc, char **argv ) {
         }
 
        else if ( testing ) {
-           exit( 0 );
+           terminating = true;
        } else
 
         ulSleep( 1 );
-    } // while true
+    } // while !terminating
         
     return 0;
 }