7b824755ee
I have prepared a patch that: - Introduces a FGTileMgr::scenery_available method which asks the tilemanager if scenery for a given range around a lat/lon pair is already loaded and make use of that method at some -9999 meter checks. - Introduces a FGScenery::get_elevation_m method which queries the altitude at a given position. In constrast to the groundcache functions this is the best choice if you ask for one *single* altitude value. Make use of that thing in AI/ATC classes and for the current views ground level. At the current views part the groundcache is reused if possible. - The computation of the 'current groundlevel' is no longer done on the tilemanagers update since the required functions are now better seperated. Alltogether it eliminates somehow redundant terrain level computations which are now superseeded by that more finegrained functions and the existence of the groundcache. Additionally it introduces an api to commonly required functions which was very complex to do prevously.
369 lines
11 KiB
C++
369 lines
11 KiB
C++
// native_gui.cxx -- FGFS external gui data export class
|
|
//
|
|
// Written by Curtis Olson, started January 2002.
|
|
//
|
|
// Copyright (C) 2002 Curtis L. Olson - http://www.flightgear.org/~curt
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation; either version 2 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
//
|
|
// $Id$
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <simgear/debug/logstream.hxx>
|
|
#include <simgear/io/lowlevel.hxx> // endian tests
|
|
#include <simgear/io/iochannel.hxx>
|
|
#include <simgear/timing/sg_time.hxx>
|
|
|
|
#include <FDM/flight.hxx>
|
|
#include <Time/tmp.hxx>
|
|
#include <Main/fg_props.hxx>
|
|
#include <Main/globals.hxx>
|
|
#include <Scenery/scenery.hxx>
|
|
|
|
#include "native_gui.hxx"
|
|
|
|
// FreeBSD works better with this included last ... (?)
|
|
#if defined(WIN32) && !defined(__CYGWIN__)
|
|
# include <windows.h>
|
|
#else
|
|
# include <netinet/in.h> // htonl() ntohl()
|
|
#endif
|
|
|
|
|
|
// #define FG_USE_NETWORK_BYTE_ORDER
|
|
#if defined( FG_USE_NETWORK_BYTE_ORDER )
|
|
|
|
// The function htond is defined this way due to the way some
|
|
// processors and OSes treat floating point values. Some will raise
|
|
// an exception whenever a "bad" floating point value is loaded into a
|
|
// floating point register. Solaris is notorious for this, but then
|
|
// so is LynxOS on the PowerPC. By translating the data in place,
|
|
// there is no need to load a FP register with the "corruped" floating
|
|
// point value. By doing the BIG_ENDIAN test, I can optimize the
|
|
// routine for big-endian processors so it can be as efficient as
|
|
// possible
|
|
static void htond (double &x)
|
|
{
|
|
if ( sgIsLittleEndian() ) {
|
|
int *Double_Overlay;
|
|
int Holding_Buffer;
|
|
|
|
Double_Overlay = (int *) &x;
|
|
Holding_Buffer = Double_Overlay [0];
|
|
|
|
Double_Overlay [0] = htonl (Double_Overlay [1]);
|
|
Double_Overlay [1] = htonl (Holding_Buffer);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
static void htonf (float &x)
|
|
{
|
|
if ( sgIsLittleEndian() ) {
|
|
int *Float_Overlay;
|
|
int Holding_Buffer;
|
|
|
|
Float_Overlay = (int *) &x;
|
|
Holding_Buffer = Float_Overlay [0];
|
|
|
|
Float_Overlay [0] = htonl (Holding_Buffer);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
FGNativeGUI::FGNativeGUI() {
|
|
}
|
|
|
|
FGNativeGUI::~FGNativeGUI() {
|
|
}
|
|
|
|
|
|
// open hailing frequencies
|
|
bool FGNativeGUI::open() {
|
|
if ( is_enabled() ) {
|
|
SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
|
|
<< "is already in use, ignoring" );
|
|
return false;
|
|
}
|
|
|
|
SGIOChannel *io = get_io_channel();
|
|
|
|
if ( ! io->open( get_direction() ) ) {
|
|
SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
|
|
return false;
|
|
}
|
|
|
|
set_enabled( true );
|
|
|
|
cur_fdm_state->_set_Sea_level_radius( SG_EQUATORIAL_RADIUS_FT );
|
|
return true;
|
|
}
|
|
|
|
|
|
void FGProps2NetGUI( FGNetGUI *net ) {
|
|
static SGPropertyNode *nav_freq
|
|
= fgGetNode("/instrumentation/nav/frequencies/selected-mhz", true);
|
|
static SGPropertyNode *nav_target_radial
|
|
= fgGetNode("/instrumentation/nav/radials/target-radial-deg", true);
|
|
static SGPropertyNode *nav_inrange
|
|
= fgGetNode("/instrumentation/nav/in-range", true);
|
|
static SGPropertyNode *nav_loc
|
|
= fgGetNode("/instrumentation/nav/nav-loc", true);
|
|
static SGPropertyNode *nav_gs_dist_signed
|
|
= fgGetNode("/instrumentation/nav/gs-distance", true);
|
|
static SGPropertyNode *nav_loc_dist
|
|
= fgGetNode("/instrumentation/nav/nav-distance", true);
|
|
static SGPropertyNode *nav_reciprocal_radial
|
|
= fgGetNode("/instrumentation/nav/radials/reciprocal-radial-deg", true);
|
|
static SGPropertyNode *nav_gs_deflection
|
|
= fgGetNode("/instrumentation/nav/gs-needle-deflection", true);
|
|
unsigned int i;
|
|
|
|
// Version sanity checking
|
|
net->version = FG_NET_GUI_VERSION;
|
|
|
|
// Aero parameters
|
|
net->longitude = cur_fdm_state->get_Longitude();
|
|
net->latitude = cur_fdm_state->get_Latitude();
|
|
net->altitude = cur_fdm_state->get_Altitude() * SG_FEET_TO_METER;
|
|
net->phi = cur_fdm_state->get_Phi();
|
|
net->theta = cur_fdm_state->get_Theta();
|
|
net->psi = cur_fdm_state->get_Psi();
|
|
|
|
// Velocities
|
|
net->vcas = cur_fdm_state->get_V_calibrated_kts();
|
|
net->climb_rate = cur_fdm_state->get_Climb_Rate();
|
|
|
|
// Consumables
|
|
net->num_tanks = FGNetGUI::FG_MAX_TANKS;
|
|
for ( i = 0; i < net->num_tanks; ++i ) {
|
|
SGPropertyNode *node = fgGetNode("/consumables/fuel/tank", i, true);
|
|
net->fuel_quantity[i] = node->getDoubleValue("level-gal_us");
|
|
}
|
|
|
|
// Environment
|
|
net->cur_time = globals->get_time_params()->get_cur_time();
|
|
net->warp = globals->get_warp();
|
|
net->ground_elev = cur_fdm_state->get_Runway_altitude_m();
|
|
|
|
// Approach
|
|
net->tuned_freq = nav_freq->getDoubleValue();
|
|
net->nav_radial = nav_target_radial->getDoubleValue();
|
|
net->in_range = nav_inrange->getBoolValue();
|
|
|
|
if ( nav_loc->getBoolValue() ) {
|
|
// is an ILS
|
|
net->dist_nm
|
|
= nav_gs_dist_signed->getDoubleValue()
|
|
* SG_METER_TO_NM;
|
|
} else {
|
|
// is a VOR
|
|
net->dist_nm = nav_loc_dist->getDoubleValue()
|
|
* SG_METER_TO_NM;
|
|
}
|
|
|
|
net->course_deviation_deg
|
|
= nav_reciprocal_radial->getDoubleValue()
|
|
- nav_target_radial->getDoubleValue();
|
|
|
|
if ( net->course_deviation_deg < -1000.0
|
|
|| net->course_deviation_deg > 1000.0 )
|
|
{
|
|
// Sanity check ...
|
|
net->course_deviation_deg = 0.0;
|
|
}
|
|
while ( net->course_deviation_deg > 180.0 ) {
|
|
net->course_deviation_deg -= 360.0;
|
|
}
|
|
while ( net->course_deviation_deg < -180.0 ) {
|
|
net->course_deviation_deg += 360.0;
|
|
}
|
|
if ( fabs(net->course_deviation_deg) > 90.0 )
|
|
net->course_deviation_deg
|
|
= ( net->course_deviation_deg<0.0
|
|
? -net->course_deviation_deg - 180.0
|
|
: -net->course_deviation_deg + 180.0 );
|
|
|
|
if ( nav_loc->getBoolValue() ) {
|
|
// is an ILS
|
|
net->gs_deviation_deg
|
|
= nav_gs_deflection->getDoubleValue()
|
|
/ 5.0;
|
|
} else {
|
|
// is an ILS
|
|
net->gs_deviation_deg = -9999.0;
|
|
}
|
|
|
|
#if defined( FG_USE_NETWORK_BYTE_ORDER )
|
|
// Convert the net buffer to network format
|
|
net->version = htonl(net->version);
|
|
|
|
htond(net->longitude);
|
|
htond(net->latitude);
|
|
htonf(net->altitude);
|
|
htonf(net->phi);
|
|
htonf(net->theta);
|
|
htonf(net->psi);
|
|
htonf(net->vcas);
|
|
htonf(net->climb_rate);
|
|
|
|
for ( i = 0; i < net->num_tanks; ++i ) {
|
|
htonf(net->fuel_quantity[i]);
|
|
}
|
|
net->num_tanks = htonl(net->num_tanks);
|
|
|
|
net->cur_time = htonl( net->cur_time );
|
|
net->warp = htonl( net->warp );
|
|
net->ground_elev = htonl( net->ground_elev );
|
|
|
|
htonf(net->tuned_freq);
|
|
htonf(net->nav_radial);
|
|
net->in_range = htonl( net->in_range );
|
|
htonf(net->dist_nm);
|
|
htonf(net->course_deviation_deg);
|
|
htonf(net->gs_deviation_deg);
|
|
#endif
|
|
}
|
|
|
|
|
|
void FGNetGUI2Props( FGNetGUI *net ) {
|
|
unsigned int i;
|
|
|
|
#if defined( FG_USE_NETWORK_BYTE_ORDER )
|
|
// Convert to the net buffer from network format
|
|
net->version = ntohl(net->version);
|
|
|
|
htond(net->longitude);
|
|
htond(net->latitude);
|
|
htonf(net->altitude);
|
|
htonf(net->phi);
|
|
htonf(net->theta);
|
|
htonf(net->psi);
|
|
htonf(net->vcas);
|
|
htonf(net->climb_rate);
|
|
|
|
net->num_tanks = htonl(net->num_tanks);
|
|
for ( i = 0; i < net->num_tanks; ++i ) {
|
|
htonf(net->fuel_quantity[i]);
|
|
}
|
|
|
|
net->cur_time = ntohl(net->cur_time);
|
|
net->warp = ntohl(net->warp);
|
|
net->ground_elev = htonl( net->ground_elev );
|
|
|
|
htonf(net->tuned_freq);
|
|
htonf(net->nav_radial);
|
|
net->in_range = htonl( net->in_range );
|
|
htonf(net->dist_nm);
|
|
htonf(net->course_deviation_deg);
|
|
htonf(net->gs_deviation_deg);
|
|
#endif
|
|
|
|
if ( net->version == FG_NET_GUI_VERSION ) {
|
|
// cout << "pos = " << net->longitude << " " << net->latitude << endl;
|
|
// cout << "sea level rad = " << cur_fdm_state->get_Sea_level_radius()
|
|
// << endl;
|
|
cur_fdm_state->_updateGeodeticPosition( net->latitude,
|
|
net->longitude,
|
|
net->altitude
|
|
* SG_METER_TO_FEET );
|
|
cur_fdm_state->_set_Euler_Angles( net->phi,
|
|
net->theta,
|
|
net->psi );
|
|
|
|
cur_fdm_state->_set_V_calibrated_kts( net->vcas );
|
|
cur_fdm_state->_set_Climb_Rate( net->climb_rate );
|
|
|
|
for (i = 0; i < net->num_tanks; ++i ) {
|
|
SGPropertyNode * node
|
|
= fgGetNode("/consumables/fuel/tank", i, true);
|
|
node->setDoubleValue("level-gal_us", net->fuel_quantity[i] );
|
|
}
|
|
|
|
if ( net->cur_time ) {
|
|
fgSetLong("/sim/time/cur-time-override", net->cur_time);
|
|
}
|
|
|
|
globals->set_warp( net->warp );
|
|
|
|
// Approach
|
|
fgSetDouble( "/instrumentation/nav[0]/frequencies/selected-mhz",
|
|
net->tuned_freq );
|
|
fgSetBool( "/instrumentation/nav[0]/in-range", net->in_range );
|
|
fgSetDouble( "/instrumentation/dme/distance-nm", net->dist_nm );
|
|
fgSetDouble( "/instrumentation/nav[0]/heading-needle-deflection",
|
|
net->course_deviation_deg );
|
|
fgSetDouble( "/instrumentation/nav[0]/gs-needle-deflection",
|
|
net->gs_deviation_deg );
|
|
} else {
|
|
SG_LOG( SG_IO, SG_ALERT,
|
|
"Error: version mismatch in FGNetNativeGUI2Props()" );
|
|
SG_LOG( SG_IO, SG_ALERT,
|
|
"\tread " << net->version << " need " << FG_NET_GUI_VERSION );
|
|
SG_LOG( SG_IO, SG_ALERT,
|
|
"\tNeed to upgrade net_fdm.hxx and recompile." );
|
|
}
|
|
}
|
|
|
|
|
|
// process work for this port
|
|
bool FGNativeGUI::process() {
|
|
SGIOChannel *io = get_io_channel();
|
|
int length = sizeof(buf);
|
|
|
|
if ( get_direction() == SG_IO_OUT ) {
|
|
// cout << "size of cur_fdm_state = " << length << endl;
|
|
FGProps2NetGUI( &buf );
|
|
if ( ! io->write( (char *)(& buf), length ) ) {
|
|
SG_LOG( SG_IO, SG_ALERT, "Error writing data." );
|
|
return false;
|
|
}
|
|
} else if ( get_direction() == SG_IO_IN ) {
|
|
if ( io->get_type() == sgFileType ) {
|
|
if ( io->read( (char *)(& buf), length ) == length ) {
|
|
SG_LOG( SG_IO, SG_DEBUG, "Success reading data." );
|
|
FGNetGUI2Props( &buf );
|
|
}
|
|
} else {
|
|
while ( io->read( (char *)(& buf), length ) == length ) {
|
|
SG_LOG( SG_IO, SG_DEBUG, "Success reading data." );
|
|
FGNetGUI2Props( &buf );
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// close the channel
|
|
bool FGNativeGUI::close() {
|
|
SGIOChannel *io = get_io_channel();
|
|
|
|
set_enabled( false );
|
|
|
|
if ( ! io->close() ) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|