--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 \
-Version 1.99.5
-* October 30, 2008 (source code snapshot release)
+Version 2.0.0 - February 25, 2010
+
+ FlightGear 2.0.0. reflects the maturation of the OpenSceneGraph port
+ that started with the previous 1.9.0 release. In addition to many
+ internal code improvements, FlightGear 2.0.0. marks the introduction
+ of many new exciting improvements in the graphics and sound system,
+ as well as improved usability of key features, and improved behavior
+ of exsisting features. Highlights of this new version include:
+ Dramatic new 3D clouds, dramatic lighting conditions, improved
+ support for custom scenery, and many many new and detailed aircraft
+ models.
+
+ Sound
+ * Complete overhaul of the sound code
+ * doppler effects
+ * distance attenuation
+ * 3D positional sound sources
+ * assignment of sound sources to external objects (i.e. AI controlled
+ aircraft)
+ * User selection of the sound device
+
+ Visual Effects
+ * Use of Shaders for dynamic textures
+ * Use of Effects files
+ * Improved 3D clouds
+ * Color changes based on humidity and other weather effects allow for very
+ dramatic lighting conditions
+ * Dynamic water textures
+ * Text animation based on OSGText
+
+ Usability
+ * Allow screenshots in more common file formats
+ * User selectable sound device
+ * More intuitive selection of the weather settings through the GUI and/or
+ commandline
+
+ Infrastructure
+ * Airport geometry data can be read from the scenery, allowing for more
+ flexible regeneration of terrain tiles
+
+ Internals
+ * Improved efficiency of the property tree
+ * A more efficient ground cache
+ * Many improvements to the route management code
+ * Removed many compiler warnings
+ * More realistic atmosphere model
+
+ Behavior
+ * More realistic ILS behavior
+ * Autopilot improvements
+ * A generic autobrake function
+ * Winds over mountainous areas cause up- and downdrafts that can be used
+ for gliding
+ * More realistic behavior of the route manager
+ * Wild fires, which can be extinguished by firefighter aircraft operating
+ across the multplayer server
+ * Navaid frequencies and radials can be transmitted to Atlas
+
+ Utilities
+ * A python script to visualize Yasim configuration files in Blender
+
+ AI
+ * Allow traffic departing and arriving at the same airport
+ * Add Ground Vehicles - including automobiles, trucks, articulated trucks,
+ trains (including high speed trains)
+ * ATC interactions between AI aircraft and ground controllers
+ * Performance characteristics of AI aircraft can be specified in a
+ performance database
+ * Push-back vehicles are available for a selected number of aircraft
+ * Add escorts for AI carrier - frigates, guided missile cruiser, amphibious
+ warfare ships now make up the Vinson Battle Group
+ * Improved radar functionality - now detects AI escorts etc.
+ * AI objects are now solid (i.e. users can collide with them)
+ * Some preliminary support for SID/STAR procedures for AI aircraft
+
+Version 1.9.0
+* December 20, 2008 (source code snapshot release)
New in 0.9.10
You *must* have OpenSceneGraph (OSG) installed to build this version of
FlightGear.
-Notice that FlightGear 1.9.0 requires at least version 2.7.8. Using earlier
-versions of OSG will yield serious rendering bugs.
+Notice that from FlightGear version 1.9.0, OSG version 2.7.8 or later
+is required. Using earlier versions of OSG will yield serious
+rendering bugs. If you are using an 'older' distribution, this may mean
+a suitable version of OSG may not be availble through the usual package
+repositories.
You can get the latest version of OSG from:
make
sudo make install
+Also later release versions of OpenSceneGraph can be obtained by
+svn, or you can use the OSG development svn 'trunk', but be warned,
+OSG is always in heavy development, and at certain moments
+in time, it may not compile completely, so, as usual, it is
+recommended that you stay with released versions.
+
+Installation notes:
+
+In some unix/linux distributions, particularly 64-bit
+systems, OSG may install its shared libraries in other than
+/usr/lib, /usr/local/lib or $prefix/lib!
+
+This does not seem to effect binary installation, which is
+to $prefix/bin, nor header installation, which remains
+$prefix/include. Just the shared libraries, and perhaps
+only for 64-bit systems, or higher as, and when available.
+
+The default is /usr/local/lib64 or $prefix/lib64 in
+64-bit systems. This may cause problems with the auto-conf
+tools when configuring and compiling FlighGear, since
+even using the configure option --with-osg=$prefix
+will not 'fix' the problem.
+
+The are various ways to deal with this, which mainly depend
+on whether you just want one version of OSG 'globally'
+installed, or desire to be able to build, and run, FlightGear
+against 'different' versions of OSG.
+
+There is a parameter, -D LIB_POSTFIX= or -D LIB_POSTFIX=""
+which can be passed to cmake OSG to force the OSG library
+installation into the 'standard' "$prefix/lib".
+
+OSG cmake advises of a post installation step -
+ $ sudo make install_ld_conf
+which, if available, will add an openscenegraph.conf file
+to the /etc/ld.so.conf.d folder, with the line -
+${CMAKE_INSTALL_PREFIX}lib${LIB_POSTFIX}
+and run 'ldconfig' to add this to the 'cache'. But this
+option does not always seem available.
+
+Configuring SimGear and Flightgear notes:
+
+If you install OSG in other than the 'standard' directories,
+you must add --with-osg=$prefix when configuring
+SimGear and FlightGear. This will cause the auto-conf
+tools to look in $prefix/include for headers, and
+$prefix/lib for libraries, but _NOT_ in 'lib64'!
+
+If the OSG is installed to a unique $prefix directory, then
+it is also possible to make a 'link' entry lib -> lib64 to
+get over this, but obviously this is not available if
+$prefix/lib already exists, and contains entries.
+
+Running fgfs, and others, with OSG shared libraries:
+
+You must also deal with the executable loader being
+able to find the OSG shared libraries when running fgfs,
+and others, through perhaps using :-
+ $ export LD_LIBRARY_PATH=/path/to/osg/lib[64][:/other/paths]
+or more permanently using a '.conf' file in the
+/etc/ld.so.conf.d directory, and running
+ $ sudo ldconfig -v to update the 'cache'.
+
exit
fi
-AC_MSG_CHECKING([for SimGear 1.9.0 or newer])
+AC_MSG_CHECKING([for SimGear 2.0.0 or newer])
AC_TRY_RUN([
#include <stdio.h>
#define STRINGIFY(X) XSTRINGIFY(X)
#define XSTRINGIFY(X) #X
-#define MIN_MAJOR 1
-#define MIN_MINOR 9
+#define MIN_MAJOR 2
+#define MIN_MINOR 0
#define MIN_MICRO 0
int main() {
AC_DEFINE([ENABLE_PLIB_JOYSTICK], 1, [Define to enable plib joystick support])
# Find the OSG libraries. Note special handling for OS X frameworks
-case "${host}" in
-*-apple-darwin*)
- if test "x$with_osg_framework" = "x"; then
- if test "x$enable_osgdebug" = "xyes"; then
- # debug version of osg libs
- AC_CHECK_LIB(OpenThreadsd,OpenThreadsGetVersion)
- openthreads_LIBS="$LIBS"
- LIBS=""
- AC_CHECK_LIB(osgd,osgGetVersion)
- AC_CHECK_LIB(osgUtild,osgUtilGetVersion)
- AC_CHECK_LIB(osgDBd,osgDBGetVersion)
- AC_CHECK_LIB(osgTextd,osgTextGetVersion)
- AC_CHECK_LIB(osgGAd,osgGAGetVersion)
- AC_CHECK_LIB(osgViewerd,osgViewerGetVersion)
- AC_CHECK_LIB(osgSimd,osgSimGetVersion)
- AC_CHECK_LIB(osgParticled,osgParticleGetVersion)
- OSG_LIBS="$LIBS -losgFXd $openthreads_LIBS"
- LIBS=""
- else
- # release version of osg libs
- AC_CHECK_LIB(OpenThreads,OpenThreadsGetVersion)
- openthreads_LIBS="$LIBS"
- LIBS=""
- AC_CHECK_LIB(osg,osgGetVersion)
- AC_CHECK_LIB(osgUtil,osgUtilGetVersion)
- AC_CHECK_LIB(osgDB,osgDBGetVersion)
- AC_CHECK_LIB(osgText,osgTextGetVersion)
- AC_CHECK_LIB(osgGA,osgGAGetVersion)
- AC_CHECK_LIB(osgViewer,osgViewerGetVersion)
- AC_CHECK_LIB(osgSim,osgSimGetVersion)
- AC_CHECK_LIB(osgParticle,osgParticleGetVersion)
- OSG_LIBS="$LIBS -losgFX $openthreads_LIBS"
- LIBS=""
- # echo $LIBS
- fi
- AC_SUBST(openthreads_LIBS)
- AC_SUBST(OSG_LIBS)
- else
- # Checking osg frameworks.
- AC_CHECK_FRAMEWORK(osgViewer, [#include <osgViewer/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgGA, [#include <osgGA/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgText, [#include <osgText/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgFX, [#include <osgFX/AnisotropicLighting>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgUtil, [#include <osgUtil/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgDB, [#include <osgDB/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgSim, [#include <osgSim/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osgParticle, [#include <osgParticle/Version>], $with_osg_framework)
- AC_CHECK_FRAMEWORK(osg, [#include <osg/Version>], $with_osg_framework)
- osg_FRAMEWORKS="$FRAMEWORKS"
- FRAMEWORKS=""
- AC_CHECK_FRAMEWORK(OpenThreads, [#include <OpenThreads/Version>], $with_osg_framework)
- openthreads_FRAMEWORK="$FRAMEWORKS"
- AC_SUBST(osg_FRAMEWORKS)
- AC_SUBST(openthreads_FRAMEWORK)
- fi
- ;;
-*)
+if test "x$with_osg_framework" = "x"; then
if test "x$enable_osgdebug" = "xyes"; then
- AC_CHECK_LIB(OpenThreadsd,OpenThreadsGetVersion)
- AC_CHECK_LIB(osgd,osgGetVersion)
- AC_CHECK_LIB(osgUtild,osgUtilGetVersion)
- AC_CHECK_LIB(osgDBd,osgDBGetVersion)
- AC_CHECK_LIB(osgTextd,osgTextGetVersion)
- AC_CHECK_LIB(osgGAd,osgGAGetVersion)
- AC_CHECK_LIB(osgViewerd,osgViewerGetVersion)
- AC_CHECK_LIB(osgSimd,osgSimGetVersion)
- AC_CHECK_LIB(osgParticled,osgParticleGetVersion)
- LIBS="$LIBS -losgFXd $opengl_LIBS"
+ AC_CHECK_LIB(OpenThreadsd,OpenThreadsGetVersion, , [AC_MSG_ERROR(OpenThreads library not found.)],)
+ AC_CHECK_LIB(osgd,osgGetVersion, , [AC_MSG_ERROR(OpenSceneGraph library not found.)],)
+ AC_CHECK_LIB(osgUtild,osgUtilGetVersion, , [AC_MSG_ERROR(OpenSceneGraph utility library not found.)],)
+ AC_CHECK_LIB(osgDBd,osgDBGetVersion, , [AC_MSG_ERROR(OpenSceneGraph database library not found.)],)
+ AC_CHECK_LIB(osgTextd,osgTextGetVersion, , [AC_MSG_ERROR(OpenSceneGraph Text library not found.)],)
+ AC_CHECK_LIB(osgGAd,osgGAGetVersion, , [AC_MSG_ERROR(OpenSceneGraph GUI Abstraction library not found.)],)
+ AC_CHECK_LIB(osgViewerd,osgViewerGetVersion, , [AC_MSG_ERROR(OpenSceneGraph Viewer library not found.)],)
+ AC_CHECK_LIB(osgSimd,osgSimGetVersion, , [AC_MSG_ERROR(OpenSceneGraph simulation library not found.)],)
+ AC_CHECK_LIB(osgParticled,osgParticleGetVersion, , [AC_MSG_ERROR(OpenSceneGraph Particle library not found.)],)
+ AC_CHECK_LIB(osgFXd, osgFXGetVersion, , [AC_MSG_ERROR(OpenSceneGraph FX library not found.)],)
else
- AC_CHECK_LIB(OpenThreads,OpenThreadsGetVersion)
- AC_CHECK_LIB(osg,osgGetVersion)
- AC_CHECK_LIB(osgUtil,osgUtilGetVersion)
- AC_CHECK_LIB(osgDB,osgDBGetVersion)
- AC_CHECK_LIB(osgText,osgTextGetVersion)
- AC_CHECK_LIB(osgGA,osgGAGetVersion)
- AC_CHECK_LIB(osgViewer,osgViewerGetVersion)
- AC_CHECK_LIB(osgSim,osgSimGetVersion)
- AC_CHECK_LIB(osgParticle,osgParticleGetVersion)
- LIBS="$LIBS -losgFX $opengl_LIBS"
+ AC_CHECK_LIB(OpenThreads,OpenThreadsGetVersion, , [AC_MSG_ERROR(OpenThreads library not found.)],)
+ AC_CHECK_LIB(osg,osgGetVersion, , [AC_MSG_ERROR(OpenSceneGraph library not found.)],)
+ AC_CHECK_LIB(osgUtil,osgUtilGetVersion, , [AC_MSG_ERROR(OpenSceneGraph utility library not found.)],)
+ AC_CHECK_LIB(osgDB,osgDBGetVersion, , [AC_MSG_ERROR(OpenSceneGraph database library not found.)],)
+ AC_CHECK_LIB(osgText,osgTextGetVersion, , [AC_MSG_ERROR(OpenSceneGraph Text library not found.)],)
+ AC_CHECK_LIB(osgGA,osgGAGetVersion, , [AC_MSG_ERROR(OpenSceneGraph GUI Abstraction library not found.)],)
+ AC_CHECK_LIB(osgViewer,osgViewerGetVersion, , [AC_MSG_ERROR(OpenSceneGraph Viewer library not found.)],)
+ AC_CHECK_LIB(osgSim,osgSimGetVersion, , [AC_MSG_ERROR(OpenSceneGraph simulation library not found.)],)
+ AC_CHECK_LIB(osgParticle,osgParticleGetVersion, , [AC_MSG_ERROR(OpenSceneGraph Particle library not found.)],)
+ AC_CHECK_LIB(osgFX, osgFXGetVersion, , [AC_MSG_ERROR(OpenSceneGraph FX library not found.)],)
fi
- ;;
-esac
+else
+ # Checking osg frameworks.
+ AC_CHECK_FRAMEWORK(osgViewer, [#include <osgViewer/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgGA, [#include <osgGA/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgText, [#include <osgText/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgFX, [#include <osgFX/AnisotropicLighting>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgUtil, [#include <osgUtil/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgDB, [#include <osgDB/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgSim, [#include <osgSim/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osgParticle, [#include <osgParticle/Version>], $with_osg_framework)
+ AC_CHECK_FRAMEWORK(osg, [#include <osg/Version>], $with_osg_framework)
+ osg_FRAMEWORKS="$FRAMEWORKS"
+ FRAMEWORKS=""
+ AC_CHECK_FRAMEWORK(OpenThreads, [#include <OpenThreads/Version>], $with_osg_framework)
+ openthreads_FRAMEWORK="$FRAMEWORKS"
+ AC_SUBST(osg_FRAMEWORKS)
+ AC_SUBST(openthreads_FRAMEWORK)
+fi
AM_CONDITIONAL(HAVE_FRAMEWORK_OSG, test "x$ac_cv_framework_osg" != "x")
AC_CHECK_HEADER(osg/Version)
<File
RelativePath="..\..\src\GUI\SafeTexFont.hxx">
</File>
+ <File RelativePath="..\..\..\src\GUI\WaypointList.cxx"></File>
+ <File RelativePath="..\..\..\src\GUI\WaypointList.hxx"></File>
</Filter>
<Filter
Name="Lib_Input"
RelativePath="..\..\..\src\GUI\SafeTexFont.hxx"
>
</File>
+ <File RelativePath="..\..\..\src\GUI\WaypointList.cxx"></File>
+ <File RelativePath="..\..\..\src\GUI\WaypointList.hxx"></File>
</Filter>
<Filter
Name="Lib_Input"
static string tempReg;
-class AI_OutOfSight{};
-class FP_Inactive{};
-
FGAIAircraft::FGAIAircraft(FGAISchedule *ref) : FGAIBase(otAircraft) {
trafficRef = ref;
if (trafficRef) {
void FGAIAircraft::Run(double dt) {
FGAIAircraft::dt = dt;
-
- try {
- updatePrimaryTargetValues(); // target hdg, alt, speed
- }
- catch (AI_OutOfSight) {
- return;
+
+ bool outOfSight = false,
+ flightplanActive = true;
+ updatePrimaryTargetValues(flightplanActive, outOfSight); // target hdg, alt, speed
+ if (outOfSight) {
+ return;
}
- catch (FP_Inactive) {
- //return;
- groundTargetSpeed = 0;
+
+ if (!flightplanActive) {
+ groundTargetSpeed = 0;
}
handleATCRequests(); // ATC also has a word to say
/**
* Update target values (heading, alt, speed) depending on flight plan or control properties
*/
-void FGAIAircraft::updatePrimaryTargetValues() {
+void FGAIAircraft::updatePrimaryTargetValues(bool& flightplanActive, bool& aiOutOfSight) {
if (fp) // AI object has a flightplan
{
//TODO make this a function of AIBase
}
if (trafficRef) {
//cerr << trafficRef->getRegistration() << " Setting altitude to " << altitude_ft;
- if (! aiTrafficVisible()) {
+ aiOutOfSight = !aiTrafficVisible();
+ if (aiOutOfSight) {
setDie(true);
//cerr << trafficRef->getRegistration() << " is set to die " << endl;
- throw AI_OutOfSight();
+ aiOutOfSight = true;
+ return;
}
}
timeElapsed = now - fp->getStartTime();
- if (! fp->isActive(now)) {
- throw FP_Inactive();
- }
+ flightplanActive = fp->isActive(now);
} else {
// no flight plan, update target heading, speed, and altitude
// from control properties. These default to the initial
#include <ATC/trafficcontrol.hxx>
#include <string>
-using std::string;
class PerformanceData;
void doGroundAltitude();
bool loadNextLeg ();
- void setAcType(const string& ac) { acType = ac; };
- void setCompany(const string& comp) { company = comp;};
+ void setAcType(const std::string& ac) { acType = ac; };
+ void setCompany(const std::string& comp) { company = comp;};
void announcePositionToController(); //TODO have to be public?
void processATC(FGATCInstruction instruction);
virtual const char* getTypeString(void) const { return "aircraft"; }
- string GetTransponderCode() { return transponderCode; };
- void SetTransponderCode(string tc) { transponderCode = tc;};
+ std::string GetTransponderCode() { return transponderCode; };
+ void SetTransponderCode(const std::string& tc) { transponderCode = tc;};
// included as performance data needs them, who else?
inline PerformanceData* getPerformance() { return _performance; };
inline double getVerticalSpeed() const { return vs; };
inline double altitudeAGL() const { return props->getFloatValue("position/altitude-agl-ft");};
inline double airspeed() const { return props->getFloatValue("velocities/airspeed-kt");};
- string atGate();
+ std::string atGate();
protected:
void Run(double dt);
void controlHeading(FGAIFlightPlan::waypoint* curr);
void controlSpeed(FGAIFlightPlan::waypoint* curr,
FGAIFlightPlan::waypoint* next);
- void updatePrimaryTargetValues();
+
+ void updatePrimaryTargetValues(bool& flightplanActive, bool& aiOutOfSight);
+
void updateSecondaryTargetValues();
void updatePosition();
void updateHeading();
double sign(double x);
- string acType;
- string company;
- string transponderCode;
+ std::string acType;
+ std::string company;
+ std::string transponderCode;
int spinCounter;
double prevSpeed;
speed(0),
altitude(0),
radius(0),
+ frequencyId(0),
allowTransmission(true) {
}
string sender, receiver;
int stationFreq = 0;
int taxiFreq = 0;
+ int freqId = 0;
string atisInformation;
- //double commFreqD;
- switch (msgDir) {
- case ATC_AIR_TO_GROUND:
- sender = rec->getAircraft()->getTrafficRef()->getCallSign();
- switch (rec->getLeg()) {
- case 2:
- case 3:
- stationFreq =
- rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(rec->getLeg());
- taxiFreq =
- rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(3);
- receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
- atisInformation = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getAtisInformation();
- break;
- case 4:
- receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
- break;
- }
- break;
- case ATC_GROUND_TO_AIR:
- receiver = rec->getAircraft()->getTrafficRef()->getCallSign();
- switch (rec->getLeg()) {
- case 2:
- case 3:
- stationFreq =
- rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(rec->getLeg());
- taxiFreq =
- rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(3);
- sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
- atisInformation = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getAtisInformation();
- break;
-
- case 4:
- sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
- break;
- }
- break;
- }
string text;
string taxiFreqStr;
double heading = 0;
string transponderCode;
FGAIFlightPlan *fp;
string fltRules;
+
+ //double commFreqD;
+ sender = rec->getAircraft()->getTrafficRef()->getCallSign();
+ switch (rec->getLeg()) {
+ case 2:
+ case 3:
+ freqId = rec->getNextFrequency();
+ stationFreq =
+ rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(rec->getLeg()+freqId);
+ taxiFreq =
+ rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(3);
+ receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
+ atisInformation = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getAtisInformation();
+ break;
+ case 4:
+ receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
+ break;
+ }
+ // Swap sender and receiver value in case of a ground to air transmission
+ if (msgDir == ATC_GROUND_TO_AIR) {
+ string tmp = sender;
+ sender = receiver;
+ receiver = tmp;
+ }
switch (msgId) {
case MSG_ANNOUNCE_ENGINE_START:
text = sender + ". Ready to Start up";
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;
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);
int id, waitsForId;
int currentPos;
int leg;
+ int frequencyId;
int state;
bool allowTransmission;
time_t timer;
bool allowTransmissions() { return allowTransmission; };
void suppressRepeatedTransmissions () { allowTransmission=false; };
void allowRepeatedTransmissions () { allowTransmission=true; };
+ void nextFrequency() { frequencyId++; };
+ int getNextFrequency() { return frequencyId; };
};
typedef vector<FGTrafficRecord> TrafficVector;
MSG_ACKNOWLEDGE_ENGINE_START,
MSG_REQUEST_PUSHBACK_CLEARANCE,
MSG_PERMIT_PUSHBACK_CLEARANCE,
- MSG_HOLD_PUSHBACK_CLEARANCE } AtcMsgId;
+ MSG_HOLD_PUSHBACK_CLEARANCE,
+ MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY } AtcMsgId;
typedef enum {
ATC_AIR_TO_GROUND,
ATC_GROUND_TO_AIR } AtcMsgDir;
wpn->getChild("eta", 0, true);
_route->clear();
+ _route->set_current(0);
update_mirror();
_pathNode = fgGetNode(RM "file-path", 0, true);
aProp->setStringValue( eta_str );
}
-void FGRouteMgr::add_waypoint( const SGWayPoint& wp, int n ) {
+void FGRouteMgr::add_waypoint( const SGWayPoint& wp, int n )
+{
_route->add_waypoint( wp, n );
- if (_route->current_index() > n) {
+ if ((n >= 0) && (_route->current_index() > n)) {
_route->set_current(_route->current_index() + 1);
}
+ waypointsChanged();
+}
+
+void FGRouteMgr::waypointsChanged()
+{
+ double routeDistanceNm = _route->total_distance() * SG_METER_TO_NM;
+ totalDistance->setDoubleValue(routeDistanceNm);
+ double cruiseSpeedKts = cruise->getDoubleValue("speed", 0.0);
+ if (cruiseSpeedKts > 1.0) {
+ // very very crude approximation, doesn't allow for climb / descent
+ // performance or anything else at all
+ ete->setDoubleValue(routeDistanceNm / cruiseSpeedKts * (60.0 * 60.0));
+ }
+
update_mirror();
_edited->fireValueChanged();
+ checkFinished();
}
-
SGWayPoint FGRouteMgr::pop_waypoint( int n ) {
if ( _route->size() <= 0 ) {
return SGWayPoint();
SGWayPoint wp = _route->get_waypoint(n);
_route->delete_waypoint(n);
- update_mirror();
- _edited->fireValueChanged();
- checkFinished();
-
+ waypointsChanged();
return wp;
}
mgr->loadRoute();
} else if (!strcmp(s, "@SAVE")) {
mgr->saveRoute();
- } else if (!strcmp(s, "@POP"))
- mgr->pop_waypoint(0);
- else if (!strncmp(s, "@DELETE", 7))
+ } else if (!strcmp(s, "@POP")) {
+ SG_LOG(SG_AUTOPILOT, SG_WARN, "route-manager @POP command is deprecated");
+ } else if (!strcmp(s, "@NEXT")) {
+ mgr->jumpToIndex(mgr->currentWaypoint() + 1);
+ } else if (!strcmp(s, "@PREVIOUS")) {
+ mgr->jumpToIndex(mgr->currentWaypoint() - 1);
+ } else if (!strncmp(s, "@JUMP", 5)) {
+ mgr->jumpToIndex(atoi(s + 5));
+ } else if (!strncmp(s, "@DELETE", 7))
mgr->pop_waypoint(atoi(s + 7));
else if (!strncmp(s, "@INSERT", 7)) {
char *r;
}
_route->set_current(0);
-
- double routeDistanceNm = _route->total_distance() * SG_METER_TO_NM;
- totalDistance->setDoubleValue(routeDistanceNm);
- double cruiseSpeedKts = cruise->getDoubleValue("speed", 0.0);
- if (cruiseSpeedKts > 1.0) {
- // very very crude approximation, doesn't allow for climb / descent
- // performance or anything else at all
- ete->setDoubleValue(routeDistanceNm / cruiseSpeedKts * (60.0 * 60.0));
- }
-
active->setBoolValue(true);
SG_LOG(SG_AUTOPILOT, SG_INFO, "route-manager, activate route ok");
return true;
void FGRouteMgr::jumpToIndex(int index)
{
- if (!active->getBoolValue()) {
- SG_LOG(SG_AUTOPILOT, SG_ALERT, "trying to sequence waypoints with no active route");
- return;
- }
-
if ((index < 0) || (index >= _route->size())) {
SG_LOG(SG_AUTOPILOT, SG_ALERT, "passed invalid index (" <<
index << ") to FGRouteMgr::jumpToIndex");
return _route->current_index();
}
+void FGRouteMgr::setWaypointTargetAltitudeFt(unsigned int index, int altFt)
+{
+ SGWayPoint wp = _route->get_waypoint(index);
+ wp.setTargetAltFt(altFt);
+ // simplest way to update a waypoint is to remove and re-add it
+ _route->delete_waypoint(index);
+ _route->add_waypoint(wp, index);
+ waypointsChanged();
+}
+
void FGRouteMgr::saveRoute()
{
SGPath path(_pathNode->getStringValue());
*/
SGWayPoint* make_waypoint(const string& target);
+ /**
+ * Helper to keep various pieces of state in sync when the SGRoute is
+ * modified (waypoints added, inserted, removed). Notably, this fires the
+ * 'edited' signal.
+ */
+ void waypointsChanged();
void update_mirror();
*/
void jumpToIndex(int index);
+ /**
+ *
+ */
+ void setWaypointTargetAltitudeFt(unsigned int index, int altFt);
+
void saveRoute();
void loadRoute();
};
libGUI_a_SOURCES = \
new_gui.cxx new_gui.hxx \
dialog.cxx dialog.hxx \
- menubar.cxx menubar.hxx \
- gui.cxx gui.h gui_funcs.cxx \
- fonts.cxx \
- AirportList.cxx AirportList.hxx \
+ menubar.cxx menubar.hxx \
+ gui.cxx gui.h gui_funcs.cxx \
+ fonts.cxx \
+ AirportList.cxx AirportList.hxx \
property_list.cxx property_list.hxx \
layout.cxx layout-props.cxx layout.hxx \
- SafeTexFont.cxx SafeTexFont.hxx
-
+ SafeTexFont.cxx SafeTexFont.hxx \
+ WaypointList.cxx WaypointList.hxx
+
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
layout_test_SOURCES = layout-test.cxx
--- /dev/null
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "WaypointList.hxx"
+
+#include <algorithm>
+#include <plib/puAux.h>
+
+#include <simgear/route/waypoint.hxx>
+#include <simgear/structure/callback.hxx>
+#include <simgear/sg_inlines.h>
+
+#include <Main/globals.hxx>
+#include <Main/fg_props.hxx>
+
+#include <Autopilot/route_mgr.hxx>
+
+enum {
+ SCROLL_NO = 0,
+ SCROLL_UP,
+ SCROLL_DOWN
+};
+
+static const int DRAG_START_DISTANCE_PX = 5;
+
+class RouteManagerWaypointModel :
+ public WaypointList::Model,
+ public SGPropertyChangeListener
+{
+public:
+ RouteManagerWaypointModel()
+ {
+ _rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
+
+ SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true);
+ routeEdited->addChangeListener(this);
+ }
+
+ virtual ~RouteManagerWaypointModel()
+ {
+ SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true);
+ routeEdited->removeChangeListener(this);
+ }
+
+// implement WaypointList::Model
+ virtual unsigned int numWaypoints() const
+ {
+ return _rm->size();
+ }
+
+ virtual int currentWaypoint() const
+ {
+ return _rm->currentWaypoint();
+ }
+
+ virtual SGWayPoint waypointAt(unsigned int index) const
+ {
+ return _rm->get_waypoint(index);
+ }
+
+ virtual void deleteAt(unsigned int index)
+ {
+ _rm->pop_waypoint(index);
+ }
+
+ virtual void setWaypointTargetAltitudeFt(unsigned int index, int altFt)
+ {
+ _rm->setWaypointTargetAltitudeFt(index, altFt);
+ }
+
+ virtual void moveWaypointToIndex(unsigned int srcIndex, unsigned int destIndex)
+ {
+ if (destIndex > srcIndex) {
+ --destIndex;
+ }
+
+ SGWayPoint wp = _rm->pop_waypoint(srcIndex);
+ _rm->add_waypoint(wp, destIndex);
+ }
+
+ virtual void setUpdateCallback(SGCallback* cb)
+ {
+ _cb = cb;
+ }
+
+// implement SGPropertyChangeListener
+ void valueChanged(SGPropertyNode *prop)
+ {
+ if (_cb) {
+ (*_cb)();
+ }
+ }
+private:
+ FGRouteMgr* _rm;
+ SGCallback* _cb;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void drawClippedString(puFont& font, const char* s, int x, int y, int maxWidth)
+{
+ int fullWidth = font.getStringWidth(s);
+ if (fullWidth <= maxWidth) { // common case, easy and efficent
+ font.drawString(s, x, y);
+ return;
+ }
+
+ std::string buf(s);
+ int len = buf.size();
+ do {
+ buf.resize(--len);
+ fullWidth = font.getStringWidth(buf.c_str());
+ } while (fullWidth > maxWidth);
+
+ font.drawString(buf.c_str(), x, y);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+WaypointList::WaypointList(int x, int y, int width, int height) :
+ puFrame(x, y, width, height),
+ GUI_ID(FGCLASS_WAYPOINTLIST),
+ _scrollPx(0),
+ _dragging(false),
+ _dragScroll(SCROLL_NO),
+ _showLatLon(false),
+ _model(NULL),
+ _updateCallback(NULL),
+ _scrollCallback(NULL)
+{
+ // pretend to be a list, so fgPopup doesn't mess with our mouse events
+ type |= PUCLASS_LIST;
+ setModel(new RouteManagerWaypointModel());
+ setSize(width, height);
+ setValue(-1);
+}
+
+WaypointList::~WaypointList()
+{
+ delete _model;
+ delete _updateCallback;
+ delete _scrollCallback;
+}
+
+void WaypointList::setUpdateCallback(SGCallback* cb)
+{
+ _updateCallback = cb;
+}
+
+void WaypointList::setScrollCallback(SGCallback* cb)
+{
+ _scrollCallback = cb;
+}
+
+void WaypointList::setSize(int width, int height)
+{
+ double scrollP = getVScrollPercent();
+ _heightPx = height;
+ puFrame::setSize(width, height);
+
+ if (wantsVScroll()) {
+ setVScrollPercent(scrollP);
+ } else {
+ _scrollPx = 0;
+ }
+}
+
+int WaypointList::checkHit ( int button, int updown, int x, int y )
+{
+ if ( isHit( x, y ) || ( puActiveWidget () == this ) )
+ {
+ doHit ( button, updown, x, y ) ;
+ return TRUE ;
+ }
+
+ return FALSE ;
+}
+
+
+void WaypointList::doHit( int button, int updown, int x, int y )
+{
+ puFrame::doHit(button, updown, x, y);
+ if (updown == PU_DRAG) {
+ handleDrag(x, y);
+ return;
+ }
+
+ if (button != active_mouse_button) {
+ return;
+ }
+
+ if (updown == PU_UP) {
+ puDeactivateWidget();
+ if (_dragging) {
+ doDrop(x, y);
+ return;
+ }
+ } else if (updown == PU_DOWN) {
+ puSetActiveWidget(this, x, y);
+ _mouseDownX = x;
+ _mouseDownY = y;
+ return;
+ }
+
+// update selection
+ int row = rowForY(y - abox.min[1]);
+ if (row >= (int) _model->numWaypoints()) {
+ row = -1; // 'no selection'
+ }
+
+ if (row == getSelected()) {
+ _showLatLon = !_showLatLon;
+ puPostRefresh();
+ return;
+ }
+
+ setSelected(row);
+}
+
+void WaypointList::handleDrag(int x, int y)
+{
+ if (!_dragging) {
+ // don't start drags immediately, require a certain mouse movement first
+ int manhattanLength = abs(x - _mouseDownX) + abs(y - _mouseDownY);
+ if (manhattanLength < DRAG_START_DISTANCE_PX) {
+ return;
+ }
+
+ _dragSourceRow = rowForY(y - abox.min[1]);
+ _dragging = true;
+ _dragScroll = SCROLL_NO;
+ }
+
+ if (y < abox.min[1]) {
+ if (_dragScroll != SCROLL_DOWN) {
+ _dragScroll = SCROLL_DOWN;
+ _dragScrollTime.stamp();
+ }
+ } else if (y > abox.max[1]) {
+ if (_dragScroll != SCROLL_UP) {
+ _dragScroll = SCROLL_UP;
+ _dragScrollTime.stamp();
+ }
+ } else {
+ _dragScroll = SCROLL_NO;
+ _dragTargetRow = rowForY(y - abox.min[1] - (rowHeightPx() / 2));
+ }
+}
+
+void WaypointList::doDrop(int x, int y)
+{
+ _dragging = false;
+ puDeactivateWidget();
+
+ if ((y < abox.min[1]) || (y >= abox.max[1])) {
+ return;
+ }
+
+ if (_dragSourceRow != _dragTargetRow) {
+ _model->moveWaypointToIndex(_dragSourceRow, _dragTargetRow);
+
+ // keep row indexes linged up when moving an item down the list
+ if (_dragSourceRow < _dragTargetRow) {
+ --_dragTargetRow;
+ }
+
+ setSelected(_dragTargetRow);
+ }
+}
+
+void WaypointList::invokeDownCallback(void)
+{
+ _dragging = false;
+ _dragScroll = SCROLL_NO;
+ SG_LOG(SG_GENERAL, SG_INFO, "cancel drag");
+}
+
+int WaypointList::rowForY(int y) const
+{
+ if (!_model) {
+ return -1;
+ }
+
+ // flip y to increase down, not up (as rows do)
+ int flipY = _heightPx - y;
+ int row = (flipY + _scrollPx) / rowHeightPx();
+ return row;
+}
+
+void WaypointList::draw( int dx, int dy )
+{
+ puFrame::draw(dx, dy);
+
+ if (!_model) {
+ return;
+ }
+
+ if (_dragScroll != SCROLL_NO) {
+ doDragScroll();
+ }
+
+ glEnable(GL_SCISSOR_TEST);
+ GLint sx = (int) abox.min[0],
+ sy = abox.min[1];
+ GLsizei w = (GLsizei) abox.max[0] - abox.min[0],
+ h = _heightPx;
+
+ sx += border_thickness;
+ sy += border_thickness;
+ w -= 2 * border_thickness;
+ h -= 2 * border_thickness;
+
+ glScissor(sx + dx, sy + dy, w, h);
+ int row = firstVisibleRow(),
+ final = lastVisibleRow(),
+ rowHeight = rowHeightPx(),
+ y = rowHeight;
+
+ y -= (_scrollPx % rowHeight); // partially draw the first row
+
+ for ( ; row <= final; ++row, y += rowHeight) {
+ drawRow(dx, dy, row, y);
+ } // of row drawing iteration
+
+ glDisable(GL_SCISSOR_TEST);
+
+ if (_dragging) {
+ // draw the insert marker after the rows
+ int insertY = (_dragTargetRow * rowHeight) - _scrollPx;
+ SG_CLAMP_RANGE(insertY, 0, std::min(_heightPx, totalHeightPx()));
+
+ glColor4f(1.0f, 0.5f, 0.0f, 0.8f);
+ glLineWidth(3.0f);
+ glBegin(GL_LINES);
+ glVertex2f(dx + abox.min[0], dy + abox.max[1] - insertY);
+ glVertex2f(dx + abox.max[0], dy + abox.max[1] - insertY);
+ glEnd();
+ }
+}
+
+void WaypointList::drawRow(int dx, int dy, int rowIndex, int y)
+{
+ bool isSelected = (rowIndex == getSelected());
+ bool isCurrent = (rowIndex == _model->currentWaypoint());
+ bool isDragSource = (_dragging && (rowIndex == _dragSourceRow));
+
+ puBox bkgBox = abox;
+ bkgBox.min[1] = abox.max[1] - y;
+ bkgBox.max[1] = bkgBox.min[1] + rowHeightPx();
+
+ puColour currentColor;
+ puSetColor(currentColor, 1.0, 1.0, 0.0, 0.5);
+
+ if (isDragSource) {
+ // draw later, on *top* of text string
+ } else if (isCurrent) {
+ bkgBox.draw(dx, dy, PUSTYLE_PLAIN, ¤tColor, false, 0);
+ } else if (isSelected) { // -PLAIN means selected, apparently
+ bkgBox.draw(dx, dy, -PUSTYLE_PLAIN, colour, false, 0);
+ }
+
+ int xx = dx + abox.min[0] + PUSTR_LGAP;
+ int yy = dy + abox.max[1] - y ;
+ yy += 4; // center text in row height
+
+ // row textual data
+ const SGWayPoint wp(_model->waypointAt(rowIndex));
+ char buffer[128];
+ int count = ::snprintf(buffer, 128, "%03d %-5s", rowIndex, wp.get_id().c_str());
+
+ if (wp.get_name().size() > 0 && (wp.get_name() != wp.get_id())) {
+ // append name if present, and different to id
+ ::snprintf(buffer + count, 128 - count, " (%s)", wp.get_name().c_str());
+ }
+
+ glColor4fv ( colour [ PUCOL_LEGEND ] ) ;
+ drawClippedString(legendFont, buffer, xx, yy, 300);
+
+ if (_showLatLon) {
+ char ns = (wp.get_target_lat() > 0.0) ? 'N' : 'S';
+ char ew = (wp.get_target_lon() > 0.0) ? 'E' : 'W';
+
+ ::snprintf(buffer, 128 - count, "%4.2f%c %4.2f%c",
+ fabs(wp.get_target_lon()), ew, fabs(wp.get_target_lat()), ns);
+ } else {
+ ::snprintf(buffer, 128 - count, "%03.0f %5.1fnm",
+ wp.get_track(), wp.get_distance() * SG_METER_TO_NM);
+ }
+
+ legendFont.drawString(buffer, xx + 300 + PUSTR_LGAP, yy);
+
+ int altFt = (int) wp.get_target_alt() * SG_METER_TO_FEET;
+ if (altFt > -9990) {
+ int altHundredFt = (altFt + 50) / 100; // round to nearest 100ft
+ if (altHundredFt < 100) {
+ count = ::snprintf(buffer, 128, "%d'", altHundredFt * 100);
+ } else { // display as a flight-level
+ count = ::snprintf(buffer, 128, "FL%d", altHundredFt);
+ }
+
+ legendFont.drawString(buffer, xx + 400 + PUSTR_LGAP, yy);
+ } // of valid wp altitude
+
+ if (isDragSource) {
+ puSetColor(currentColor, 1.0, 0.5, 0.0, 0.5);
+ bkgBox.draw(dx, dy, PUSTYLE_PLAIN, ¤tColor, false, 0);
+ }
+}
+
+const double SCROLL_PX_SEC = 200.0;
+
+void WaypointList::doDragScroll()
+{
+ double dt = (SGTimeStamp::now() - _dragScrollTime).toSecs();
+ _dragScrollTime.stamp();
+ int deltaPx = (int)(dt * SCROLL_PX_SEC);
+
+ if (_dragScroll == SCROLL_UP) {
+ _scrollPx = _scrollPx - deltaPx;
+ SG_CLAMP_RANGE(_scrollPx, 0, scrollRangePx());
+ _dragTargetRow = firstVisibleRow();
+ } else {
+ _scrollPx = _scrollPx + deltaPx;
+ SG_CLAMP_RANGE(_scrollPx, 0, scrollRangePx());
+ _dragTargetRow = lastFullyVisibleRow() + 1;
+ }
+
+ if (_scrollCallback) {
+ (*_scrollCallback)();
+ }
+}
+
+int WaypointList::getSelected()
+{
+ return getIntegerValue();
+}
+
+void WaypointList::setSelected(int rowIndex)
+{
+ if (rowIndex == getSelected()) {
+ return;
+ }
+
+ setValue(rowIndex);
+ invokeCallback();
+ if (rowIndex == -1) {
+ return;
+ }
+
+ ensureRowVisible(rowIndex);
+}
+
+void WaypointList::ensureRowVisible(int rowIndex)
+{
+ if ((rowIndex >= firstFullyVisibleRow()) && (rowIndex <= lastFullyVisibleRow())) {
+ return; // already visible, fine
+ }
+
+ // ideal position would place the desired row in the middle of the
+ // visible section - hence subtract half the visible height.
+ int targetScrollPx = (rowIndex * rowHeightPx()) - (_heightPx / 2);
+
+ // clamp the scroll value to something valid
+ SG_CLAMP_RANGE(targetScrollPx, 0, scrollRangePx());
+ _scrollPx = targetScrollPx;
+
+ puPostRefresh();
+ if (_scrollCallback) { // keep scroll observers in sync
+ (*_scrollCallback)();
+ }
+}
+
+unsigned int WaypointList::numWaypoints() const
+{
+ if (!_model) {
+ return 0;
+ }
+
+ return _model->numWaypoints();
+}
+
+bool WaypointList::wantsVScroll() const
+{
+ return totalHeightPx() > _heightPx;
+}
+
+float WaypointList::getVScrollPercent() const
+{
+ float scrollRange = scrollRangePx();
+ if (scrollRange < 1.0f) {
+ return 0.0;
+ }
+
+ return _scrollPx / scrollRange;
+}
+
+float WaypointList::getVScrollThumbPercent() const
+{
+ return _heightPx / (float) totalHeightPx();
+}
+
+void WaypointList::setVScrollPercent(float perc)
+{
+ float scrollRange = scrollRangePx();
+ _scrollPx = (int)(scrollRange * perc);
+}
+
+int WaypointList::firstVisibleRow() const
+{
+ return _scrollPx / rowHeightPx();
+}
+
+int WaypointList::firstFullyVisibleRow() const
+{
+ int rh = rowHeightPx();
+ return (_scrollPx + rh - 1) / rh;
+}
+
+int WaypointList::numVisibleRows() const
+{
+ int rh = rowHeightPx();
+ int topOffset = _scrollPx % rh; // pixels of first visible row
+ return (_heightPx - topOffset + rh - 1) / rh;
+
+}
+
+int WaypointList::numFullyVisibleRows() const
+{
+ int rh = rowHeightPx();
+ int topOffset = _scrollPx % rh; // pixels of first visible row
+ return (_heightPx - topOffset) / rh;
+}
+
+int WaypointList::rowHeightPx() const
+{
+ return legendFont.getStringHeight() + PUSTR_BGAP;
+}
+
+int WaypointList::scrollRangePx() const
+{
+ return std::max(0, totalHeightPx() - _heightPx);
+}
+
+int WaypointList::totalHeightPx() const
+{
+ if (!_model) {
+ return 0;
+ }
+
+ return (int) _model->numWaypoints() * rowHeightPx();
+}
+
+int WaypointList::lastFullyVisibleRow() const
+{
+ int row = firstFullyVisibleRow() + numFullyVisibleRows();
+ return std::min(row, (int) _model->numWaypoints() - 1);
+}
+
+int WaypointList::lastVisibleRow() const
+{
+ int row = firstVisibleRow() + numVisibleRows();
+ return std::min(row, (int) _model->numWaypoints() - 1);
+}
+
+void WaypointList::setModel(Model* model)
+{
+ if (_model) {
+ delete _model;
+ }
+
+ _model = model;
+ _model->setUpdateCallback(make_callback(this, &WaypointList::modelUpdateCallback));
+
+ puPostRefresh();
+}
+
+int WaypointList::checkKey (int key, int updown )
+{
+ if ((updown == PU_UP) || !isVisible () || !isActive () || (window != puGetWindow())) {
+ return FALSE ;
+ }
+
+ switch (key)
+ {
+ case PU_KEY_HOME:
+ setSelected(0);
+ break;
+
+ case PU_KEY_END:
+ setSelected(_model->numWaypoints() - 1);
+ break ;
+
+ case PU_KEY_UP :
+ case PU_KEY_PAGE_UP :
+ if (getSelected() >= 0) {
+ setSelected(getSelected() - 1);
+ }
+ break ;
+
+ case PU_KEY_DOWN :
+ case PU_KEY_PAGE_DOWN : {
+ int newSel = getSelected() + 1;
+ if (newSel >= (int) _model->numWaypoints()) {
+ setSelected(-1);
+ } else {
+ setSelected(newSel);
+ }
+ break ;
+ }
+
+ case '-':
+ if (getSelected() >= 0) {
+ int newAlt = wayptAltFtHundreds(getSelected()) - 10;
+ if (newAlt < 0) {
+ _model->setWaypointTargetAltitudeFt(getSelected(), -9999);
+ } else {
+ _model->setWaypointTargetAltitudeFt(getSelected(), newAlt * 100);
+ }
+ }
+ break;
+
+ case '=':
+ if (getSelected() >= 0) {
+ int newAlt = wayptAltFtHundreds(getSelected()) + 10;
+ if (newAlt < 0) {
+ _model->setWaypointTargetAltitudeFt(getSelected(), 0);
+ } else {
+ _model->setWaypointTargetAltitudeFt(getSelected(), newAlt * 100);
+ }
+ }
+ break;
+
+ case 0x7f: // delete
+ if (getSelected() >= 0) {
+ int index = getSelected();
+ _model->deleteAt(index);
+ setSelected(index - 1);
+ }
+ break;
+
+ default :
+ return FALSE;
+ }
+
+ return TRUE ;
+}
+
+int WaypointList::wayptAltFtHundreds(int index) const
+{
+ double alt = _model->waypointAt(index).get_target_alt();
+ if (alt < -9990.0) {
+ return -9999;
+ }
+
+ int altFt = (int) alt * SG_METER_TO_FEET;
+ return (altFt + 50) / 100; // round to nearest 100ft
+}
+
+void WaypointList::modelUpdateCallback()
+{
+ // local stuff
+
+ if (_updateCallback) {
+ (*_updateCallback)();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+static void handle_scrollbar(puObject* scrollbar)
+{
+ ScrolledWaypointList* self = (ScrolledWaypointList*)scrollbar->getUserData();
+ self->setScrollPercent(scrollbar->getFloatValue());
+}
+
+static void waypointListCb(puObject* wpl)
+{
+ ScrolledWaypointList* self = (ScrolledWaypointList*)wpl->getUserData();
+ self->setValue(wpl->getIntegerValue());
+ self->invokeCallback();
+}
+
+ScrolledWaypointList::ScrolledWaypointList(int x, int y, int width, int height) :
+ puGroup(x,y),
+ _scrollWidth(16)
+{
+ // ensure our type is compound, so fgPopup::applySize doesn't descend into
+ // us, and try to cast our children's user-data to GUIInfo*.
+ type |= PUCLASS_LIST;
+
+ init(width, height);
+}
+
+void ScrolledWaypointList::setValue(float v)
+{
+ puGroup::setValue(v);
+ _list->setValue(v);
+}
+
+void ScrolledWaypointList::setValue(int v)
+{
+ puGroup::setValue(v);
+ _list->setValue(v);
+}
+
+void ScrolledWaypointList::init(int w, int h)
+{
+ _list = new WaypointList(0, 0, w, h);
+ _list->setUpdateCallback(make_callback(this, &ScrolledWaypointList::modelUpdated));
+ _hasVScroll = _list->wantsVScroll();
+ _list->setUserData(this);
+ _list->setCallback(waypointListCb);
+
+ _list->setScrollCallback(make_callback(this, &ScrolledWaypointList::updateScroll));
+
+ _scrollbar = new puaScrollBar(w - _scrollWidth, 0, h,
+ 1 /*arrow*/, 1 /* vertical */, _scrollWidth);
+ _scrollbar->setMinValue(0.0);
+ _scrollbar->setMaxValue(1.0);
+ _scrollbar->setUserData(this);
+ _scrollbar->setCallback(handle_scrollbar);
+ close(); // close the group
+
+ setSize(w, h);
+}
+
+void ScrolledWaypointList::modelUpdated()
+{
+ int w, h;
+ getSize(&w, &h);
+ updateWantsScroll(w, h);
+}
+
+void ScrolledWaypointList::setScrollPercent(float v)
+{
+ // slider's min is the bottom, so invert the value
+ _list->setVScrollPercent(1.0f - v);
+}
+
+void ScrolledWaypointList::setSize(int w, int h)
+{
+ updateWantsScroll(w, h);
+}
+
+void ScrolledWaypointList::updateWantsScroll(int w, int h)
+{
+ _hasVScroll = _list->wantsVScroll();
+
+ if (_hasVScroll) {
+ _scrollbar->reveal();
+ _scrollbar->setPosition(w - _scrollWidth, 0);
+ _scrollbar->setSize(_scrollWidth, h);
+ _list->setSize(w - _scrollWidth, h);
+ updateScroll();
+ } else {
+ _scrollbar->hide();
+ _list->setSize(w, h);
+ }
+}
+
+void ScrolledWaypointList::updateScroll()
+{
+ // _scrollbar->setMaxValue(_list->numWaypoints());
+ _scrollbar->setValue(1.0f - _list->getVScrollPercent());
+ _scrollbar->setSliderFraction(_list->getVScrollThumbPercent());
+}
+
--- /dev/null
+/**
+ * WaypointList.hxx - scrolling list of waypoints, with special formatting
+ */
+
+#ifndef GUI_WAYPOINT_LIST_HXX
+#define GUI_WAYPOINT_LIST_HXX
+
+#include <simgear/compiler.h>
+#include <simgear/timing/timestamp.hxx>
+
+#include <plib/pu.h>
+
+#include "dialog.hxx" // for GUI_ID
+
+// forward decls
+class puaScrollBar;
+class SGWayPoint;
+class SGCallback;
+
+class WaypointList : public puFrame, public GUI_ID
+{
+public:
+ WaypointList(int x, int y, int width, int height);
+ virtual ~WaypointList();
+
+ virtual void setSize(int width, int height);
+ virtual int checkHit ( int button, int updown, int x, int y);
+ virtual void doHit( int button, int updown, int x, int y ) ;
+ virtual void draw( int dx, int dy ) ;
+ virtual int checkKey(int key, int updown);
+ virtual void invokeDownCallback (void);
+
+ void setSelected(int rowIndex);
+ int getSelected();
+
+ /**
+ * Do we want a vertical scrollbar (or similar)
+ */
+ bool wantsVScroll() const;
+
+ /**
+ * Get scrollbar position as a percentage of total range.
+ * returns negative number if scrolling is not possible
+ */
+ float getVScrollPercent() const;
+
+ /**
+ *
+ */
+ void setVScrollPercent(float perc);
+
+ /**
+ * Get required thumb size as percentage of total height
+ */
+ float getVScrollThumbPercent() const;
+
+ int numVisibleRows() const;
+
+ void ensureRowVisible(int row);
+
+ void setUpdateCallback(SGCallback* cb);
+ void setScrollCallback(SGCallback* cb);
+
+ /**
+ * Abstract interface for waypoint source
+ */
+ class Model
+ {
+ public:
+ virtual ~Model() { }
+
+ virtual unsigned int numWaypoints() const = 0;
+ virtual int currentWaypoint() const = 0;
+ virtual SGWayPoint waypointAt(unsigned int index) const = 0;
+
+ // update notifications
+ virtual void setUpdateCallback(SGCallback* cb) = 0;
+
+ // editing operations
+ virtual void deleteAt(unsigned int index) = 0;
+ virtual void setWaypointTargetAltitudeFt(unsigned int index, int altFt) = 0;
+ virtual void moveWaypointToIndex(unsigned int srcIndex, unsigned int dstIndex) = 0;
+ };
+
+ void setModel(Model* model);
+
+ unsigned int numWaypoints() const;
+protected:
+
+private:
+ void drawRow(int dx, int dy, int rowIndex, int yOrigin);
+
+ void handleDrag(int x, int y);
+ void doDrop(int x, int y);
+ void doDragScroll();
+
+ /**
+ * Pixel height of a row, including padding
+ */
+ int rowHeightPx() const;
+
+ /**
+ * model row corresponding to an on-screen y-value
+ */
+ int rowForY(int y) const;
+
+ /**
+ * reutrn rowheight * total number of rows, i.e the height we'd
+ * need to be to show every row without scrolling
+ */
+ int totalHeightPx() const;
+
+ /**
+ * Pixel scroll range, based on widget height and number of rows
+ */
+ int scrollRangePx() const;
+
+ int firstVisibleRow() const;
+ int lastVisibleRow() const;
+
+ int numFullyVisibleRows() const;
+ int firstFullyVisibleRow() const;
+ int lastFullyVisibleRow() const;
+
+ int wayptAltFtHundreds(int index) const;
+
+ void modelUpdateCallback();
+
+ int _scrollPx; // scroll ammount (in pixels)
+ int _heightPx;
+
+ bool _dragging;
+ int _dragSourceRow;
+ int _dragTargetRow;
+ int _mouseDownX, _mouseDownY;
+
+ int _dragScroll;
+ SGTimeStamp _dragScrollTime;
+
+ bool _showLatLon;
+ Model* _model;
+ SGCallback* _updateCallback;
+ SGCallback* _scrollCallback;
+};
+
+class ScrolledWaypointList : public puGroup
+{
+public:
+ ScrolledWaypointList(int x, int y, int width, int height);
+
+ virtual void setSize(int width, int height);
+
+ void setScrollPercent(float v);
+
+ virtual void setValue(float v);
+ virtual void setValue(int v);
+private:
+ void init(int w, int h);
+
+ void updateScroll();
+ void updateWantsScroll(int w, int h);
+
+ void modelUpdated();
+
+ puaScrollBar* _scrollbar;
+ WaypointList* _list;
+ int _scrollWidth;
+ bool _hasVScroll;
+};
+
+#endif // GUI_WAYPOINT_LIST_HXX
#include "AirportList.hxx"
#include "property_list.hxx"
#include "layout.hxx"
-
+#include "WaypointList.hxx"
enum format_type { f_INVALID, f_INT, f_LONG, f_FLOAT, f_DOUBLE, f_STRING };
static const int FORMAT_BUFSIZE = 255;
puObject *widget = _propertyObjects[i]->object;
int widgetType = widget->getType();
- if ((widgetType & PUCLASS_LIST) && (dynamic_cast<GUI_ID *>(widget)->id & FGCLASS_LIST)) {
- fgList *pl = static_cast<fgList*>(widget);
- pl->update();
+ if (widgetType & PUCLASS_LIST) {
+ GUI_ID* gui_id = dynamic_cast<GUI_ID *>(widget);
+ if (gui_id && (gui_id->id & FGCLASS_LIST)) {
+ fgList *pl = static_cast<fgList*>(widget);
+ pl->update();
+ } else {
+ copy_to_pui(_propertyObjects[i]->node, widget);
+ }
} else if (widgetType & PUCLASS_COMBOBOX) {
fgComboBox* combo = static_cast<fgComboBox*>(widget);
combo->update();
setupObject(obj, props);
setColor(obj, props, EDITFIELD);
return obj;
+ } else if (type == "waypointlist") {
+ ScrolledWaypointList* obj = new ScrolledWaypointList(x, y, width, height);
+ setupObject(obj, props);
+ return obj;
} else {
return 0;
}
#define FGCLASS_LIST 0x00000001
#define FGCLASS_AIRPORTLIST 0x00000002
#define FGCLASS_PROPERTYLIST 0x00000004
+#define FGCLASS_WAYPOINTLIST 0x00000008
+
class GUI_ID { public: GUI_ID(int id) : id(id) {} virtual ~GUI_ID() {} int id; };
#define NOMINMAX 1
+#define HAVE_CULLSETTINGS_CLEAR_MASK 1
+
#ifndef ENABLE_AUDIO_SUPPORT
#define ENABLE_AUDIO_SUPPORT
#endif
void draw_stipple_line(float x1, float y1, float x2, float y2);
void draw_text(float x, float y, const char *msg, int align = 0, int digit = 0);
void draw_circle(float x1, float y1, float r) const;
+ void draw_arc(float x1, float y1, float t0, float t1, float r) const;
void draw_bullet(float, float, float);
HUD *_hud;
private:
SGSharedPtr<SGCondition> _active_condition; // stadiametric (true) or standby (false)
+ SGSharedPtr<SGCondition> _tachy_condition; // tachymetric (true) or standby (false)
+ SGSharedPtr<SGCondition> _align_condition; // tachymetric (true) or standby (false)
+
Input _diameter; // inner/outer radius relation
+ Input _pitch;
+ Input _yaw;
+ Input _speed;
+ Input _range;
+ Input _t0;
+ Input _t1;
+ Input _offset_x;
+ Input _offset_y;
+
float _bullet_size;
float _inner_radius;
+ float _compression;
+ float _limit_x;
+ float _limit_y;
+
};
glEnd();
}
+void HUD::Item::draw_arc(float xoffs, float yoffs, float t0, float t1, float r) const
+{
+ glBegin(GL_LINE_STRIP);
+ float step = SG_PI / r;
+ t0 = t0 * SG_DEGREES_TO_RADIANS;
+ t1 = t1 * SG_DEGREES_TO_RADIANS;
+
+ for (float alpha = t0; alpha < t1; alpha += step) {
+ float x = r * cos(alpha);
+ float y = r * sin(alpha);
+ glVertex2f(x + xoffs, y + yoffs);
+ }
+ glEnd();
+}
void HUD::Item::draw_bullet(float x, float y, float size)
{
#include "HUD.hxx"
-
// MIL-STD-1787B aiming reticle
HUD::AimingReticle::AimingReticle(HUD *hud, const SGPropertyNode *n, float x, float y) :
- Item(hud, n, x, y),
- _active_condition(0),
- _diameter(n->getNode("diameter-input", false)),
- _bullet_size(_w / 6.0),
- _inner_radius(_w / 2.0)
+Item(hud, n, x, y),
+_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
}
+
}
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
double FGNavRadio::localizerWidth(FGNavRecord* aLOC)
{
FGRunway* rwy = aLOC->runway();
- assert(rwy);
+ if (!rwy) {
+ return 6.0; // no runway associated, return default value
+ }
+
SGVec3d thresholdCart(SGVec3d::fromGeod(rwy->threshold()));
double axisLength = dist(aLOC->cart(), thresholdCart);
camera->setProjectionMatrix(projectionMatrix);
camera->setCullMask(camera->getCullMask()
| simgear::BACKGROUND_BIT);
- camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
farCamera->setNodeMask(0);
} else {
Matrix nearProj, farProj;
{1100, "sim/model/variant", simgear::props::INT},
{1101, "sim/model/livery/file", simgear::props::STRING},
+ {1200, "environment/wildfire/data", simgear::props::STRING},
+
{10001, "sim/multiplay/transmission-freq-hz", simgear::props::STRING},
{10002, "sim/multiplay/chat", simgear::props::STRING},
mServer.set(txAddress.c_str(), txPort);
if (strncmp (mServer.getHost(), "0.0.0.0", 8) == 0) {
mHaveServer = false;
- SG_LOG(SG_NETWORK, SG_ALERT,
+ SG_LOG(SG_NETWORK, SG_DEBUG,
"FGMultiplayMgr - could not resolve '"
<< txAddress << "', Multiplayermode disabled");
} else {
rxPort = txPort;
}
if (rxPort <= 0) {
- SG_LOG(SG_NETWORK, SG_ALERT,
+ SG_LOG(SG_NETWORK, SG_DEBUG,
"FGMultiplayMgr - No receiver port, Multiplayermode disabled");
return (false);
}
// A memory leak was reported here by valgrind
mSocket = new netSocket();
if (!mSocket->open(false)) {
- SG_LOG( SG_NETWORK, SG_ALERT,
+ SG_LOG( SG_NETWORK, SG_DEBUG,
"FGMultiplayMgr::init - Failed to create data socket" );
return false;
}
mSocket->setBlocking(false);
if (mSocket->bind(rxAddress.c_str(), rxPort) != 0) {
perror("bind");
- SG_LOG( SG_NETWORK, SG_ALERT,
+ SG_LOG( SG_NETWORK, SG_DEBUG,
"FGMultiplayMgr::Open - Failed to bind receive socket" );
return false;
}
if ((! mInitialised) || (! mHaveServer))
return;
if (! mHaveServer) {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::SendMyPosition - no server");
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition - no server");
return;
}
break;
}
if (bytes <= static_cast<ssize_t>(sizeof(T_MsgHdr))) {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "received message with insufficient data" );
break;
}
MsgHdr->ReplyPort = XDR_decode_uint32 (MsgHdr->ReplyPort);
MsgHdr->Callsign[MAX_CALLSIGN_LEN -1] = '\0';
if (MsgHdr->Magic != MSG_MAGIC) {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "message has invalid magic number!" );
break;
}
if (MsgHdr->Version != PROTO_VER) {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "message has invalid protocoll number!" );
break;
}
if (MsgHdr->MsgLen != bytes) {
- SG_LOG(SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "message from " << MsgHdr->Callsign << " has invalid length!");
break;
}
case OLD_POS_DATA_ID:
break;
default:
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "Unknown message Id received: " << MsgHdr->MsgId );
break;
}
{
const T_MsgHdr* MsgHdr = Msg.msgHdr();
if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "Position message received with insufficient data" );
return;
}
default:
pData->float_value = XDR_decode_float(*xdr);
- SG_LOG(SG_NETWORK, SG_ALERT, "Unknown Prop type " << pData->id << " " << pData->type);
+ SG_LOG(SG_NETWORK, SG_DEBUG, "Unknown Prop type " << pData->id << " " << pData->type);
xdr++;
break;
}
{
const T_MsgHdr* MsgHdr = Msg.msgHdr();
if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1) {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
<< "Chat message received with insufficient data" );
return;
}
case FG_DOUBLE:
val = _out_message[i].offset +
- _out_message[i].prop->getFloatValue() * _out_message[i].factor;
- snprintf(tmp, 255, _out_message[i].format.c_str(), (float)val);
+ _out_message[i].prop->getDoubleValue() * _out_message[i].factor;
+ snprintf(tmp, 255, _out_message[i].format.c_str(), (double)val);
break;
default: // SG_STRING
}
}
} else {
- do {
- if (!binary_mode) {
- length = io->readline( buf, FG_MAX_MSG_SIZE );
- if ( length > 0 ) {
- parse_message();
- }
- } else {
- length = io->read( buf, binary_record_length );
- if ( length == binary_record_length ) {
- parse_message();
- } else if ( length > 0 ) {
- SG_LOG( SG_IO, SG_ALERT,
- "Generic protocol: Received binary "
- "record of unexpected size, expected: "
- << binary_record_length << " but received: "
- << length);
- }
+ if (!binary_mode) {
+ while ((length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
+ parse_message();
}
- } while ( length == binary_record_length );
+ } else {
+ while ((length = io->read( buf, binary_record_length ))
+ == binary_record_length ) {
+ parse_message();
+ }
+
+ if ( length > 0 ) {
+ SG_LOG( SG_IO, SG_ALERT,
+ "Generic protocol: Received binary "
+ "record of unexpected size, expected: "
+ << binary_record_length << " but received: "
+ << length);
+ }
+ }
}
}
return true;
#include "SchedFlight.hxx"
#include "TrafficMgr.hxx"
-using std::sort;
-
/******************************************************************************
* the FGAISchedule class contains data members and code to maintain a
* schedule of Flights for an articically controlled aircraft.
AIManagerRef = 0;
heavy = false;
- lat = 0;
- lon = 0;
radius = 0;
groundOffset = 0;
distanceToUser = 0;
airline = arln;
m_class = mclass;
flightType = fltpe;
- lat = 0;
- lon = 0;
radius = rad;
groundOffset = grnd;
distanceToUser = 0;
heavy = other.heavy;
flightIdentifier = other.flightIdentifier;
flights = other.flights;
- lat = other.lat;
- lon = other.lon;
AIManagerRef = other.AIManagerRef;
acType = other.acType;
airline = other.airline;
return true;
}
-bool FGAISchedule::update(time_t now)
+bool FGAISchedule::update(time_t now, const SGVec3d& userCart)
{
- FGAirport *dep;
- FGAirport *arr;
- double angle;
-
- FGAIManager *aimgr;
- string airport;
-
- double speed;
-
+ if (!fgGetBool("/sim/traffic-manager/enabled"))
+ return true;
+
time_t
totalTimeEnroute,
elapsedTimeEnroute,
- remainingTimeEnroute, deptime = 0;
- double
- userLatitude,
- userLongitude;
-
- SGVec3d newPos(0, 0, 0);
-
-
- if (fgGetBool("/sim/traffic-manager/enabled") == false)
- return true;
+ remainingTimeEnroute,
+ deptime = 0;
- aimgr = (FGAIManager *) globals-> get_subsystem("ai_model");
- // Out-of-work aircraft seeks employment. Willing to work irregular hours ...
- //cerr << "About to find a flight " << endl;
- if (flights.empty()) {
- //execute this loop at least once.
- SG_LOG(SG_GENERAL, SG_BULK, "Scheduling for : " << modelPath << " " << registration << " " << homePort);
- FGScheduledFlight *flight = 0;
- do {
- flight = findAvailableFlight(currentDestination, flightIdentifier);
- if (flight) {
- currentDestination = flight->getArrivalAirport()->getId();
- time_t arr, dep;
- dep = flight->getDepartureTime();
- arr = flight->getArrivalTime();
- string depT = asctime(gmtime(&dep));
- string arrT = asctime(gmtime(&arr));
-
- depT = depT.substr(0,24);
- arrT = arrT.substr(0,24);
- SG_LOG(SG_GENERAL, SG_BULK, " " << flight->getCallSign() << ":"
- << " " << flight->getDepartureAirport()->getId() << ":"
- << " " << depT << ":"
- << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
- << " " << arrT << ":");
- flights.push_back(flight);
- }
- } while ((currentDestination != homePort) && (flight != 0));
- SG_LOG(SG_GENERAL, SG_BULK, cerr << " Done " << endl);
- }
- //cerr << " Done " << endl;
- // No flights available for this aircraft
- if (flights.size() == 0) {
+ scheduleFlights();
+ if (flights.empty()) { // No flights available for this aircraft
return false;
}
+
// Sort all the scheduled flights according to scheduled departure time.
// Because this is done at every update, we only need to check the status
// of the first listed flight.
//sort(flights.begin(), flights.end(), compareScheduledFlights);
- if (firstRun) {
+
+ if (firstRun) {
if (fgGetBool("/sim/traffic-manager/instantaneous-action") == true) {
deptime = now + rand() % 300; // Wait up to 5 minutes until traffic starts moving to prevent too many aircraft
// from cluttering the gate areas.
- cerr << "Scheduling " << registration << " for instantaneous action flight " << endl;
}
firstRun = false;
}
+
+ FGScheduledFlight* flight = flights.front();
if (!deptime)
- deptime = (*flights.begin())->getDepartureTime();
- FGScheduledFlightVecIterator i = flights.begin();
- SG_LOG (SG_GENERAL, SG_DEBUG,"Traffic Manager: Processing registration " << registration << " with callsign " << (*i)->getCallSign());
- if (AIManagerRef)
- {
- // Check if this aircraft has been released.
- FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
- if (tmgr->isReleased(AIManagerRef))
- AIManagerRef = 0;
+ deptime = flight->getDepartureTime();
+
+ if (AIManagerRef) {
+ // Check if this aircraft has been released.
+ FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
+ if (tmgr->isReleased(AIManagerRef)) {
+ AIManagerRef = NULL;
+ } else {
+ return true; // in visual range, let the AIManager handle it
}
-
- if (!AIManagerRef)
- {
- userLatitude = fgGetDouble("/position/latitude-deg");
- userLongitude = fgGetDouble("/position/longitude-deg");
-
- //cerr << "Estimated minimum distance to user: " << distanceToUser << endl;
- // This flight entry is entirely in the past, do we need to
- // push it forward in time to the next scheduled departure.
- if (((*i)->getDepartureTime() < now) && ((*i)->getArrivalTime() < now))
- {
- SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic Manager: Flight is in the Past");
- //cerr << modelPath << " " << registration << ": Flights from the past belong to the past :-)" << endl;
- //exit(1);
- // Don't just update: check whether we need to load a new leg. etc.
- // This update occurs for distant aircraft, so we can update the current leg
- // and detach it from the current list of aircraft.
- (*i)->update();
- i = flights.erase(i);
+ }
+
+ // This flight entry is entirely in the past, do we need to
+ // push it forward in time to the next scheduled departure.
+ if (flight->getArrivalTime() < now) {
+ SG_LOG (SG_GENERAL, SG_BULK, "Traffic Manager: Flight is in the Past");
+ // Don't just update: check whether we need to load a new leg. etc.
+ // This update occurs for distant aircraft, so we can update the current leg
+ // and detach it from the current list of aircraft.
+ flight->update();
+ flights.erase(flights.begin()); // pop_front(), effectively
return true;
}
-
- // Departure time in the past and arrival time in the future.
- // This flight is in progress, so we need to calculate it's
- // approximate position and -if in range- create an AIAircraft
- // object for it.
- //if ((i->getDepartureTime() < now) && (i->getArrivalTime() > now))
+
+ FGAirport* dep = flight->getDepartureAirport();
+ FGAirport* arr = flight->getArrivalAirport();
+ if (!dep || !arr) {
+ return false;
+ }
+
+ double speed = 450.0;
+ if (dep != arr) {
+ totalTimeEnroute = flight->getArrivalTime() - flight->getDepartureTime();
+ if (flight->getDepartureTime() < now) {
+ elapsedTimeEnroute = now - flight->getDepartureTime();
+ remainingTimeEnroute = totalTimeEnroute - elapsedTimeEnroute;
+ double x = elapsedTimeEnroute / (double) totalTimeEnroute;
- // Part of this flight is in the future.
- if ((*i)->getArrivalTime() > now)
- {
-
- dep = (*i)->getDepartureAirport();
- arr = (*i)->getArrivalAirport ();
- if (!(dep && arr))
- return false;
-
- if (dep != arr) {
- SGVec3d a = SGVec3d::fromGeoc(SGGeoc::fromDegM(dep->getLongitude(),
- dep->getLatitude(), 1));
- SGVec3d b = SGVec3d::fromGeoc(SGGeoc::fromDegM(arr->getLongitude(),
- arr->getLatitude(), 1));
- SGVec3d _cross = cross(b, a);
-
- angle = sgACos(dot(a, b));
-
- // Okay, at this point we have the angle between departure and
- // arrival airport, in degrees. From here we can interpolate the
- // position of the aircraft by calculating the ratio between
- // total time enroute and elapsed time enroute.
-
- totalTimeEnroute = (*i)->getArrivalTime() - (*i)->getDepartureTime();
- if (now > (*i)->getDepartureTime())
- {
- //err << "Lat = " << lat << ", lon = " << lon << endl;
- //cerr << "Time diff: " << now-i->getDepartureTime() << endl;
- elapsedTimeEnroute = now - (*i)->getDepartureTime();
- remainingTimeEnroute = (*i)->getArrivalTime() - now;
- SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic Manager: Flight is in progress.");
- }
- else
- {
- lat = dep->getLatitude();
- lon = dep->getLongitude();
- elapsedTimeEnroute = 0;
- remainingTimeEnroute = totalTimeEnroute;
- SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic Manager: Flight is pending.");
- }
- angle *= ( (double) elapsedTimeEnroute/ (double) totalTimeEnroute);
- //cout << "a = " << a[0] << " " << a[1] << " " << a[2]
- // << "b = " << b[0] << " " << b[1] << " " << b[2] << endl;
- sgdMat4 matrix;
- sgdMakeRotMat4(matrix, angle, _cross.data());
- for(int j = 0; j < 3; j++) {
- for (int k = 0; k<3; k++) {
- newPos[j] += matrix[j][k]*a[k];
- }
- }
- }
- SGGeod current;
- if ((now > (*i)->getDepartureTime() && (dep != arr))) {
- current = SGGeod::fromCart(newPos);
- speed = SGGeodesy::distanceNm(current, arr->geod()) /
- ((double) remainingTimeEnroute/3600.0);
- } else {
- current = dep->geod();
- speed = 450;
- }
- SGGeod user = SGGeod::fromDegM(userLongitude, userLatitude, (*i)->getCruiseAlt());
-
- distanceToUser = SGGeodesy::distanceNm(current, user);
-
- // If distance between user and simulated aircaft is less
- // then 500nm, create this flight. At jet speeds 500 nm is roughly
- // one hour flight time, so that would be a good approximate point
- // to start a more detailed simulation of this aircraft.
- SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic manager: " << registration << " is scheduled for a flight from "
+ // current pos is based on great-circle course between departure/arrival,
+ // with percentage of distance travelled, based upon percentage of time
+ // enroute elapsed.
+ double course, az2, distanceM;
+ SGGeodesy::inverse(dep->geod(), arr->geod(), course, az2, distanceM);
+ double coveredDistance = distanceM * x;
+
+ SGGeodesy::direct(dep->geod(), course, coveredDistance, position, az2);
+
+ SG_LOG (SG_GENERAL, SG_BULK, "Traffic Manager: Flight is in progress, %=" << x);
+ speed = ((distanceM - coveredDistance) * SG_METER_TO_NM) / 3600.0;
+ } else {
+ // not departed yet
+ remainingTimeEnroute = totalTimeEnroute;
+ elapsedTimeEnroute = 0;
+ position = dep->geod();
+ SG_LOG (SG_GENERAL, SG_BULK, "Traffic Manager: Flight is pending, departure in "
+ << flight->getDepartureTime() - now << " seconds ");
+ }
+ } else {
+ // departure / arrival coincident
+ remainingTimeEnroute = totalTimeEnroute = 0.0;
+ elapsedTimeEnroute = 0;
+ position = dep->geod();
+ }
+
+ // cartesian calculations are more numerically stable over the (potentially)
+ // large distances involved here: see bug #80
+ distanceToUser = dist(userCart, SGVec3d::fromGeod(position)) * SG_METER_TO_NM;
+
+ // If distance between user and simulated aircaft is less
+ // then 500nm, create this flight. At jet speeds 500 nm is roughly
+ // one hour flight time, so that would be a good approximate point
+ // to start a more detailed simulation of this aircraft.
+ SG_LOG (SG_GENERAL, SG_BULK, "Traffic manager: " << registration << " is scheduled for a flight from "
<< dep->getId() << " to " << arr->getId() << ". Current distance to user: "
<< distanceToUser);
- if (distanceToUser < TRAFFICTOAIDISTTOSTART)
- {
- string flightPlanName = dep->getId() + string("-") + arr->getId() +
- string(".xml");
- SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic manager: Creating AIModel");
- //int alt;
- //if ((i->getDepartureTime() < now))
- //{
- // alt = i->getCruiseAlt() *100;
- // }
- //else
- //{
- // alt = dep->_elevation+19;
- // }
-
- // Only allow traffic to be created when the model path (or the AI version of mp) exists
- SGPath mp(globals->get_fg_root());
- SGPath mp_ai = mp;
-
- mp.append(modelPath);
- mp_ai.append("AI");
- mp_ai.append(modelPath);
-
- if (mp.exists() || mp_ai.exists())
- {
- FGAIAircraft *aircraft = new FGAIAircraft(this);
- aircraft->setPerformance(m_class); //"jet_transport";
- aircraft->setCompany(airline); //i->getAirline();
- aircraft->setAcType(acType); //i->getAcType();
- aircraft->setPath(modelPath.c_str());
- //aircraft->setFlightPlan(flightPlanName);
- aircraft->setLatitude(lat);
- aircraft->setLongitude(lon);
- aircraft->setAltitude((*i)->getCruiseAlt()*100); // convert from FL to feet
- aircraft->setSpeed(speed);
- aircraft->setBank(0);
+ if (distanceToUser >= TRAFFICTOAIDISTTOSTART) {
+ return true; // out of visual range, for the moment.
+ }
+
+ return createAIAircraft(flight, speed, deptime);
+}
+
+bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime)
+{
+ FGAirport* dep = flight->getDepartureAirport();
+ FGAirport* arr = flight->getArrivalAirport();
+ string flightPlanName = dep->getId() + "-" + arr->getId() + ".xml";
+ SG_LOG(SG_GENERAL, SG_INFO, "Traffic manager: Creating AIModel from:" << flightPlanName);
+
+ // Only allow traffic to be created when the model path (or the AI version of mp) exists
+ SGPath mp(globals->get_fg_root());
+ SGPath mp_ai = mp;
+
+ mp.append(modelPath);
+ mp_ai.append("AI");
+ mp_ai.append(modelPath);
+
+ if (!mp.exists() && !mp_ai.exists()) {
+ SG_LOG(SG_INPUT, SG_WARN, "TrafficManager: Could not load model " << mp.str());
+ return true;
+ }
+
+ FGAIAircraft *aircraft = new FGAIAircraft(this);
+ aircraft->setPerformance(m_class); //"jet_transport";
+ aircraft->setCompany(airline); //i->getAirline();
+ aircraft->setAcType(acType); //i->getAcType();
+ aircraft->setPath(modelPath.c_str());
+ //aircraft->setFlightPlan(flightPlanName);
+ aircraft->setLatitude(position.getLatitudeDeg());
+ aircraft->setLongitude(position.getLongitudeDeg());
+ aircraft->setAltitude(flight->getCruiseAlt()*100); // convert from FL to feet
+ aircraft->setSpeed(speedKnots);
+ aircraft->setBank(0);
- courseToDest = SGGeodesy::courseDeg(current, arr->geod());
- aircraft->SetFlightPlan(new FGAIFlightPlan(aircraft, flightPlanName, courseToDest, deptime,
- dep, arr,true, radius,
- (*i)->getCruiseAlt()*100,
- lat, lon, speed, flightType, acType,
+ courseToDest = SGGeodesy::courseDeg(position, arr->geod());
+ aircraft->SetFlightPlan(new FGAIFlightPlan(aircraft, flightPlanName, courseToDest, deptime,
+ dep, arr, true, radius,
+ flight->getCruiseAlt()*100,
+ position.getLatitudeDeg(),
+ position.getLongitudeDeg(),
+ speedKnots, flightType, acType,
airline));
- aimgr->attach(aircraft);
-
-
- AIManagerRef = aircraft->getID();
- //cerr << "Class: " << m_class << ". acType: " << acType << ". Airline: " << airline << ". Speed = " << speed << ". From " << dep->getId() << " to " << arr->getId() << ". Time Fraction = " << (remainingTimeEnroute/(double) totalTimeEnroute) << endl;
- //cerr << "Latitude : " << lat << ". Longitude : " << lon << endl;
- //cerr << "Dep : " << dep->getLatitude()<< ", "<< dep->getLongitude() << endl;
- //cerr << "Arr : " << arr->getLatitude()<< ", "<< arr->getLongitude() << endl;
- //cerr << "Time remaining = " << (remainingTimeEnroute/3600.0) << endl;
- //cerr << "Total time = " << (totalTimeEnroute/3600.0) << endl;
- //cerr << "Distance remaining = " << distanceToDest*SG_METER_TO_NM << endl;
- }
- else
- {
- SG_LOG(SG_INPUT, SG_WARN, "TrafficManager: Could not load model " << mp.str());
- }
- }
- return true;
- }
+
+
+ FGAIManager* aimgr = (FGAIManager *) globals-> get_subsystem("ai_model");
+ aimgr->attach(aircraft);
+ AIManagerRef = aircraft->getID();
+ return true;
+}
- // Both departure and arrival time are in the future, so this
- // the aircraft is parked at the departure airport.
- // Currently this status is mostly ignored, but in future
- // versions, code should go here that -if within user range-
- // positions these aircraft at parking locations at the airport.
- if (((*i)->getDepartureTime() > now) && ((*i)->getArrivalTime() > now))
- {
- dep = (*i)->getDepartureAirport();
- return true;
- }
+void FGAISchedule::scheduleFlights()
+{
+ if (!flights.empty()) {
+ return;
+ }
+
+ SG_LOG(SG_GENERAL, SG_BULK, "Scheduling for : " << modelPath << " " << registration << " " << homePort);
+ FGScheduledFlight *flight = NULL;
+ do {
+ flight = findAvailableFlight(currentDestination, flightIdentifier);
+ if (!flight) {
+ break;
}
- //cerr << "Traffic schedule got to beyond last clause" << endl;
- // EMH: prevent a warning, should this be 'true' instead?
- // DT: YES. Originally, this code couldn't be reached, but
- // when the "if(!(AIManagerManager))" clause is false we
- // fall through right to the end. This is a valid flow.
- // the actual value is pretty innocent, only it triggers
- // warning in TrafficManager::update().
- // (which was added as a sanity check for myself in the first place. :-)
- return true;
+
+ currentDestination = flight->getArrivalAirport()->getId();
+
+ time_t arr, dep;
+ dep = flight->getDepartureTime();
+ arr = flight->getArrivalTime();
+ string depT = asctime(gmtime(&dep));
+ string arrT = asctime(gmtime(&arr));
+
+ depT = depT.substr(0,24);
+ arrT = arrT.substr(0,24);
+ SG_LOG(SG_GENERAL, SG_BULK, " " << flight->getCallSign() << ":"
+ << " " << flight->getDepartureAirport()->getId() << ":"
+ << " " << depT << ":"
+ << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
+ << " " << arrT << ":");
+
+ flights.push_back(flight);
+ } while (currentDestination != homePort);
+ SG_LOG(SG_GENERAL, SG_BULK, " Done ");
}
-
bool FGAISchedule::next()
{
- FGScheduledFlightVecIterator i = flights.begin();
- (*i)->release();
- //FIXME: remove first entry,
- // load new flights until back at home airport
- // Lock loaded flights
- //sort(flights.begin(), flights.end(), compareScheduledFlights);
- // until that time
- i = flights.erase(i);
- //cerr << "Next: scheduling for : " << modelPath << " " << registration << endl;
+ if (!flights.empty()) {
+ flights.front()->release();
+ flights.erase(flights.begin());
+ }
+
FGScheduledFlight *flight = findAvailableFlight(currentDestination, flightIdentifier);
- if (flight) {
- currentDestination = flight->getArrivalAirport()->getId();
- time_t arr, dep;
- dep = flight->getDepartureTime();
- arr = flight->getArrivalTime();
- string depT = asctime(gmtime(&dep));
- string arrT = asctime(gmtime(&arr));
-
- depT = depT.substr(0,24);
- arrT = arrT.substr(0,24);
- //cerr << " " << flight->getCallSign() << ":"
- // << " " << flight->getDepartureAirport()->getId() << ":"
- // << " " << depT << ":"
- // << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
- // << " " << arrT << ":" << endl;
-
- flights.push_back(flight);
- return true;
- } else {
- return false;
+ if (!flight) {
+ return false;
}
- //cerr << "FGAISchedule :: next needs updating" << endl;
- //exit(1);
+
+ currentDestination = flight->getArrivalAirport()->getId();
+/*
+ time_t arr, dep;
+ dep = flight->getDepartureTime();
+ arr = flight->getArrivalTime();
+ string depT = asctime(gmtime(&dep));
+ string arrT = asctime(gmtime(&arr));
+
+ depT = depT.substr(0,24);
+ arrT = arrT.substr(0,24);
+ //cerr << " " << flight->getCallSign() << ":"
+ // << " " << flight->getDepartureAirport()->getId() << ":"
+ // << " " << depT << ":"
+ // << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
+ // << " " << arrT << ":" << endl;
+*/
+ flights.push_back(flight);
+ return true;
}
FGScheduledFlight* FGAISchedule::findAvailableFlight (const string ¤tDestination,
//sort(fltBegin, fltEnd, compareScheduledFlights);
//cerr << counter++ << endl;
}
- sort(fltBegin, fltEnd, compareScheduledFlights);
+ std::sort(fltBegin, fltEnd, compareScheduledFlights);
for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
//bool valid = true;
counter++;
// is departure port valid?
// is arrival port valid?
//cerr << "Ack no flight found: " << endl;
- return 0;
+ return NULL;
}
double FGAISchedule::getSpeed()
string currentDestination;
bool heavy;
FGScheduledFlightVec flights;
- float lat;
- float lon;
+ SGGeod position;
double radius;
double groundOffset;
double distanceToUser;
bool firstRun;
double courseToDest;
-
+ void scheduleFlights();
+
+ /**
+ * Transition this schedule from distant mode to AI mode;
+ * create the AIAircraft (and flight plan) and register with the AIManager
+ */
+ bool createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime);
+
public:
FGAISchedule(); // constructor
FGAISchedule(string model,
~FGAISchedule(); //destructor
- bool update(time_t now);
+ bool update(time_t now, const SGVec3d& userCart);
bool init();
double getSpeed ();
if (scheduledAircraft.size() == 0) {
return;
}
+
+ SGVec3d userCart = SGVec3d::fromGeod(SGGeod::fromDeg(
+ fgGetDouble("/position/longitude-deg"),
+ fgGetDouble("/position/latitude-deg")));
+
if(currAircraft == scheduledAircraft.end())
{
currAircraft = scheduledAircraft.begin();
}
- if (!((*currAircraft)->update(now)))
+ if (!((*currAircraft)->update(now, userCart)))
{
// NOTE: With traffic manager II, this statement below is no longer true
// after proper initialization, we shouldnt get here.
#ifdef __MINGW32__
#include <time.h>
#include <unistd.h>
+#elif defined(_MSC_VER)
+#include <io.h>
#endif
#include <stdlib.h> // atoi() atof() abs() system()
+#include <signal.h> // signal()
#include <simgear/compiler.h>
}
-std::deque<std::string> waitingTiles;
-typedef std::map<std::string,time_t> CompletedTiles;
+deque<string> waitingTiles;
+typedef map<string,time_t> CompletedTiles;
CompletedTiles completedTiles;
+netSocket theSocket;
#ifdef HAVE_SVN_CLIENT_H
}
}
+#ifdef _MSC_VER
+typedef void (__cdecl * sighandler_t)(int);
+#elif defined( __APPLE__ )
+typedef sig_t sighandler_t;
+#endif
+
+bool terminating = false;
+sighandler_t prior_signal_handlers[32];
+int termination_triggering_signals[] = {
+#ifndef _MSC_VER
+ SIGHUP, SIGINT, SIGQUIT, SIGKILL,
+#else
+ SIGINT, SIGILL, SIGFPE, SIGSEGV, SIGTERM, SIGBREAK, SIGABRT,
+#endif
+ 0}; // zero terminated
+
+void terminate_request_handler(int param) {
+ char msg[] = "\nReceived signal XX, intend to exit soon.\n"
+ "repeat the signal to force immediate termination.\n";
+ msg[17] = '0' + param / 10;
+ msg[18] = '0' + param % 10;
+ write(1, msg, sizeof(msg) - 1);
+ terminating = true;
+ signal(param, prior_signal_handlers[param]);
+ theSocket.close();
+}
+
const int nowhere = -9999;
+
// parse message
static void parse_message( const string &msg, int *lat, int *lon ) {
double dlat, dlon;
string::size_type pos = text.find( "$GPGGA" );
if ( pos == string::npos )
{
- *lat = -9999.0;
- *lon = -9999.0;
+ *lat = nowhere;
+ *lon = nowhere;
return;
}
string tmp = text.substr( pos + 7 );
void getWaitingTile() {
while ( !waitingTiles.empty() ) {
- CompletedTiles::iterator ii = completedTiles.find( waitingTiles.front() );
+ CompletedTiles::iterator ii =
+ completedTiles.find( waitingTiles.front() );
time_t now = time(0);
if ( ii == completedTiles.end() || ii->second + 600 < now ) {
sync_tree(waitingTiles.front().c_str());
int main( int argc, char **argv ) {
int port = 5501;
- char host[256] = ""; // accept messages from anyone
+ char host[256] = "localhost";
bool testing = false;
bool do_checkout(true);
int verbose(0);
// Must call this before any other net stuff
netInit( &argc,argv );
- netSocket s;
-
- if ( ! s.open( false ) ) { // open a UDP socket
+ if ( ! theSocket.open( false ) ) { // open a UDP socket
printf("error opening socket\n");
return -1;
}
- if ( s.bind( host, port ) == -1 ) {
+ if ( theSocket.bind( host, port ) == -1 ) {
printf("error binding to port %d\n", port);
return -1;
}
}
- while ( true ) { // main loop
+ for (int* sigp=termination_triggering_signals; *sigp; sigp++) {
+ prior_signal_handlers[*sigp] =
+ signal(*sigp, *terminate_request_handler);
+ if (verbose) cout << "Intercepted signal " << *sigp << endl;
+ }
+
+ while ( !terminating ) {
+ // main loop
recv_msg = false;
if ( testing ) {
// No FGFS communications
lon = -123;
recv_msg = (lat != last_lat) || (lon != last_lon);
} else {
- s.setBlocking(waitingTiles.empty());
- len = s.recv(msg, maxlen, 0);
+ if (verbose && waitingTiles.empty()) {
+ cout << "Idle; waiting for FlightGear position\n";
+ }
+ theSocket.setBlocking(waitingTiles.empty());
+ len = theSocket.recv(msg, maxlen, 0);
if (len >= 0) {
msg[len] = '\0';
recv_msg = true;
// Ignore messages where the location does not change
if ( lat != last_lat || lon != last_lon ) {
cout << "pos in msg = " << lat << "," << lon << endl;
- std::deque<std::string> oldRequests;
+ deque<string> oldRequests;
oldRequests.swap( waitingTiles );
int lat_dir, lon_dir, dist;
if ( last_lat == nowhere || last_lon == nowhere ) {
}
else if ( testing ) {
- exit( 0 );
+ terminating = true;
} else
ulSleep( 1 );
- } // while true
+ } // while !terminating
return 0;
}