first commit

This commit is contained in:
Your Name
2022-10-20 20:29:11 +08:00
commit 4d531f8044
3238 changed files with 1387862 additions and 0 deletions

50
utils/CMakeLists.txt Normal file
View File

@@ -0,0 +1,50 @@
# win32 is just excluded because of not having argument parsing there ...
if(RTI_FOUND AND NOT WIN32)
add_subdirectory(fgai)
endif()
if(ENABLE_FGELEV)
add_subdirectory(fgelev)
endif()
if(WITH_FGPANEL)
add_subdirectory(fgpanel)
endif()
if(ENABLE_FGVIEWER)
add_subdirectory(fgviewer)
endif()
if(ENABLE_GPSSMOOTH)
add_subdirectory(GPSsmooth)
endif()
if(ENABLE_TERRASYNC)
add_subdirectory(TerraSync)
endif()
if(ENABLE_FGCOM)
add_subdirectory(fgcom)
endif()
if(ENABLE_STGMERGE)
add_subdirectory(stgmerge)
endif()
if(ENABLE_TRAFFIC)
add_subdirectory(traffic)
endif()
if (ENABLE_FGQCANVAS)
if(Qt5Core_VERSION VERSION_EQUAL 5.7 OR Qt5Core_VERSION VERSION_GREATER 5.7)
add_subdirectory(fgqcanvas)
else()
message(WARNING "FGQCanvas enabled, but Qt >= 5.7 is required for this feature")
endif()
endif()
if(ENABLE_DEMCONVERT)
if(GDALFOUND)
add_subdirectory(demconvert)
endif()
endif()

View File

@@ -0,0 +1,41 @@
add_executable(GPSsmooth
GPSsmooth.cxx GPSsmooth.hxx
gps_main.cxx
)
add_executable(MIDGsmooth
MIDG-II.cxx MIDG-II.hxx
MIDG_main.cxx
)
add_executable(UGsmooth
UGear.cxx UGear.hxx
UGear_command.cxx UGear_command.hxx
UGear_telnet.cxx UGear_telnet.hxx
UGear_main.cxx
)
target_include_directories(GPSsmooth PRIVATE ${PLIB_INCLUDE_DIR})
target_include_directories(MIDGsmooth PRIVATE ${PLIB_INCLUDE_DIR})
target_include_directories(UGsmooth PRIVATE ${PLIB_INCLUDE_DIR})
target_link_libraries(GPSsmooth
SimGearCore
${PLIB_SG_LIBRARY}
${PLIB_UL_LIBRARY}
)
target_link_libraries(MIDGsmooth
SimGearCore
${PLIB_SG_LIBRARY}
${PLIB_UL_LIBRARY}
)
target_link_libraries(UGsmooth
SimGearCore
${PLIB_SG_LIBRARY}
${PLIB_UL_LIBRARY}
${ZLIB_LIBRARY}
)
install(TARGETS GPSsmooth MIDGsmooth UGsmooth RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

View File

@@ -0,0 +1,166 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/compiler.h>
#include <iostream>
#include <simgear/constants.h>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/misc/sg_path.hxx>
#include "GPSsmooth.hxx"
using std::cout;
using std::endl;
GPSTrack::GPSTrack() {};
GPSTrack::~GPSTrack() {};
// load the specified file, return the number of records loaded
int GPSTrack::load( const std::string &file ) {
int count = 0;
data.clear();
// openg the file
sg_gzifstream in( SGPath::fromLocal8Bit(file.c_str()) );
if ( !in.is_open() ) {
cout << "Cannot open file: " << file << endl;
return 0;
}
std::vector <std::string> tokens;
GPSPoint p;
while ( ! in.eof() ) {
char tmp[2049];
in.getline(tmp, 2048);
tokens.clear();
tokens = simgear::strutils::split(tmp, ",");
int dd;
double raw, min;
if ( tokens[0] == "$GPRMC" && tokens.size() == 13 ) {
double raw_time = atof(tokens[1].c_str());
GPSTime gps_time = GPSTime( raw_time );
if ( (gps_time.get_time() > p.gps_time.get_time()) &&
(p.gps_time.get_time() > 1.0) )
{
// new data cycle store last data before continuing
data.push_back( p );
count++;
}
p.gps_time = gps_time;
raw = atof( tokens[3].c_str() );
dd = (int)(raw / 100.00);
min = raw - dd * 100.0;
p.lat_deg = dd + min / 60.0;
if ( tokens[4] == "S" ) {
p.lat_deg = -p.lat_deg;
}
raw = atof( tokens[5].c_str() );
dd = (int)(raw / 100.00);
min = raw - dd * 100.0;
p.lon_deg = dd + min / 60.0;
if ( tokens[6] == "W" ) {
p.lon_deg = -p.lon_deg;
}
static double max_speed = 0.0;
p.speed_kts = atof( tokens[7].c_str() );
if ( p.speed_kts > max_speed ) {
max_speed = p.speed_kts;
cout << "max speed = " << max_speed << endl;
}
p.course_true = atof( tokens[8].c_str() ) * SGD_DEGREES_TO_RADIANS;
} else if ( tokens[0] == "$GPGGA" && tokens.size() == 15 ) {
double raw_time = atof(tokens[1].c_str());
GPSTime gps_time = GPSTime( raw_time );
if ( fabs(gps_time.get_time() - p.gps_time.get_time()) > 0.0001 &&
(p.gps_time.get_time() > 1.0) ) {
// new data cycle store last data before continuing
data.push_back( p );
count++;
}
p.gps_time = gps_time;
raw = atof( tokens[2].c_str() );
dd = (int)(raw / 100.00);
min = raw - dd * 100.0;
p.lat_deg = dd + min / 60.0;
if ( tokens[3] == "S" ) {
p.lat_deg = -p.lat_deg;
}
raw = atof( tokens[4].c_str() );
dd = (int)(raw / 100.00);
min = raw - dd * 100.0;
p.lon_deg = dd + min / 60.0;
if ( tokens[5] == "W" ) {
p.lon_deg = -p.lon_deg;
}
p.fix_quality = atoi( tokens[6].c_str() );
p.num_satellites = atoi( tokens[7].c_str() );
p.hdop = atof( tokens[8].c_str() );
static double max_alt = 0.0;
double alt = atof( tokens[9].c_str() );
if ( alt > max_alt ) {
max_alt = alt;
cout << "max alt = " << max_alt << endl;
}
if ( tokens[10] == "F" || tokens[10] == "f" ) {
alt *= SG_FEET_TO_METER;
}
p.altitude_msl = alt;
}
}
return count;
}
static double interp( double a, double b, double p, bool rotational = false ) {
double diff = b - a;
if ( rotational ) {
// special handling of rotational data
if ( diff > SGD_PI ) {
diff -= SGD_2PI;
} else if ( diff < -SGD_PI ) {
diff += SGD_2PI;
}
}
return a + diff * p;
}
GPSPoint GPSInterpolate( const GPSPoint A, const GPSPoint B,
const double percent ) {
GPSPoint p;
p.gps_time = GPSTime((int)interp(A.gps_time.get_time(),
B.gps_time.get_time(),
percent));
p.lat_deg = interp(A.lat_deg, B.lat_deg, percent);
p.lon_deg = interp(A.lon_deg, B.lon_deg, percent);
p.fix_quality = (int)interp(A.fix_quality, B.fix_quality, percent);
p.num_satellites = (int)interp(A.num_satellites, B.num_satellites, percent);
p.hdop = interp(A.hdop, B.hdop, percent);
p.altitude_msl = interp(A.altitude_msl, B.altitude_msl, percent);
p.speed_kts = interp(A.speed_kts, B.speed_kts, percent);
p.course_true = interp(A.course_true, B.course_true, percent, true);
return p;
}

View File

@@ -0,0 +1,102 @@
#pragma once
#include <simgear/compiler.h>
#include <iostream>
#include <string>
#include <vector>
// encapsulate a gps integer time (fixme, assumes all times in a track
// are from the same day, so we don't handle midnight roll over)
class GPSTime {
public:
double seconds;
inline GPSTime( const int hh, const int mm, const double ss ) {
seconds = hh*3600 + mm*60 + ss;
}
inline GPSTime( const double gpstime ) {
double tmp = gpstime;
int hh = (int)(tmp / 10000);
tmp -= hh * 10000;
int mm = (int)(tmp / 100);
tmp -= mm * 100;
double ss = tmp;
seconds = hh*3600 + mm*60 + ss;
// std::cout << gpstime << " = " << seconds << std::endl;
}
inline ~GPSTime() {}
inline double get_time() const { return seconds; }
inline double diff_sec( const GPSTime t ) const {
return seconds - t.seconds;
}
};
// encapsulate the interesting gps data for a moment in time
class GPSPoint {
public:
GPSTime gps_time;
double lat_deg;
double lon_deg;
int fix_quality;
int num_satellites;
double hdop;
double altitude_msl;
double speed_kts;
double course_true;
GPSPoint() :
gps_time(GPSTime(0,0,0)),
lat_deg(0.0),
lon_deg(0.0),
fix_quality(0),
num_satellites(0),
hdop(0.0),
altitude_msl(0.0),
speed_kts(0.0),
course_true(0.0)
{ }
inline double get_time() const { return gps_time.get_time(); }
};
// Manage a saved gps log (track file)
class GPSTrack {
private:
std::vector <GPSPoint> data;
public:
GPSTrack();
~GPSTrack();
int load( const std::string &file );
inline int size() const { return data.size(); }
inline GPSPoint get_point( const unsigned int i )
{
if ( i < data.size() ) {
return data[i];
} else {
return GPSPoint();
}
}
};
GPSPoint GPSInterpolate( const GPSPoint A, const GPSPoint B,
const double percent );

586
utils/GPSsmooth/MIDG-II.cxx Normal file
View File

@@ -0,0 +1,586 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/compiler.h>
#include <iostream>
#include <simgear/constants.h>
#include <simgear/io/sg_file.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/misc/stdint.hxx>
#include "MIDG-II.hxx"
using std::cout;
using std::endl;
MIDGTrack::MIDGTrack() {};
MIDGTrack::~MIDGTrack() {};
/*
* Unused function
*/
#if(0)
static uint32_t read_swab( char *buf, size_t offset, size_t size ) {
uint32_t result = 0;
char *ptr = buf + offset;
// MIDG data is big endian so swap if needed.
if ( sgIsLittleEndian() ) {
if ( size == 4 ) {
sgEndianSwap( (uint32_t *)ptr );
} else if ( size == 2 ) {
sgEndianSwap( (uint16_t *)ptr );
}
}
if ( size == 4 ) {
result = *(uint32_t *)ptr;
} else if ( size == 2 ) {
result = *(uint16_t *)ptr;
} else if ( size == 1 ) {
result = *(uint8_t *)ptr;
} else {
cout << "unknown size in read_swab()" << endl;
}
return result;
}
#endif
static bool validate_cksum( uint8_t id, uint8_t size, char *buf,
uint8_t cksum0, uint8_t cksum1 )
{
uint8_t c0 = 0;
uint8_t c1 = 0;
c0 += id;
c1 += c0;
// cout << "c0 = " << (unsigned int)c0 << " c1 = " << (unsigned int)c1 << endl;
c0 += size;
c1 += c0;
// cout << "c0 = " << (unsigned int)c0 << " c1 = " << (unsigned int)c1 << endl;
for ( uint8_t i = 0; i < size; i++ ) {
c0 += (uint8_t)buf[i];
c1 += c0;
// cout << "c0 = " << (unsigned int)c0 << " c1 = " << (unsigned int)c1
// << " [" << (unsigned int)buf[i] << "]" << endl;
}
// cout << "c0 = " << (unsigned int)c0 << " (" << (unsigned int)cksum0
// << ") c1 = " << (unsigned int)c1 << " (" << (unsigned int)cksum1
// << ")" << endl;
if ( c0 == cksum0 && c1 == cksum1 ) {
return true;
} else {
return false;
}
}
void MIDGTrack::parse_msg( const int id, char *buf, MIDGpos *pos, MIDGatt *att )
{
/*
* Completely unused parser results. Removed from compiling to remove the warnings
*/
#if(0)
if ( id == 1 ) {
uint32_t ts;
uint16_t status;
int16_t temp;
// cout << "message 1 =" << endl;
// timestamp
ts = (uint32_t)read_swab( buf, 0, 4 );
// cout << " time stamp = " << ts << endl;
// status
status = (uint16_t)read_swab( buf, 4, 2 );
// cout << " status = " << status << endl;
// temp
temp = (int16_t)read_swab( buf, 6, 2 );
// cout << " temp = " << temp << endl;
} else if ( id == 2 ) {
uint32_t ts;
int16_t p, q, r;
int16_t ax, ay, az;
int16_t mx, my, mz;
uint8_t flags;
// cout << "message 2 =" << endl;
// timestamp
ts = (uint32_t)read_swab( buf, 0, 4 );
// cout << " time stamp = " << ts << endl;
// p, q, r
p = (int16_t)read_swab( buf, 4, 2 );
q = (int16_t)read_swab( buf, 6, 2 );
r = (int16_t)read_swab( buf, 8, 2 );
// cout << " pqr = " << p << "," << q << "," << r << endl;
// ax, ay, az
ax = (int16_t)read_swab( buf, 10, 2 );
ay = (int16_t)read_swab( buf, 12, 2 );
az = (int16_t)read_swab( buf, 14, 2 );
// cout << " ax ay az = " << ax << "," << ay << "," << az << endl;
// mx, my, mz
mx = (int16_t)read_swab( buf, 16, 2 );
my = (int16_t)read_swab( buf, 18, 2 );
mz = (int16_t)read_swab( buf, 20, 2 );
// cout << " mx my mz = " << mx << "," << my << "," << mz << endl;
// flags
flags = (uint8_t)read_swab( buf, 22, 1 );
// cout << " GPS 1PPS flag = " << (int)(flags & (1 << 6))
// << " Timestamp is gps = " << (int)(flags & (1 << 7)) << endl;
} else if ( id == 3 ) {
uint32_t ts;
int16_t mx, my, mz;
uint8_t flags;
// cout << "message 3 =" << endl;
// timestamp
ts = (uint32_t)read_swab( buf, 0, 4 );
// cout << " time stamp = " << ts << endl;
// mx, my, mz
mx = (int16_t)read_swab( buf, 4, 2 );
my = (int16_t)read_swab( buf, 6, 2 );
mz = (int16_t)read_swab( buf, 8, 2 );
// cout << " mx my mz = " << mx << "," << my << "," << mz << endl;
// flags
flags = (uint8_t)read_swab( buf, 10, 1 );
// cout << " GPS 1PPS flag = " << (int)(flags & (1 << 6)) << endl;
} else if ( id == 10 ) {
uint32_t ts;
int16_t p, q, r;
int16_t ax, ay, az;
int16_t yaw, pitch, roll;
int32_t Qw, Qx, Qy, Qz;
uint8_t flags;
// cout << "message 10 =" << endl;
// timestamp
ts = (uint32_t)read_swab( buf, 0, 4 );
// cout << " att time stamp = " << ts << endl;
att->midg_time = MIDGTime( ts );
// p, q, r
p = (int16_t)read_swab( buf, 4, 2 );
q = (int16_t)read_swab( buf, 6, 2 );
r = (int16_t)read_swab( buf, 8, 2 );
// cout << " pqr = " << p << "," << q << "," << r << endl;
// ax, ay, az
ax = (int16_t)read_swab( buf, 10, 2 );
ay = (int16_t)read_swab( buf, 12, 2 );
az = (int16_t)read_swab( buf, 14, 2 );
// cout << " ax ay az = " << ax << "," << ay << "," << az << endl;
// yaw, pitch, roll
yaw = (int16_t)read_swab( buf, 16, 2 );
pitch = (int16_t)read_swab( buf, 18, 2 );
roll = (int16_t)read_swab( buf, 20, 2 );
// cout << " yaw, pitch, roll = " << yaw << "," << pitch << ","
// << roll << endl;
att->yaw_rad = ( (double)yaw / 100.0 ) * SG_PI / 180.0;
att->pitch_rad = ( (double)pitch / 100.0 ) * SG_PI / 180.0;
att->roll_rad = ( (double)roll / 100.0 ) * SG_PI / 180.0;
// Qw, Qx, Qy, Qz
Qw = (int32_t)read_swab( buf, 22, 4 );
Qx = (int32_t)read_swab( buf, 26, 4 );
Qy = (int32_t)read_swab( buf, 30, 4 );
Qz = (int32_t)read_swab( buf, 34, 4 );
// cout << " Qw,Qx,Qy,Qz = " << Qw << "," << Qx << "," << Qy << ","
// << Qz << endl;
// flags
flags = (uint8_t)read_swab( buf, 38, 1 );
// cout << " External hdg measurement applied = "
// << (int)(flags & (1 << 3)) << endl
// << " Magnatometer measurement applied = "
// << (int)(flags & (1 << 4)) << endl
// << " DGPS = " << (int)(flags & (1 << 5)) << endl
// << " Timestamp is gps = " << (int)(flags & (1 << 6)) << endl
// << " INS mode = " << (int)(flags & (1 << 7))
// << endl;
} else if ( id == 12 ) {
uint32_t ts;
int32_t posx, posy, posz;
int32_t velx, vely, velz;
uint8_t flags;
// cout << "message 12 =" << endl;
// timestamp
ts = (uint32_t)read_swab( buf, 0, 4 );
// cout << " pos time stamp = " << ts << endl;
pos->midg_time = MIDGTime( ts );
// posx, posy, posz
posx = (int32_t)read_swab( buf, 4, 4 );
posy = (int32_t)read_swab( buf, 8, 4 );
posz = (int32_t)read_swab( buf, 12, 4 );
// cout << " pos = " << posx << "," << posy << "," << posz << endl;
double xyz[3];
xyz[0] = (double)posx/100; xyz[1] = (double)posy/100; xyz[2] = (double)posz/100;
double lat, lon, alt;
sgCartToGeod(xyz, &lat, &lon, &alt);
pos->lat_deg = lat * 180.0 / SG_PI;
pos->lon_deg = lon * 180.0 / SG_PI;
pos->altitude_msl = alt;
// cout << " lon = " << pos->lon_deg << " lat = " << pos->lat_deg
// << " alt = " << pos->altitude_msl << endl;
// velx, vely, velz
velx = (int32_t)read_swab( buf, 16, 4 );
vely = (int32_t)read_swab( buf, 20, 4 );
velz = (int32_t)read_swab( buf, 24, 4 );
// cout << " vel = " << velx << "," << vely << "," << velz << endl;
double tmp1 = velx*velx + vely*vely + velz*velz;
double vel_cms = sqrt( tmp1 );
double vel_ms = vel_cms / 100.0;
pos->speed_kts = vel_ms * SG_METER_TO_NM * 3600;
// flags
flags = (uint8_t)read_swab( buf, 28, 1 );
// cout << " ENU pos rel to 1st fix = " << (int)(flags & (1 << 0)) << endl
// << " Velocity format = " << (int)(flags & (1 << 1)) << endl
// << " bit 2 = " << (int)(flags & (1 << 2)) << endl
// << " bit 3 = " << (int)(flags & (1 << 3)) << endl
// << " GPS pos/vel valid = " << (int)(flags & (1 << 4)) << endl
// << " DGPS = " << (int)(flags & (1 << 5)) << endl
// << " Timestamp is gps = " << (int)(flags & (1 << 6)) << endl
// << " Solution src (0=gps, 1=ins) = " << (int)(flags & (1 << 7))
// << endl;
} else if ( id == 20 ) {
uint32_t gps_ts, gps_week;
uint16_t details;
int32_t gps_posx, gps_posy, gps_posz;
int32_t gps_velx, gps_vely, gps_velz;
int16_t pdop, pacc, sacc;
// cout << "message 20 =" << endl;
// timestamp -- often slightly off from midg time stamp so
// let's not use gps ts to determine if we need to push the
// previous data or not, just roll it into the current data
// independent of time stamp.
gps_ts = (uint32_t)read_swab( buf, 0, 4 );
// pt->midg_time = MIDGTime( ts );
gps_week = (uint16_t)read_swab( buf, 4, 2 );
// cout << " gps time stamp = " << gps_ts << " week = " << gps_week
// << endl;
// details
details = (uint16_t)read_swab( buf, 6, 2 );
// cout << " details = " << details << endl;
// gps_posx, gps_posy, gps_posz
gps_posx = (int32_t)read_swab( buf, 8, 4 );
gps_posy = (int32_t)read_swab( buf, 12, 4 );
gps_posz = (int32_t)read_swab( buf, 16, 4 );
// cout << " gps_pos = " << gps_posx << "," << gps_posy << ","
// << gps_posz << endl;
// gps_velx, gps_vely, gps_velz
gps_velx = (int32_t)read_swab( buf, 20, 4 );
gps_vely = (int32_t)read_swab( buf, 24, 4 );
gps_velz = (int32_t)read_swab( buf, 28, 4 );
// cout << " gps_vel = " << gps_velx << "," << gps_vely << ","
// << gps_velz << endl;
// position dop
pdop = (uint16_t)read_swab( buf, 32, 2 );
// cout << " pdop = " << pdop << endl;
// position accuracy
pacc = (uint16_t)read_swab( buf, 34, 2 );
// cout << " pacc = " << pacc << endl;
// speed accuracy
sacc = (uint16_t)read_swab( buf, 36, 2 );
// cout << " sacc = " << sacc << endl;
} else {
cout << "unknown id = " << id << endl;
}
#endif
}
// load the specified file, return the number of records loaded
bool MIDGTrack::load( const string &file ) {
int count = 0;
MIDGpos pos;
MIDGatt att;
uint32_t pos_time = 1;
uint32_t att_time = 1;
pos_data.clear();
att_data.clear();
// open the file
SGFile input( file );
if ( !input.open( SG_IO_IN ) ) {
cout << "Cannot open file: " << file << endl;
return false;
}
while ( ! input.eof() ) {
// cout << "looking for next message ..." << endl;
int id = next_message( &input, NULL, &pos, &att );
count++;
if ( id == 10 ) {
if ( att.get_msec() > att_time ) {
att_data.push_back( att );
att_time = att.get_msec();
} else {
cout << "oops att back in time" << endl;
}
} else if ( id == 12 ) {
if ( pos.get_msec() > pos_time ) {
pos_data.push_back( pos );
pos_time = pos.get_msec();
} else {
cout << "oops pos back in time" << endl;
}
}
}
cout << "processed " << count << " messages" << endl;
return true;
}
// attempt to work around some system dependent issues. Our read can
// return < data than we want.
int myread( SGIOChannel *ch, SGIOChannel *log, char *buf, int length ) {
bool myeof = false;
int result = 0;
if ( !myeof ) {
result = ch->read( buf, length );
// cout << "wanted " << length << " read " << result << " bytes" << endl;
if ( ch->get_type() == sgFileType ) {
myeof = ((SGFile *)ch)->eof();
}
}
if ( result > 0 && log != NULL ) {
log->write( buf, result );
}
return result;
}
// attempt to work around some system dependent issues. Our read can
// return < data than we want.
int serial_read( SGSerialPort *serial, char *buf, int length ) {
int result = 0;
int bytes_read = 0;
char *tmp = buf;
while ( bytes_read < length ) {
result = serial->read_port( tmp, length - bytes_read );
bytes_read += result;
tmp += result;
// cout << " read " << bytes_read << " of " << length << endl;
}
return bytes_read;
}
// load the next message of a real time data stream
int MIDGTrack::next_message( SGIOChannel *ch, SGIOChannel *log,
MIDGpos *pos, MIDGatt *att )
{
char tmpbuf[256];
char savebuf[256];
// cout << "in next_message()" << endl;
bool myeof = false;
// scan for sync characters
uint8_t sync0, sync1;
myread( ch, log, tmpbuf, 1 ); sync0 = (unsigned char)tmpbuf[0];
myread( ch, log, tmpbuf, 1 ); sync1 = (unsigned char)tmpbuf[0];
while ( (sync0 != 129 || sync1 != 161) && !myeof ) {
sync0 = sync1;
myread( ch, log, tmpbuf, 1 ); sync1 = (unsigned char)tmpbuf[0];
// cout << "scanning for start of message "
// << (unsigned int)sync0 << " " << (unsigned int)sync1
// << ", eof = " << ch->eof() << endl;
if ( ch->get_type() == sgFileType ) {
myeof = ((SGFile *)ch)->eof();
}
}
// cout << "found start of message ..." << endl;
// read message id and size
myread( ch, log, tmpbuf, 1 ); uint8_t id = (unsigned char)tmpbuf[0];
myread( ch, log, tmpbuf, 1 ); uint8_t size = (unsigned char)tmpbuf[0];
// cout << "message = " << (int)id << " size = " << (int)size << endl;
// load message
if ( ch->get_type() == sgFileType ) {
int count = myread( ch, log, savebuf, size );
if ( count != size ) {
cout << "ERROR: didn't read enough bytes!" << endl;
}
} else {
#ifdef READ_ONE_BY_ONE
for ( int i = 0; i < size; ++i ) {
myread( ch, log, tmpbuf, 1 ); savebuf[i] = tmpbuf[0];
}
#else
myread( ch, log, savebuf, size );
#endif
}
// read checksum
myread( ch, log, tmpbuf, 1 ); uint8_t cksum0 = (unsigned char)tmpbuf[0];
myread( ch, log, tmpbuf, 1 ); uint8_t cksum1 = (unsigned char)tmpbuf[0];
if ( validate_cksum( id, size, savebuf, cksum0, cksum1 ) ) {
parse_msg( id, savebuf, pos, att );
return id;
}
cout << "Check sum failure!" << endl;
return -1;
}
// load the next message of a real time data stream
int MIDGTrack::next_message( SGSerialPort *serial, SGIOChannel *log,
MIDGpos *pos, MIDGatt *att )
{
char tmpbuf[256];
char savebuf[256];
cout << "in next_message()" << endl;
bool myeof = false;
// scan for sync characters
uint8_t sync0, sync1;
serial_read( serial, tmpbuf, 2 );
sync0 = (unsigned char)tmpbuf[0];
sync1 = (unsigned char)tmpbuf[1];
while ( (sync0 != 129 || sync1 != 161) && !myeof ) {
sync0 = sync1;
serial_read( serial, tmpbuf, 1 ); sync1 = (unsigned char)tmpbuf[0];
cout << "scanning for start of message "
<< (unsigned int)sync0 << " " << (unsigned int)sync1
<< endl;
}
cout << "found start of message ..." << endl;
// read message id and size
serial_read( serial, tmpbuf, 2 );
uint8_t id = (unsigned char)tmpbuf[0];
uint8_t size = (unsigned char)tmpbuf[1];
// cout << "message = " << (int)id << " size = " << (int)size << endl;
// load message
serial_read( serial, savebuf, size );
// read checksum
serial_read( serial, tmpbuf, 2 );
uint8_t cksum0 = (unsigned char)tmpbuf[0];
uint8_t cksum1 = (unsigned char)tmpbuf[1];
if ( validate_cksum( id, size, savebuf, cksum0, cksum1 ) ) {
parse_msg( id, savebuf, pos, att );
//
// FIXME
// WRITE DATA TO LOG FILE
//
return id;
}
cout << "Check sum failure!" << endl;
return -1;
}
static double interp( double a, double b, double p, bool rotational = false ) {
double diff = b - a;
if ( rotational ) {
// special handling of rotational data
if ( diff > SGD_PI ) {
diff -= SGD_2PI;
} else if ( diff < -SGD_PI ) {
diff += SGD_2PI;
}
}
return a + diff * p;
}
MIDGpos MIDGInterpPos( const MIDGpos A, const MIDGpos B, const double percent )
{
MIDGpos p;
p.midg_time = MIDGTime((uint32_t)interp(A.midg_time.get_msec(),
B.midg_time.get_msec(),
percent));
p.lat_deg = interp(A.lat_deg, B.lat_deg, percent);
p.lon_deg = interp(A.lon_deg, B.lon_deg, percent);
p.altitude_msl = interp(A.altitude_msl, B.altitude_msl, percent);
p.fix_quality = (int)interp(A.fix_quality, B.fix_quality, percent);
p.num_satellites = (int)interp(A.num_satellites, B.num_satellites, percent);
p.hdop = interp(A.hdop, B.hdop, percent);
p.speed_kts = interp(A.speed_kts, B.speed_kts, percent);
p.course_true = interp(A.course_true, B.course_true, percent, true);
return p;
}
MIDGatt MIDGInterpAtt( const MIDGatt A, const MIDGatt B, const double percent )
{
MIDGatt p;
p.midg_time = MIDGTime((uint32_t)interp(A.midg_time.get_msec(),
B.midg_time.get_msec(),
percent));
p.yaw_rad = interp(A.yaw_rad, B.yaw_rad, percent, true);
p.pitch_rad = interp(A.pitch_rad, B.pitch_rad, percent, true);
p.roll_rad = interp(A.roll_rad, B.roll_rad, percent, true);
return p;
}

157
utils/GPSsmooth/MIDG-II.hxx Normal file
View File

@@ -0,0 +1,157 @@
#pragma once
#include <simgear/compiler.h>
#include <iostream>
#include <string>
#include <vector>
#include <simgear/misc/stdint.hxx>
#include <simgear/io/iochannel.hxx>
#include <simgear/serial/serial.hxx>
// encapsulate a midg integer time (fixme, assumes all times in a track
// are from the same day, so we don't handle midnight roll over)
class MIDGTime {
public:
uint32_t msec;
double seconds;
inline MIDGTime( const int dd, const int hh, const int mm,
const double ss )
{
seconds = dd*86400.0 + hh*3600.0 + mm*60.0 + ss;
msec = (uint32_t)(seconds * 1000);
}
inline MIDGTime( const uint32_t midgtime_msec ) {
msec = midgtime_msec;
seconds = (double)midgtime_msec / 1000.0;
// std::cout << midgtime << " = " << seconds << std::endl;
}
inline ~MIDGTime() {}
inline double get_seconds() const { return seconds; }
inline uint32_t get_msec() const { return msec; }
inline double diff_seconds( const MIDGTime t ) const {
return seconds - t.seconds;
}
};
// base class for MIDG data types
class MIDGpoint {
public:
MIDGTime midg_time;
MIDGpoint() :
midg_time(MIDGTime(0))
{ }
inline double get_seconds() const { return midg_time.get_seconds(); }
inline uint32_t get_msec() const { return midg_time.get_msec(); }
};
// encapsulate the interesting midg data for a moment in time
class MIDGpos : public MIDGpoint {
public:
double lat_deg;
double lon_deg;
double altitude_msl;
int fix_quality;
int num_satellites;
double hdop;
double speed_kts;
double course_true;
MIDGpos() :
lat_deg(0.0),
lon_deg(0.0),
altitude_msl(0.0),
fix_quality(0),
num_satellites(0),
hdop(0.0),
speed_kts(0.0),
course_true(0.0)
{ }
};
// encapsulate the interesting midg data for a moment in time
class MIDGatt : public MIDGpoint {
public:
double yaw_rad;
double pitch_rad;
double roll_rad;
MIDGatt() :
yaw_rad(0.0),
pitch_rad(0.0),
roll_rad(0.0)
{ }
};
// Manage a saved midg log (track file)
class MIDGTrack {
private:
std::vector <MIDGpos> pos_data;
std::vector <MIDGatt> att_data;
// parse message and put current data into vector if message has a
// newer time stamp than existing data.
void parse_msg( const int id, char *buf, MIDGpos *pos, MIDGatt *att );
public:
MIDGTrack();
~MIDGTrack();
// read/parse the next message from the specified data stream,
// returns id # if a valid message found.
int next_message( SGIOChannel *ch, SGIOChannel *log,
MIDGpos *pos, MIDGatt *att );
int next_message( SGSerialPort *serial, SGIOChannel *log,
MIDGpos *pos, MIDGatt *att );
// load the named file into internal buffers
bool load( const std::string &file );
inline int pos_size() const { return pos_data.size(); }
inline int att_size() const { return att_data.size(); }
inline MIDGpos get_pospt( const unsigned int i )
{
if ( i < pos_data.size() ) {
return pos_data[i];
} else {
return MIDGpos();
}
}
inline MIDGatt get_attpt( const unsigned int i )
{
if ( i < att_data.size() ) {
return att_data[i];
} else {
return MIDGatt();
}
}
};
MIDGpos MIDGInterpPos( const MIDGpos A, const MIDGpos B, const double percent );
MIDGatt MIDGInterpAtt( const MIDGatt A, const MIDGatt B, const double percent );

View File

@@ -0,0 +1,628 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
# include <windows.h>
#else
# include <netinet/in.h> // htonl() ntohl()
#endif
#include <iostream>
#include <string>
#include <plib/sg.h>
#include <simgear/constants.h>
#include <simgear/io/lowlevel.hxx> // endian tests
#include <simgear/io/sg_file.hxx>
#include <simgear/io/sg_serial.hxx>
#include <simgear/io/raw_socket.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/timing/timestamp.hxx>
#include <Network/net_ctrls.hxx>
#include <Network/net_fdm.hxx>
#include "MIDG-II.hxx"
using std::cout;
using std::endl;
using std::string;
// Network channels
static simgear::Socket fdm_sock, ctrls_sock;
// midg data
MIDGTrack track;
// Default ports
static int fdm_port = 5505;
static int ctrls_port = 5506;
// Default path
static string infile = "";
static string serialdev = "";
static string outfile = "";
// Master time counter
float sim_time = 0.0f;
double frame_us = 0.0f;
// sim control
SGTimeStamp last_time_stamp;
SGTimeStamp current_time_stamp;
// altitude offset
double alt_offset = 0.0;
// skip initial seconds
double skip = 0.0;
// for speed estimate
// double last_lat = 0.0, last_lon = 0.0;
// double kts_filter = 0.0;
bool inited = false;
// 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;
}
}
// Float version
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;
}
}
static void midg2fg( const MIDGpos pos, const MIDGatt att,
FGNetFDM *fdm, FGNetCtrls *ctrls )
{
unsigned int i;
// Version sanity checking
fdm->version = FG_NET_FDM_VERSION;
// Aero parameters
fdm->longitude = pos.lon_deg * SGD_DEGREES_TO_RADIANS;
fdm->latitude = pos.lat_deg * SGD_DEGREES_TO_RADIANS;
fdm->altitude = pos.altitude_msl + alt_offset;
fdm->agl = -9999.0;
fdm->psi = att.yaw_rad; // heading
fdm->phi = att.roll_rad; // roll
fdm->theta = att.pitch_rad; // pitch;
fdm->phidot = 0.0;
fdm->thetadot = 0.0;
fdm->psidot = 0.0;
// estimate speed
// double az1, az2, dist;
// geo_inverse_wgs_84( pos.altitude_msl, last_lat, last_lon,
// pos.lat_deg, pos.lon_deg, &az1, &az2, &dist );
// double v_ms = dist / (frame_us / 1000000);
// double v_kts = v_ms * SG_METER_TO_NM * 3600;
// kts_filter = (0.99 * kts_filter) + (0.01 * v_kts);
fdm->vcas = pos.speed_kts;
// last_lat = pos.lat_deg;
// last_lon = pos.lon_deg;
// cout << "kts_filter = " << kts_filter << " vel = " << pos.speed_kts << endl;
fdm->climb_rate = 0; // fps
// cout << "climb rate = " << aero->hdota << endl;
fdm->v_north = 0.0;
fdm->v_east = 0.0;
fdm->v_down = 0.0;
fdm->v_body_u = 0.0;
fdm->v_body_v = 0.0;
fdm->v_body_w = 0.0;
fdm->stall_warning = 0.0;
fdm->A_X_pilot = 0.0;
fdm->A_Y_pilot = 0.0;
fdm->A_Z_pilot = 0.0 /* (should be -G) */;
// Engine parameters
fdm->num_engines = 1;
fdm->eng_state[0] = 2;
// cout << "state = " << fdm->eng_state[0] << endl;
double rpm = ((pos.speed_kts - 15.0) / 65.0) * 2000.0 + 500.0;
if ( rpm < 0.0 ) { rpm = 0.0; }
if ( rpm > 3000.0 ) { rpm = 3000.0; }
fdm->rpm[0] = rpm;
fdm->fuel_flow[0] = 0.0;
fdm->egt[0] = 0.0;
// cout << "egt = " << aero->EGT << endl;
fdm->oil_temp[0] = 0.0;
fdm->oil_px[0] = 0.0;
// Consumables
fdm->num_tanks = 2;
fdm->fuel_quantity[0] = 0.0;
fdm->fuel_quantity[1] = 0.0;
// Gear and flaps
fdm->num_wheels = 3;
fdm->wow[0] = 0;
fdm->wow[1] = 0;
fdm->wow[2] = 0;
// the following really aren't used in this context
fdm->cur_time = 0;
fdm->warp = 0;
fdm->visibility = 0;
// cout << "Flap deflection = " << aero->dflap << endl;
fdm->left_flap = 0.0;
fdm->right_flap = 0.0;
fdm->elevator = -fdm->theta * 1.0;
fdm->elevator_trim_tab = 0.0;
fdm->left_flap = 0.0;
fdm->right_flap = 0.0;
fdm->left_aileron = fdm->phi * 1.0;
fdm->right_aileron = -fdm->phi * 1.0;
fdm->rudder = 0.0;
fdm->nose_wheel = 0.0;
fdm->speedbrake = 0.0;
fdm->spoilers = 0.0;
// Convert the net buffer to network format
fdm->version = htonl(fdm->version);
htond(fdm->longitude);
htond(fdm->latitude);
htond(fdm->altitude);
htonf(fdm->agl);
htonf(fdm->phi);
htonf(fdm->theta);
htonf(fdm->psi);
htonf(fdm->alpha);
htonf(fdm->beta);
htonf(fdm->phidot);
htonf(fdm->thetadot);
htonf(fdm->psidot);
htonf(fdm->vcas);
htonf(fdm->climb_rate);
htonf(fdm->v_north);
htonf(fdm->v_east);
htonf(fdm->v_down);
htonf(fdm->v_body_u);
htonf(fdm->v_body_v);
htonf(fdm->v_body_w);
htonf(fdm->A_X_pilot);
htonf(fdm->A_Y_pilot);
htonf(fdm->A_Z_pilot);
htonf(fdm->stall_warning);
htonf(fdm->slip_deg);
for ( i = 0; i < fdm->num_engines; ++i ) {
fdm->eng_state[i] = htonl(fdm->eng_state[i]);
htonf(fdm->rpm[i]);
htonf(fdm->fuel_flow[i]);
htonf(fdm->egt[i]);
htonf(fdm->cht[i]);
htonf(fdm->mp_osi[i]);
htonf(fdm->tit[i]);
htonf(fdm->oil_temp[i]);
htonf(fdm->oil_px[i]);
}
fdm->num_engines = htonl(fdm->num_engines);
for ( i = 0; i < fdm->num_tanks; ++i ) {
htonf(fdm->fuel_quantity[i]);
}
fdm->num_tanks = htonl(fdm->num_tanks);
for ( i = 0; i < fdm->num_wheels; ++i ) {
fdm->wow[i] = htonl(fdm->wow[i]);
htonf(fdm->gear_pos[i]);
htonf(fdm->gear_steer[i]);
htonf(fdm->gear_compression[i]);
}
fdm->num_wheels = htonl(fdm->num_wheels);
fdm->cur_time = htonl( fdm->cur_time );
fdm->warp = htonl( fdm->warp );
htonf(fdm->visibility);
htonf(fdm->elevator);
htonf(fdm->elevator_trim_tab);
htonf(fdm->left_flap);
htonf(fdm->right_flap);
htonf(fdm->left_aileron);
htonf(fdm->right_aileron);
htonf(fdm->rudder);
htonf(fdm->nose_wheel);
htonf(fdm->speedbrake);
htonf(fdm->spoilers);
}
static void send_data( const MIDGpos pos, const MIDGatt att ) {
int fdmsize = sizeof( FGNetFDM );
FGNetFDM fgfdm;
FGNetCtrls fgctrls;
midg2fg( pos, att, &fgfdm, &fgctrls );
fdm_sock.send(&fgfdm, fdmsize, 0);
}
void usage( const string &argv0 ) {
cout << "Usage: " << argv0 << endl;
cout << "\t[ --help ]" << endl;
cout << "\t[ --infile <infile_name>" << endl;
cout << "\t[ --serial <dev_name>" << endl;
cout << "\t[ --outfile <outfile_name> (capture the data to a file)" << endl;
cout << "\t[ --hertz <hertz> ]" << endl;
cout << "\t[ --host <hostname> ]" << endl;
cout << "\t[ --broadcast ]" << endl;
cout << "\t[ --fdm-port <fdm output port #> ]" << endl;
cout << "\t[ --ctrls-port <ctrls output port #> ]" << endl;
cout << "\t[ --altitude-offset <meters> ]" << endl;
cout << "\t[ --skip-seconds <seconds> ]" << endl;
}
int main( int argc, char **argv ) {
double hertz = 60.0;
string out_host = "localhost";
bool do_broadcast = false;
// process command line arguments
for ( int i = 1; i < argc; ++i ) {
if ( strcmp( argv[i], "--help" ) == 0 ) {
usage( argv[0] );
exit( 0 );
} else if ( strcmp( argv[i], "--hertz" ) == 0 ) {
++i;
if ( i < argc ) {
hertz = atof( argv[i] );
} else {
usage( argv[0] );
exit( -1 );
}
} else if ( strcmp( argv[i], "--infile" ) == 0 ) {
++i;
if ( i < argc ) {
infile = argv[i];
} else {
usage( argv[0] );
exit( -1 );
}
} else if ( strcmp( argv[i], "--outfile" ) == 0 ) {
++i;
if ( i < argc ) {
outfile = argv[i];
} else {
usage( argv[0] );
exit( -1 );
}
} else if ( strcmp( argv[i], "--serial" ) == 0 ) {
++i;
if ( i < argc ) {
serialdev = argv[i];
} else {
usage( argv[0] );
exit( -1 );
}
} else if ( strcmp( argv[i], "--host" ) == 0 ) {
++i;
if ( i < argc ) {
out_host = argv[i];
} else {
usage( argv[0] );
exit( -1 );
}
} else if ( strcmp( argv[i], "--broadcast" ) == 0 ) {
do_broadcast = true;
} else if ( strcmp( argv[i], "--fdm-port" ) == 0 ) {
++i;
if ( i < argc ) {
fdm_port = atoi( argv[i] );
} else {
usage( argv[0] );
exit( -1 );
}
} else if ( strcmp( argv[i], "--ctrls-port" ) == 0 ) {
++i;
if ( i < argc ) {
ctrls_port = atoi( argv[i] );
} else {
usage( argv[0] );
exit( -1 );
}
} else if ( strcmp( argv[i], "--altitude-offset" ) == 0 ) {
++i;
if ( i < argc ) {
alt_offset = atof( argv[i] );
} else {
usage( argv[0] );
exit( -1 );
}
} else if ( strcmp( argv[i], "--skip-seconds" ) == 0 ) {
++i;
if ( i < argc ) {
skip = atof( argv[i] );
} else {
usage( argv[0] );
exit( -1 );
}
} else {
usage( argv[0] );
exit( -1 );
}
}
// Setup up outgoing network connections
simgear::Socket::initSockets(); // We must call this before any other net stuff
if ( ! fdm_sock.open( false ) ) { // open a UDP socket
cout << "error opening fdm output socket" << endl;
return -1;
}
if ( ! ctrls_sock.open( false ) ) { // open a UDP socket
cout << "error opening ctrls output socket" << endl;
return -1;
}
cout << "open net channels" << endl;
fdm_sock.setBlocking( false );
ctrls_sock.setBlocking( false );
cout << "blocking false" << endl;
if ( do_broadcast ) {
fdm_sock.setBroadcast( true );
ctrls_sock.setBroadcast( true );
}
if ( fdm_sock.connect( out_host.c_str(), fdm_port ) == -1 ) {
perror("connect");
cout << "error connecting to outgoing fdm port: " << out_host
<< ":" << fdm_port << endl;
return -1;
}
cout << "connected outgoing fdm socket" << endl;
if ( ctrls_sock.connect( out_host.c_str(), ctrls_port ) == -1 ) {
perror("connect");
cout << "error connecting to outgoing ctrls port: " << out_host
<< ":" << ctrls_port << endl;
return -1;
}
cout << "connected outgoing ctrls socket" << endl;
if ( infile.length() ) {
// Load data from a track data
track.load( infile );
cout << "Loaded " << track.pos_size() << " position records." << endl;
cout << "Loaded " << track.att_size() << " attitude records." << endl;
int size = track.pos_size();
double current_time = track.get_pospt(0).get_seconds();
cout << "Track begin time is " << current_time << endl;
double end_time = track.get_pospt(size-1).get_seconds();
cout << "Track end time is " << end_time << endl;
cout << "Duration = " << end_time - current_time << endl;
// advance skip seconds forward
current_time += skip;
frame_us = 1000000.0 / hertz;
if ( frame_us < 0.0 ) {
frame_us = 0.0;
}
SGTimeStamp start_time;
start_time.stamp();
int pos_count = 0;
int att_count = 0;
MIDGpos pos0, pos1;
pos0 = pos1 = track.get_pospt( 0 );
MIDGatt att0, att1;
att0 = att1 = track.get_attpt( 0 );
while ( current_time < end_time ) {
// cout << "current_time = " << current_time << " end_time = "
// << end_time << endl;
// Advance position pointer
while ( current_time > pos1.get_seconds()
&& pos_count < track.pos_size() )
{
pos0 = pos1;
++pos_count;
// cout << "count = " << count << endl;
pos1 = track.get_pospt( pos_count );
}
// cout << "p0 = " << p0.get_time() << " p1 = " << p1.get_time()
// << endl;
// Advance attitude pointer
while ( current_time > att1.get_seconds()
&& att_count < track.att_size() )
{
att0 = att1;
++att_count;
// cout << "count = " << count << endl;
att1 = track.get_attpt( att_count );
}
// cout << "pos0 = " << pos0.get_seconds()
// << " pos1 = " << pos1.get_seconds() << endl;
double pos_percent;
if ( fabs(pos1.get_seconds() - pos0.get_seconds()) < 0.00001 ) {
pos_percent = 0.0;
} else {
pos_percent =
(current_time - pos0.get_seconds()) /
(pos1.get_seconds() - pos0.get_seconds());
}
// cout << "Percent = " << percent << endl;
double att_percent;
if ( fabs(att1.get_seconds() - att0.get_seconds()) < 0.00001 ) {
att_percent = 0.0;
} else {
att_percent =
(current_time - att0.get_seconds()) /
(att1.get_seconds() - att0.get_seconds());
}
// cout << "Percent = " << percent << endl;
MIDGpos pos = MIDGInterpPos( pos0, pos1, pos_percent );
MIDGatt att = MIDGInterpAtt( att0, att1, att_percent );
// cout << current_time << " " << p0.lat_deg << ", " << p0.lon_deg
// << endl;
// cout << current_time << " " << p1.lat_deg << ", " << p1.lon_deg
// << endl;
// cout << (double)current_time << " " << pos.lat_deg << ", "
// << pos.lon_deg << " " << att.yaw_deg << endl;
if ( pos.lat_deg > -500 ) {
printf( "%.3f %.4f %.4f %.1f %.2f %.2f %.2f\n",
current_time,
pos.lat_deg, pos.lon_deg, pos.altitude_msl,
att.yaw_rad * 180.0 / SG_PI,
att.pitch_rad * 180.0 / SG_PI,
att.roll_rad * 180.0 / SG_PI );
}
send_data( pos, att );
// Update the elapsed time.
static bool first_time = true;
if ( first_time ) {
last_time_stamp.stamp();
first_time = false;
}
current_time_stamp.stamp();
/* Convert to ms */
double elapsed_us = (current_time_stamp - last_time_stamp).toUSecs();
if ( elapsed_us < (frame_us - 2000) ) {
double requested_us = (frame_us - elapsed_us) - 2000 ;
ulMilliSecondSleep ( (int)(requested_us / 1000.0) ) ;
}
current_time_stamp.stamp();
while ( (current_time_stamp - last_time_stamp).toUSecs() < frame_us ) {
current_time_stamp.stamp();
}
current_time += (frame_us / 1000000.0);
last_time_stamp = current_time_stamp;
}
cout << "Processed " << pos_count << " entries in "
<< current_time_stamp - start_time << " seconds."
<< endl;
} else if ( serialdev.length() ) {
// process incoming data from the serial port
int count = 0;
MIDGpos pos;
MIDGatt att;
uint32_t pos_time = 1;
uint32_t att_time = 1;
// open the serial port device
SGSerialPort input( serialdev, 115200 );
if ( !input.is_enabled() ) {
cout << "Cannot open: " << serialdev << endl;
return false;
}
// open up the data log file if requested
if ( !outfile.length() ) {
cout << "no --outfile <name> specified, cannot capture data!"
<< endl;
return false;
}
SGFile output( outfile );
if ( !output.open( SG_IO_OUT ) ) {
cout << "Cannot open: " << outfile << endl;
return false;
}
while ( input.is_enabled() ) {
// cout << "looking for next message ..." << endl;
int id = track.next_message( &input, &output, &pos, &att );
cout << "message id = " << id << endl;
count++;
if ( id == 10 ) {
if ( att.get_msec() > att_time ) {
att_time = att.get_msec();
//current_time = att_time;
} else {
cout << "oops att back in time" << endl;
}
} else if ( id == 12 ) {
if ( pos.get_msec() > pos_time ) {
pos_time = pos.get_msec();
//current_time = pos_time;
} else {
cout << "oops pos back in time" << endl;
}
}
if ( pos.lat_deg > -500 ) {
// printf( "%.3f %.4f %.4f %.1f %.2f %.2f %.2f\n",
// current_time,
// pos.lat_deg, pos.lon_deg, pos.altitude_msl,
// att.yaw_rad * 180.0 / SG_PI,
// att.pitch_rad * 180.0 / SG_PI,
// att.roll_rad * 180.0 / SG_PI );
}
send_data( pos, att );
}
}
return 0;
}

File diff suppressed because it is too large Load Diff

609
utils/GPSsmooth/UGear.cxx Normal file
View File

@@ -0,0 +1,609 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <iostream>
#include <cstdio>
#include <simgear/constants.h>
#include <simgear/io/sg_file.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/misc/stdint.hxx>
#include "UGear.hxx"
using std::cout;
using std::endl;
#define START_OF_MSG0 147
#define START_OF_MSG1 224
UGTrack::UGTrack():
sg_swap(false)
{
};
UGTrack::~UGTrack() {};
// swap the 1st 4 bytes with the last 4 bytes of a stargate double so
// it matches the PC representation
static double sg_swap_double( uint8_t *buf, size_t offset ) {
double *result;
uint8_t tmpbuf[10];
for ( size_t i = 0; i < 4; ++i ) {
tmpbuf[i] = buf[offset + i + 4];
}
for ( size_t i = 0; i < 4; ++i ) {
tmpbuf[i + 4] = buf[offset + i];
}
// for ( size_t i = 0; i < 8; ++i ) {
// printf("%d ", tmpbuf[i]);
// }
// printf("\n");
result = (double *)tmpbuf;
return *result;
}
static bool validate_cksum( uint8_t id, uint8_t size, char *buf,
uint8_t cksum0, uint8_t cksum1,
bool ignore_checksum )
{
if ( ignore_checksum ) {
return true;
}
uint8_t c0 = 0;
uint8_t c1 = 0;
c0 += id;
c1 += c0;
// cout << "c0 = " << (unsigned int)c0 << " c1 = " << (unsigned int)c1 << endl;
c0 += size;
c1 += c0;
// cout << "c0 = " << (unsigned int)c0 << " c1 = " << (unsigned int)c1 << endl;
for ( uint8_t i = 0; i < size; i++ ) {
c0 += (uint8_t)buf[i];
c1 += c0;
// cout << "c0 = " << (unsigned int)c0 << " c1 = " << (unsigned int)c1
// << " [" << (unsigned int)(uint8_t)buf[i] << "]" << endl;
}
// cout << "c0 = " << (unsigned int)c0 << " (" << (unsigned int)cksum0
// << ") c1 = " << (unsigned int)c1 << " (" << (unsigned int)cksum1
// << ")" << endl;
if ( c0 == cksum0 && c1 == cksum1 ) {
return true;
} else {
return false;
}
}
void UGTrack::parse_msg( const int id, char *buf,
struct gps *gpspacket, imu *imupacket,
nav *navpacket, servo *servopacket,
health *healthpacket )
{
if ( id == GPS_PACKET ) {
*gpspacket = *(struct gps *)buf;
if ( sg_swap ) {
gpspacket->time = sg_swap_double( (uint8_t *)buf, 0 );
gpspacket->lat = sg_swap_double( (uint8_t *)buf, 8 );
gpspacket->lon = sg_swap_double( (uint8_t *)buf, 16 );
gpspacket->alt = sg_swap_double( (uint8_t *)buf, 24 );
gpspacket->vn = sg_swap_double( (uint8_t *)buf, 32 );
gpspacket->ve = sg_swap_double( (uint8_t *)buf, 40 );
gpspacket->vd = sg_swap_double( (uint8_t *)buf, 48 );
gpspacket->ITOW = sg_swap_double( (uint8_t *)buf, 56 );
}
} else if ( id == IMU_PACKET ) {
*imupacket = *(struct imu *)buf;
if ( sg_swap ) {
imupacket->time = sg_swap_double( (uint8_t *)buf, 0 );
imupacket->p = sg_swap_double( (uint8_t *)buf, 8 );
imupacket->q = sg_swap_double( (uint8_t *)buf, 16 );
imupacket->r = sg_swap_double( (uint8_t *)buf, 24 );
imupacket->ax = sg_swap_double( (uint8_t *)buf, 32 );
imupacket->ay = sg_swap_double( (uint8_t *)buf, 40 );
imupacket->az = sg_swap_double( (uint8_t *)buf, 48 );
imupacket->hx = sg_swap_double( (uint8_t *)buf, 56 );
imupacket->hy = sg_swap_double( (uint8_t *)buf, 64 );
imupacket->hz = sg_swap_double( (uint8_t *)buf, 72 );
imupacket->Ps = sg_swap_double( (uint8_t *)buf, 80 );
imupacket->Pt = sg_swap_double( (uint8_t *)buf, 88 );
imupacket->phi = sg_swap_double( (uint8_t *)buf, 96 );
imupacket->the = sg_swap_double( (uint8_t *)buf, 104 );
imupacket->psi = sg_swap_double( (uint8_t *)buf, 112 );
}
// printf("imu.time = %.4f size = %d\n", imupacket->time, sizeof(struct imu));
} else if ( id == NAV_PACKET ) {
*navpacket = *(struct nav *)buf;
if ( sg_swap ) {
navpacket->time = sg_swap_double( (uint8_t *)buf, 0 );
navpacket->lat = sg_swap_double( (uint8_t *)buf, 8 );
navpacket->lon = sg_swap_double( (uint8_t *)buf, 16 );
navpacket->alt = sg_swap_double( (uint8_t *)buf, 24 );
navpacket->vn = sg_swap_double( (uint8_t *)buf, 32 );
navpacket->ve = sg_swap_double( (uint8_t *)buf, 40 );
navpacket->vd = sg_swap_double( (uint8_t *)buf, 48 );
}
} else if ( id == SERVO_PACKET ) {
*servopacket = *(struct servo *)buf;
if ( sg_swap ) {
servopacket->time = sg_swap_double( (uint8_t *)buf, 0 );
}
// printf("servo time = %.3f %d %d\n", servopacket->time, servopacket->chn[0], servopacket->chn[1]);
} else if ( id == HEALTH_PACKET ) {
*healthpacket = *(struct health *)buf;
if ( sg_swap ) {
healthpacket->time = sg_swap_double( (uint8_t *)buf, 0 );
}
} else {
cout << "unknown id = " << id << endl;
}
}
// load the named stream log file into internal buffers
bool UGTrack::load_stream( const string &file, bool ignore_checksum ) {
int count = 0;
gps gpspacket;
imu imupacket;
nav navpacket;
servo servopacket;
health healthpacket;
double gps_time = 0;
double imu_time = 0;
double nav_time = 0;
double servo_time = 0;
double health_time = 0;
gps_data.clear();
imu_data.clear();
nav_data.clear();
servo_data.clear();
health_data.clear();
// open the file
SGFile input( file );
if ( !input.open( SG_IO_IN ) ) {
cout << "Cannot open file: " << file << endl;
return false;
}
while ( ! input.eof() ) {
// cout << "looking for next message ..." << endl;
int id = next_message( &input, NULL, &gpspacket, &imupacket,
&navpacket, &servopacket, &healthpacket,
ignore_checksum );
count++;
if ( id == GPS_PACKET ) {
if ( gpspacket.time > gps_time ) {
gps_data.push_back( gpspacket );
gps_time = gpspacket.time;
} else {
cout << "oops gps back in time: " << gpspacket.time << " " << gps_time << endl;
}
} else if ( id == IMU_PACKET ) {
if ( imupacket.time > imu_time ) {
imu_data.push_back( imupacket );
imu_time = imupacket.time;
} else {
cout << "oops imu back in time" << endl;
}
} else if ( id == NAV_PACKET ) {
if ( navpacket.time > nav_time ) {
nav_data.push_back( navpacket );
nav_time = navpacket.time;
} else {
cout << "oops nav back in time" << endl;
}
} else if ( id == SERVO_PACKET ) {
if ( servopacket.time > servo_time ) {
servo_data.push_back( servopacket );
servo_time = servopacket.time;
} else {
cout << "oops servo back in time" << endl;
}
} else if ( id == HEALTH_PACKET ) {
if ( healthpacket.time > health_time ) {
health_data.push_back( healthpacket );
health_time = healthpacket.time;
} else {
cout << "oops health back in time" << endl;
}
}
}
cout << "processed " << count << " messages" << endl;
return true;
}
// load the named stream log file into internal buffers
bool UGTrack::load_flight( const string &path ) {
gps gpspacket;
imu imupacket;
nav navpacket;
servo servopacket;
health healthpacket;
gps_data.clear();
imu_data.clear();
nav_data.clear();
servo_data.clear();
health_data.clear();
gzFile fgps = NULL;
gzFile fimu = NULL;
gzFile fnav = NULL;
gzFile fservo = NULL;
gzFile fhealth = NULL;
SGPath file;
int size;
// open the gps file
file = path; file.append( "gps.dat.gz" );
std::string fdata = file.local8BitStr();
if ( (fgps = gzopen( fdata.c_str(), "r" )) == NULL ) {
printf("Cannot open %s\n", fdata.c_str());
return false;
}
size = sizeof( struct gps );
printf("gps size = %d\n", size);
while ( gzread( fgps, &gpspacket, size ) == size ) {
gps_data.push_back( gpspacket );
}
// open the imu file
file = path; file.append( "imu.dat.gz" );
fdata = file.local8BitStr();
if ( (fimu = gzopen( fdata.c_str(), "r" )) == NULL ) {
printf("Cannot open %s\n", fdata.c_str());
return false;
}
size = sizeof( struct imu );
printf("imu size = %d\n", size);
while ( gzread( fimu, &imupacket, size ) == size ) {
imu_data.push_back( imupacket );
}
// open the nav file
file = path; file.append( "nav.dat.gz" );
fdata = file.local8BitStr();
if ( (fnav = gzopen( fdata.c_str(), "r" )) == NULL ) {
printf("Cannot open %s\n", fdata.c_str());
return false;
}
size = sizeof( struct nav );
printf("nav size = %d\n", size);
while ( gzread( fnav, &navpacket, size ) == size ) {
// printf("%.4f %.4f\n", navpacket.lat, navpacket.lon);
nav_data.push_back( navpacket );
}
// open the servo file
file = path; file.append( "servo.dat.gz" );
fdata = file.local8BitStr();
if ( (fservo = gzopen( fdata.c_str(), "r" )) == NULL ) {
printf("Cannot open %s\n", fdata.c_str());
return false;
}
size = sizeof( struct servo );
printf("servo size = %d\n", size);
while ( gzread( fservo, &servopacket, size ) == size ) {
servo_data.push_back( servopacket );
}
// open the health file
file = path; file.append( "health.dat.gz" );
fdata = file.local8BitStr();
if ( (fhealth = gzopen( fdata.c_str(), "r" )) == NULL ) {
printf("Cannot open %s\n", fdata.c_str());
return false;
}
size = sizeof( struct health );
printf("health size = %d\n", size);
while ( gzread( fhealth, &healthpacket, size ) == size ) {
health_data.push_back( healthpacket );
}
return true;
}
// attempt to work around some system dependent issues. Our read can
// return < data than we want.
int myread( SGIOChannel *ch, SGIOChannel *log, char *buf, int length ) {
bool myeof = false;
int result = 0;
if ( !myeof ) {
result = ch->read( buf, length );
// cout << "wanted " << length << " read " << result << " bytes" << endl;
if ( ch->get_type() == sgFileType ) {
myeof = ((SGFile *)ch)->eof();
}
}
if ( result > 0 && log != NULL ) {
log->write( buf, result );
}
return result;
}
// attempt to work around some system dependent issues. Our read can
// return < data than we want.
int serial_read( SGSerialPort *serial, SGIOChannel *log,
char *buf, int length )
{
int result = 0;
int bytes_read = 0;
char *tmp = buf;
while ( bytes_read < length ) {
result = serial->read_port( tmp, length - bytes_read );
bytes_read += result;
tmp += result;
// cout << " read " << bytes_read << " of " << length << endl;
}
if ( bytes_read > 0 && log != NULL ) {
log->write( buf, bytes_read );
}
return bytes_read;
}
// load the next message of a real time data stream
int UGTrack::next_message( SGIOChannel *ch, SGIOChannel *log,
gps *gpspacket, imu *imupacket, nav *navpacket,
servo *servopacket, health *healthpacket,
bool ignore_checksum )
{
char tmpbuf[256];
char savebuf[256];
// cout << "in next_message()" << endl;
bool myeof = false;
// scan for sync characters
uint8_t sync0, sync1;
myread( ch, log, tmpbuf, 2 );
sync0 = (unsigned char)tmpbuf[0];
sync1 = (unsigned char)tmpbuf[1];
while ( (sync0 != START_OF_MSG0 || sync1 != START_OF_MSG1) && !myeof ) {
sync0 = sync1;
myread( ch, log, tmpbuf, 1 ); sync1 = (unsigned char)tmpbuf[0];
cout << "scanning for start of message "
<< (unsigned int)sync0 << " " << (unsigned int)sync1
<< ", eof = " << ch->eof() << endl;
if ( ch->get_type() == sgFileType ) {
myeof = ((SGFile *)ch)->eof();
}
}
cout << "found start of message ..." << endl;
// read message id and size
myread( ch, log, tmpbuf, 2 );
uint8_t id = (unsigned char)tmpbuf[0];
uint8_t size = (unsigned char)tmpbuf[1];
// cout << "message = " << (int)id << " size = " << (int)size << endl;
// load message
if ( ch->get_type() == sgFileType ) {
int count = myread( ch, log, savebuf, size );
if ( count != size ) {
cout << "ERROR: didn't read enough bytes!" << endl;
}
} else {
#ifdef READ_ONE_BY_ONE
for ( int i = 0; i < size; ++i ) {
myread( ch, log, tmpbuf, 1 ); savebuf[i] = tmpbuf[0];
}
#else
myread( ch, log, savebuf, size );
#endif
}
// read checksum
myread( ch, log, tmpbuf, 2 );
uint8_t cksum0 = (unsigned char)tmpbuf[0];
uint8_t cksum1 = (unsigned char)tmpbuf[1];
if ( validate_cksum( id, size, savebuf, cksum0, cksum1, ignore_checksum ) )
{
parse_msg( id, savebuf, gpspacket, imupacket, navpacket, servopacket,
healthpacket );
return id;
}
cout << "Check sum failure!" << endl;
return -1;
}
// load the next message of a real time data stream
int UGTrack::next_message( SGSerialPort *serial, SGIOChannel *log,
gps *gpspacket, imu *imupacket, nav *navpacket,
servo *servopacket, health *healthpacket,
bool ignore_checksum )
{
char tmpbuf[256];
char savebuf[256];
// cout << "in next_message()" << endl;
bool myeof = false;
// scan for sync characters
int scan_count = 0;
uint8_t sync0, sync1;
serial_read( serial, log, tmpbuf, 2 );
sync0 = (unsigned char)tmpbuf[0];
sync1 = (unsigned char)tmpbuf[1];
while ( (sync0 != START_OF_MSG0 || sync1 != START_OF_MSG1) && !myeof ) {
scan_count++;
sync0 = sync1;
serial_read( serial, log, tmpbuf, 1 ); sync1 = (unsigned char)tmpbuf[0];
// cout << "scanning for start of message "
// << (unsigned int)sync0 << " " << (unsigned int)sync1
// << endl;
}
if ( scan_count > 0 ) {
cout << "found start of message after discarding " << scan_count
<< " bytes" << endl;
}
// cout << "found start of message ..." << endl;
// read message id and size
serial_read( serial, log, tmpbuf, 2 );
uint8_t id = (unsigned char)tmpbuf[0];
uint8_t size = (unsigned char)tmpbuf[1];
// cout << "message = " << (int)id << " size = " << (int)size << endl;
// load message
serial_read( serial, log, savebuf, size );
// read checksum
serial_read( serial, log, tmpbuf, 2 );
uint8_t cksum0 = (unsigned char)tmpbuf[0];
uint8_t cksum1 = (unsigned char)tmpbuf[1];
// cout << "cksum0 = " << (int)cksum0 << " cksum1 = " << (int)cksum1
// << endl;
if ( validate_cksum( id, size, savebuf, cksum0, cksum1, ignore_checksum ) )
{
parse_msg( id, savebuf, gpspacket, imupacket, navpacket, servopacket,
healthpacket );
return id;
}
cout << "Check sum failure!" << endl;
return -1;
}
static double interp( double a, double b, double p, bool rotational = false ) {
double diff = b - a;
if ( rotational ) {
// special handling of rotational data
if ( diff > SGD_PI ) {
diff -= SGD_2PI;
} else if ( diff < -SGD_PI ) {
diff += SGD_2PI;
}
}
return a + diff * p;
}
gps UGEARInterpGPS( const gps A, const gps B, const double percent )
{
gps p;
p.time = interp(A.time, B.time, percent);
p.lat = interp(A.lat, B.lat, percent);
p.lon = interp(A.lon, B.lon, percent);
p.alt = interp(A.alt, B.alt, percent);
p.ve = interp(A.ve, B.ve, percent);
p.vn = interp(A.vn, B.vn, percent);
p.vd = interp(A.vd, B.vd, percent);
p.ITOW = (int)interp(A.ITOW, B.ITOW, percent);
p.err_type = A.err_type;
return p;
}
imu UGEARInterpIMU( const imu A, const imu B, const double percent )
{
imu p;
p.time = interp(A.time, B.time, percent);
p.p = interp(A.p, B.p, percent);
p.q = interp(A.q, B.q, percent);
p.r = interp(A.r, B.r, percent);
p.ax = interp(A.ax, B.ax, percent);
p.ay = interp(A.ay, B.ay, percent);
p.az = interp(A.az, B.az, percent);
p.hx = interp(A.hx, B.hx, percent);
p.hy = interp(A.hy, B.hy, percent);
p.hz = interp(A.hz, B.hz, percent);
p.Ps = interp(A.Ps, B.Ps, percent);
p.Pt = interp(A.Pt, B.Pt, percent);
p.phi = interp(A.phi, B.phi, percent, true);
p.the = interp(A.the, B.the, percent, true);
p.psi = interp(A.psi, B.psi, percent, true);
p.err_type = A.err_type;
return p;
}
nav UGEARInterpNAV( const nav A, const nav B, const double percent )
{
nav p;
p.time = interp(A.time, B.time, percent);
p.lat = interp(A.lat, B.lat, percent);
p.lon = interp(A.lon, B.lon, percent);
p.alt = interp(A.alt, B.alt, percent);
p.ve = interp(A.ve, B.ve, percent);
p.vn = interp(A.vn, B.vn, percent);
p.vd = interp(A.vd, B.vd, percent);
p.err_type = A.err_type;
return p;
}
servo UGEARInterpSERVO( const servo A, const servo B, const double percent )
{
servo p;
for ( int i = 0; i < 8; ++i ) {
p.chn[i] = (uint16_t)interp(A.chn[i], B.chn[i], percent);
}
p.status = A.status;
return p;
}
health UGEARInterpHEALTH( const health A, const health B, const double percent )
{
health p;
p.command_sequence = B.command_sequence;
p.time = interp(A.time, B.time, percent);
return p;
}

173
utils/GPSsmooth/UGear.hxx Normal file
View File

@@ -0,0 +1,173 @@
#pragma once
#include <simgear/compiler.h>
#include <iostream>
#include <string>
#include <vector>
#include <simgear/misc/stdint.hxx>
#include <simgear/io/iochannel.hxx>
#include <simgear/serial/serial.hxx>
enum ugPacketType {
GPS_PACKET = 0,
IMU_PACKET = 1,
NAV_PACKET = 2,
SERVO_PACKET = 3,
HEALTH_PACKET = 4
};
struct imu {
double time;
double p,q,r; /* angular velocities */
double ax,ay,az; /* acceleration */
double hx,hy,hz; /* magnetic field */
double Ps,Pt; /* static/pitot pressure */
// double Tx,Ty,Tz; /* temperature */
double phi,the,psi; /* attitudes */
uint64_t err_type; /* error type */
};
struct gps {
double time;
double lat,lon,alt; /* gps position */
double ve,vn,vd; /* gps velocity */
double ITOW;
uint64_t err_type; /* error type */
};
struct nav {
double time;
double lat,lon,alt;
double ve,vn,vd;
// float t;
uint64_t err_type;
};
struct servo {
double time;
uint16_t chn[8];
uint64_t status;
};
struct health {
double time;
double target_roll_deg; /* AP target roll angle */
double target_heading_deg; /* AP target heading angle */
double target_pitch_deg; /* AP target pitch angle */
double target_climb_fps; /* AP target climb rate */
double target_altitude_ft; /* AP target altitude */
uint64_t command_sequence; /* highest received command sequence num */
uint64_t target_waypoint; /* index of current waypoint target */
uint64_t loadavg; /* system "1 minute" load average */
uint64_t ahrs_hz; /* actual ahrs loop hz */
uint64_t nav_hz; /* actual nav loop hz */
};
// Manage a saved ugear log (track file)
class UGTrack {
private:
std::vector <gps> gps_data;
std::vector <imu> imu_data;
std::vector <nav> nav_data;
std::vector <servo> servo_data;
std::vector <health> health_data;
// parse message and put current data into vector if message has a
// newer time stamp than existing data.
void parse_msg( const int id, char *buf,
gps *gpspacket, imu *imupacket, nav *navpacket,
servo *servopacket, health *healthpacket );
// activate special double swap logic for non-standard stargate
// double format
bool sg_swap;
public:
UGTrack();
~UGTrack();
// read/parse the next message from the specified data stream,
// returns id # if a valid message found.
int next_message( SGIOChannel *ch, SGIOChannel *log,
gps *gpspacket, imu *imupacket, nav *navpacket,
servo *servopacket, health * healthpacket,
bool ignore_checksum );
int next_message( SGSerialPort *serial, SGIOChannel *log,
gps *gpspacket, imu *imupacket, nav *navpacket,
servo *servopacket, health *healthpacket,
bool ignore_checksum );
// load the named stream log file into internal buffers
bool load_stream( const std::string &file, bool ignore_checksum );
// load the named flight files into internal buffers
bool load_flight( const std::string &path );
inline int gps_size() const { return gps_data.size(); }
inline int imu_size() const { return imu_data.size(); }
inline int nav_size() const { return nav_data.size(); }
inline int servo_size() const { return servo_data.size(); }
inline int health_size() const { return health_data.size(); }
inline gps get_gpspt( const unsigned int i )
{
if ( i < gps_data.size() ) {
return gps_data[i];
} else {
return gps();
}
}
inline imu get_imupt( const unsigned int i )
{
if ( i < imu_data.size() ) {
return imu_data[i];
} else {
return imu();
}
}
inline nav get_navpt( const unsigned int i )
{
if ( i < nav_data.size() ) {
return nav_data[i];
} else {
return nav();
}
}
inline servo get_servopt( const unsigned int i )
{
if ( i < servo_data.size() ) {
return servo_data[i];
} else {
return servo();
}
}
inline health get_healthpt( const unsigned int i )
{
if ( i < health_data.size() ) {
return health_data[i];
} else {
return health();
}
}
// set stargate mode where we have to do an odd swapping of doubles to
// account for their non-standard formate
inline void set_stargate_swap_mode() {
sg_swap = true;
}
};
gps UGEARInterpGPS( const gps A, const gps B, const double percent );
imu UGEARInterpIMU( const imu A, const imu B, const double percent );
nav UGEARInterpNAV( const nav A, const nav B, const double percent );
servo UGEARInterpSERVO( const servo A, const servo B, const double percent );
health UGEARInterpHEALTH( const health A, const health B,
const double percent );

View File

@@ -0,0 +1,106 @@
#include <cstring>
#include <cstdio>
#include "UGear_command.hxx"
UGCommand::UGCommand():
cmd_send_index(0),
cmd_recv_index(0),
prime_state(true)
{}
UGCommand::~UGCommand() {}
// calculate the nmea check sum
static char calc_nmea_cksum(const char *sentence) {
unsigned char sum = 0;
int i, len;
// cout << sentence << endl;
len = std::strlen(sentence);
sum = sentence[0];
for ( i = 1; i < len; i++ ) {
// cout << sentence[i];
sum ^= sentence[i];
}
// cout << endl;
// printf("sum = %02x\n", sum);
return sum;
}
// package and send the serial command
static int serial_send( SGSerialPort *serial, int sequence,
const string command )
{
char sequence_str[10];
snprintf( sequence_str, 9, "%d", sequence );
string package = sequence_str;
package += ",";
package += command;
char pkg_sum[10];
snprintf( pkg_sum, 3, "%02X", calc_nmea_cksum(package.c_str()) );
package += "*";
package += pkg_sum;
package += "\n";
unsigned int result = serial->write_port( package.c_str(),
package.length() );
if ( result != package.length() ) {
printf("ERROR: wrote %u of %u bytes to serial port!\n",
result, (unsigned)package.length() );
return 0;
}
return 1;
}
// send current command until acknowledged
int UGCommand::update( SGSerialPort *serial )
{
// if current command has been received, advance to next command
printf("sent = %d recv = %d\n", cmd_send_index, cmd_recv_index);
if ( cmd_recv_index >= cmd_send_index ) {
if ( ! cmd_queue.empty() ) {
if ( ! prime_state ) {
cmd_queue.pop();
cmd_send_index++;
} else {
prime_state = false;
}
}
}
// nothing to do if command queue empty
if ( cmd_queue.empty() ) {
prime_state = true;
return 0;
}
// send the command
string command = cmd_queue.front();
/*int result =*/ serial_send( serial, cmd_send_index, command );
return cmd_send_index;
}
void UGCommand::add( const string command )
{
printf("command queue: %s\n", command.c_str());
cmd_queue.push( command );
}
// create the global command channel manager
UGCommand command_mgr;

View File

@@ -0,0 +1,42 @@
#pragma once
#include <simgear/compiler.h>
#include <iostream>
#include <string>
#include <queue>
#include <simgear/misc/stdint.hxx>
#include <simgear/io/iochannel.hxx>
#include <simgear/serial/serial.hxx>
// Manage UGear Command Channel
class UGCommand {
private:
int cmd_send_index;
int cmd_recv_index;
bool prime_state;
std::queue <std::string> cmd_queue;
public:
UGCommand();
~UGCommand();
// send current command until acknowledged
int update( SGSerialPort *serial );
void add( const std::string command );
inline int cmd_queue_size() {
return cmd_queue.size();
}
inline void update_cmd_sequence( int sequence ) {
cmd_recv_index = sequence;
}
};
extern UGCommand command_mgr;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,141 @@
// opengc_data.hxx -- Define structure of OpenGC/FG uint32_terface parameters
//
// Version by J. Wojnaroski for uint32_terface to Open Glass Displays
//
// Modified 02/12/01 - Update engine structure for multi-engine models
// - Added data preamble to id msg types
//
// Modified 01/23/02 - Converted portions of the Engine and Gear accesssors to properties
// - Removed data from navigation functions. OpenGC provides own
//
// This file defines the class/structure of the UDP packet that sends
// the simulation data created by FlightGear to the glass displays. It
// is required to "sync" the data types contained in the packet
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef _OPENGC_DATA_HXX
#define _OPENGC_DATA_HXX
#ifndef __cplusplus
# error This library requires C++
#endif
const uint32_t OGC_VERSION = 4;
typedef unsigned int uint32_t;
class ogcFGData {
public:
// defines msg types and contents. The msg_content is used as a 'pouint32_ter' to
// a predefined set of msg strings
uint32_t version_id;
uint32_t msg_type;
uint32_t msg_content;
uint32_t reserved;
// position
double latitude;
double longitude;
double elevation;
double magvar;
// flight parameters
double pitch;
double bank;
double heading;
double altitude;
double altitude_agl; // this can also be the radar altimeter
double v_kcas;
double groundspeed;
double vvi;
double mach;
double v_keas; // equivalent airspeed in knots
// Data used by the FMC and autopilots
double phi_dot;
double theta_dot;
double psi_dot;
double alpha;
double alpha_dot;
double beta;
double beta_dot;
// Control surface positions
double left_aileron;
double right_aileron;
double aileron_trim;
double elevator;
double elevator_trim;
double rudder;
double rudder_trim;
double flaps;
double flaps_cmd;
// gear positions 0 = up and 1 = down The 747 has 5 wheel bogey assemblies
double gear_nose;
double gear_left;
double gear_right;
double gear_left_rear;
double gear_right_rear;
double parking_brake;
uint32_t wow_main; // logical and of main gear
uint32_t wow_nose;
// engine data
double rpm[4]; // this is for pistons, jets see below
double n1_turbine[4];
double epr[4];
double egt[4];
double n2_turbine[4];
double fuel_flow[4];
double man_pressure[4];
double oil_pressure[4];
double oil_temp[4];
double oil_quantity[4];
double hyd_pressure[4];
double throttle[4];
double mixture[4];
double prop_advance[4];
// fuel system
uint32_t num_tanks;
double fuel_tank[9];
// Pressures and temperatures
double static_temperature;
double total_temperature;
double static_pressure;
double total_pressure;
double dynamic_pressure;
// more environmental data
double wind;
double wind_dir;
double sea_level_pressure;
};
#endif // _OPENGC_HXX

View File

@@ -0,0 +1,235 @@
// \file props.cxx
// Property server class.
//
// Written by Curtis Olson, started September 2000.
// Modified by Bernie Bright, May 2002.
//
// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <simgear/io/sg_netChat.hxx>
#include <simgear/structure/commands.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/debug/logstream.hxx>
#include <cstdio>
#include <sstream>
#include "UGear_command.hxx"
#include "UGear_telnet.hxx"
using std::stringstream;
using std::ends;
/**
* Props connection class.
* This class represents a connection to props client.
*/
class PropsChannel : public simgear::NetChat
{
simgear::NetBuffer buffer;
/**
* Current property node name.
*/
string path;
enum Mode {
PROMPT,
DATA
};
Mode mode;
public:
/**
* Constructor.
*/
PropsChannel();
/**
* Append incoming data to our request buffer.
*
* @param s Character string to append to buffer
* @param n Number of characters to append.
*/
void collectIncomingData( const char* s, int n );
/**
* Process a complete request from the props client.
*/
void foundTerminator();
private:
/**
* Return a "Node no found" error message to the client.
*/
void node_not_found_error( const string& node_name );
};
/**
*
*/
PropsChannel::PropsChannel()
: buffer(512),
path("/"),
mode(PROMPT)
{
setTerminator( "\r\n" );
}
/**
*
*/
void
PropsChannel::collectIncomingData( const char* s, int n )
{
buffer.append( s, n );
}
/**
*
*/
void
PropsChannel::node_not_found_error( const string& node_name )
{
string error = "-ERR Node \"";
error += node_name;
error += "\" not found.";
push( error.c_str() );
push( getTerminator() );
}
/**
* We have a command.
*
*/
void
PropsChannel::foundTerminator()
{
const char* cmd = buffer.getData();
SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
std::vector<std::string> tokens = simgear::strutils::split( cmd );
if (!tokens.empty()) {
string command = tokens[0];
if ( command == "send" ) {
command_mgr.add( tokens[1] );
} else if ( command == "quit" ) {
close();
shouldDelete();
return;
} else if ( command == "data" ) {
mode = DATA;
} else if ( command == "prompt" ) {
mode = PROMPT;
} else {
const char* msg = "\
Valid commands are:\r\n\
\r\n\
data switch to raw data mode\r\n\
prompt switch to interactive mode (default)\r\n\
quit terminate connection\r\n\
send <command> send <command> to UAS\r\n";
push( msg );
}
}
if (mode == PROMPT) {
string prompt = "> ";
push( prompt.c_str() );
}
buffer.remove();
}
/**
*
*/
UGTelnet::UGTelnet( const int port_num ):
enabled(false)
{
port = port_num;
}
/**
*
*/
UGTelnet::~UGTelnet()
{
}
/**
*
*/
bool
UGTelnet::open()
{
if (enabled ) {
printf("This shouldn't happen, but the telnet channel is already in use, ignoring\n" );
return false;
}
simgear::NetChannel::open();
simgear::NetChannel::bind( "", port );
simgear::NetChannel::listen( 5 );
printf("Telnet server started on port %d\n", port );
enabled = true;
poller.addChannel(this);
return true;
}
/**
*
*/
bool
UGTelnet::close()
{
SG_LOG( SG_IO, SG_INFO, "closing UGTelnet" );
return true;
}
/**
*
*/
bool
UGTelnet::process()
{
poller.poll();
return true;
}
/**
*
*/
void
UGTelnet::handleAccept()
{
simgear::IPAddress addr;
int handle = simgear::NetChannel::accept( &addr );
printf("Telent server accepted connection from %s:%d\n",
addr.getHost(), addr.getPort() );
PropsChannel* channel = new PropsChannel();
channel->setHandle( handle );
poller.addChannel(channel);
}

View File

@@ -0,0 +1,85 @@
// \file UGear_telnet.hxx
// telnet server class.
//
// Adapted from FlightGear props.hxx/cxx code
// Written by Curtis Olson, started September 2000.
// Modified by Bernie Bright, May 2002.
//
// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#pragma once
#include <string>
#include <vector>
#include <simgear/io/sg_netChannel.hxx>
/**
* Telent server class.
* This class provides a telnet-like server for remote access to
* FlightGear properties.
*/
class UGTelnet: simgear::NetChannel
{
private:
/**
* Server port to listen on.
*/
int port;
bool enabled;
simgear::NetChannelPoller poller;
public:
/**
* Create a new TCP server.
*
* @param tokens Tokenized configuration parameters
*/
UGTelnet( const int port_num );
/**
* Destructor.
*/
~UGTelnet();
/**
* Start the telnet server.
*/
bool open();
/**
* Process network activity.
*/
bool process();
/**
*
*/
bool close();
/**
* Accept a new client connection.
*/
void handleAccept();
};

View File

@@ -0,0 +1,491 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
# include <windows.h>
#else
# include <netinet/in.h> // htonl() ntohl()
#endif
#include <iostream>
#include <string>
#include <plib/sg.h>
#include <simgear/io/lowlevel.hxx> // endian tests
#include <simgear/io/raw_socket.hxx>
#include <simgear/timing/timestamp.hxx>
#include <Network/net_ctrls.hxx>
#include <Network/net_fdm.hxx>
#include "GPSsmooth.hxx"
using std::cout;
using std::endl;
using std::string;
// Network channels
static simgear::Socket fdm_sock, ctrls_sock;
// gps data
GPSTrack track;
// Default ports
static int fdm_port = 5505;
static int ctrls_port = 5506;
// Default path
static string file = "";
// Master time counter
float sim_time = 0.0f;
// sim control
SGTimeStamp last_time_stamp;
SGTimeStamp current_time_stamp;
bool inited = false;
// 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;
}
}
// Float version
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;
}
}
static void gps2fg( const GPSPoint p, FGNetFDM *fdm, FGNetCtrls *ctrls )
{
unsigned int i;
static double last_psi;
static double last_alt;
static double phi_filter = 0.0;
static double theta_filter = 0.0;
// Nan-be-gone
if ( phi_filter != phi_filter ) {
phi_filter = 0.0;
}
if ( theta_filter != theta_filter ) {
theta_filter = 0.0;
}
// Version sanity checking
fdm->version = FG_NET_FDM_VERSION;
// Aero parameters
fdm->longitude = p.lon_deg * SGD_DEGREES_TO_RADIANS;
fdm->latitude = p.lat_deg * SGD_DEGREES_TO_RADIANS;
fdm->altitude = p.altitude_msl;
fdm->agl = -9999.0;
fdm->psi = p.course_true; // heading
double diff = p.course_true - last_psi;
if ( diff < -SGD_PI ) { diff += 2.0*SGD_PI; }
if ( diff > SGD_PI ) { diff -= 2.0*SGD_PI; }
double phi = diff * 100.0;
if ( phi > 0.5*SGD_PI ) { phi = 0.5*SGD_PI; }
if ( phi < -0.5*SGD_PI ) { phi = -0.5*SGD_PI; }
phi_filter = 0.99*phi_filter + 0.01*phi;
fdm->phi = phi_filter;
last_psi = p.course_true;
// cout << p.course_true << endl;
diff = p.altitude_msl - last_alt;
if ( diff < -SGD_PI ) { diff += 2.0*SGD_PI; }
if ( diff > SGD_PI ) { diff -= 2.0*SGD_PI; }
double theta = diff * 2.0;
if ( theta > 0.5*SGD_PI ) { theta = 0.5*SGD_PI; }
if ( theta < -0.5*SGD_PI ) { theta = -0.5*SGD_PI; }
theta_filter = 0.99*theta_filter + 0.01*theta;
fdm->theta = theta_filter;
last_alt = p.altitude_msl;
fdm->phidot = 0.0;
fdm->thetadot = 0.0;
fdm->psidot = 0.0;
fdm->vcas = p.speed_kts;
fdm->climb_rate = 0; // fps
// cout << "climb rate = " << aero->hdota << endl;
fdm->v_north = 0.0;
fdm->v_east = 0.0;
fdm->v_down = 0.0;
fdm->v_body_u = 0.0;
fdm->v_body_v = 0.0;
fdm->v_body_w = 0.0;
fdm->stall_warning = 0.0;
fdm->A_X_pilot = 0.0;
fdm->A_Y_pilot = 0.0;
fdm->A_Z_pilot = 0.0 /* (should be -G) */;
// Engine parameters
fdm->num_engines = 1;
fdm->eng_state[0] = 2;
// cout << "state = " << fdm->eng_state[0] << endl;
double rpm = ((p.speed_kts - 15.0) / 65.0) * 2000.0 + 500.0;
if ( rpm < 0.0 ) { rpm = 0.0; }
if ( rpm > 3000.0 ) { rpm = 3000.0; }
fdm->rpm[0] = rpm;
fdm->fuel_flow[0] = 0.0;
fdm->egt[0] = 0.0;
// cout << "egt = " << aero->EGT << endl;
fdm->oil_temp[0] = 0.0;
fdm->oil_px[0] = 0.0;
// Consumables
fdm->num_tanks = 2;
fdm->fuel_quantity[0] = 0.0;
fdm->fuel_quantity[1] = 0.0;
// Gear and flaps
fdm->num_wheels = 3;
fdm->wow[0] = 0;
fdm->wow[1] = 0;
fdm->wow[2] = 0;
// the following really aren't used in this context
fdm->cur_time = 0;
fdm->warp = 0;
fdm->visibility = 0;
// cout << "Flap deflection = " << aero->dflap << endl;
fdm->left_flap = 0.0;
fdm->right_flap = 0.0;
fdm->elevator = -theta_filter * 5.0;
fdm->elevator_trim_tab = 0.0;
fdm->left_flap = 0.0;
fdm->right_flap = 0.0;
fdm->left_aileron = phi_filter * 1.5;
fdm->right_aileron = phi_filter * 1.5;
fdm->rudder = 0.0;
fdm->nose_wheel = 0.0;
fdm->speedbrake = 0.0;
fdm->spoilers = 0.0;
// Convert the net buffer to network format
fdm->version = htonl(fdm->version);
htond(fdm->longitude);
htond(fdm->latitude);
htond(fdm->altitude);
htonf(fdm->agl);
htonf(fdm->phi);
htonf(fdm->theta);
htonf(fdm->psi);
htonf(fdm->alpha);
htonf(fdm->beta);
htonf(fdm->phidot);
htonf(fdm->thetadot);
htonf(fdm->psidot);
htonf(fdm->vcas);
htonf(fdm->climb_rate);
htonf(fdm->v_north);
htonf(fdm->v_east);
htonf(fdm->v_down);
htonf(fdm->v_body_u);
htonf(fdm->v_body_v);
htonf(fdm->v_body_w);
htonf(fdm->A_X_pilot);
htonf(fdm->A_Y_pilot);
htonf(fdm->A_Z_pilot);
htonf(fdm->stall_warning);
htonf(fdm->slip_deg);
for ( i = 0; i < fdm->num_engines; ++i ) {
fdm->eng_state[i] = htonl(fdm->eng_state[i]);
htonf(fdm->rpm[i]);
htonf(fdm->fuel_flow[i]);
htonf(fdm->egt[i]);
htonf(fdm->cht[i]);
htonf(fdm->mp_osi[i]);
htonf(fdm->tit[i]);
htonf(fdm->oil_temp[i]);
htonf(fdm->oil_px[i]);
}
fdm->num_engines = htonl(fdm->num_engines);
for ( i = 0; i < fdm->num_tanks; ++i ) {
htonf(fdm->fuel_quantity[i]);
}
fdm->num_tanks = htonl(fdm->num_tanks);
for ( i = 0; i < fdm->num_wheels; ++i ) {
fdm->wow[i] = htonl(fdm->wow[i]);
htonf(fdm->gear_pos[i]);
htonf(fdm->gear_steer[i]);
htonf(fdm->gear_compression[i]);
}
fdm->num_wheels = htonl(fdm->num_wheels);
fdm->cur_time = htonl( fdm->cur_time );
fdm->warp = htonl( fdm->warp );
htonf(fdm->visibility);
htonf(fdm->elevator);
htonf(fdm->elevator_trim_tab);
htonf(fdm->left_flap);
htonf(fdm->right_flap);
htonf(fdm->left_aileron);
htonf(fdm->right_aileron);
htonf(fdm->rudder);
htonf(fdm->nose_wheel);
htonf(fdm->speedbrake);
htonf(fdm->spoilers);
}
static void send_data( const GPSPoint p ) {
// int ctrlsize = sizeof( FGNetCtrls );
int fdmsize = sizeof( FGNetFDM );
// cout << "Running main loop" << endl;
FGNetFDM fgfdm;
FGNetCtrls fgctrls;
gps2fg( p, &fgfdm, &fgctrls );
fdm_sock.send(&fgfdm, fdmsize, 0);
}
void usage( const string &argv0 ) {
cout << "Usage: " << argv0 << endl;
cout << "\t[ --help ]" << endl;
cout << "\t[ --file <file_name>" << endl;
cout << "\t[ --hertz <hertz> ]" << endl;
cout << "\t[ --host <hostname> ]" << endl;
cout << "\t[ --broadcast ]" << endl;
cout << "\t[ --fdm-port <fdm output port #> ]" << endl;
cout << "\t[ --ctrls-port <ctrls output port #> ]" << endl;
}
int main( int argc, char **argv ) {
double hertz = 60.0;
string out_host = "localhost";
bool do_broadcast = false;
// process command line arguments
for ( int i = 1; i < argc; ++i ) {
if ( strcmp( argv[i], "--help" ) == 0 ) {
usage( argv[0] );
exit( 0 );
} else if ( strcmp( argv[i], "--hertz" ) == 0 ) {
++i;
if ( i < argc ) {
hertz = atof( argv[i] );
} else {
usage( argv[0] );
exit( -1 );
}
} else if ( strcmp( argv[i], "--file" ) == 0 ) {
++i;
if ( i < argc ) {
file = argv[i];
} else {
usage( argv[0] );
exit( -1 );
}
} else if ( strcmp( argv[i], "--host" ) == 0 ) {
++i;
if ( i < argc ) {
out_host = argv[i];
} else {
usage( argv[0] );
exit( -1 );
}
} else if ( strcmp( argv[i], "--broadcast" ) == 0 ) {
do_broadcast = true;
} else if ( strcmp( argv[i], "--fdm-port" ) == 0 ) {
++i;
if ( i < argc ) {
fdm_port = atoi( argv[i] );
} else {
usage( argv[0] );
exit( -1 );
}
} else if ( strcmp( argv[i], "--ctrls-port" ) == 0 ) {
++i;
if ( i < argc ) {
ctrls_port = atoi( argv[i] );
} else {
usage( argv[0] );
exit( -1 );
}
} else {
usage( argv[0] );
exit( -1 );
}
}
// Load the track data
if ( file == "" ) {
cout << "No track file specified" << endl;
exit(-1);
}
track.load( file );
cout << "Loaded " << track.size() << " records." << endl;
// Setup up outgoing network connections
simgear::Socket::initSockets(); // We must call this before any other net stuff
if ( ! fdm_sock.open( false ) ) { // open a UDP socket
cout << "error opening fdm output socket" << endl;
return -1;
}
if ( ! ctrls_sock.open( false ) ) { // open a UDP socket
cout << "error opening ctrls output socket" << endl;
return -1;
}
cout << "open net channels" << endl;
fdm_sock.setBlocking( false );
ctrls_sock.setBlocking( false );
cout << "blocking false" << endl;
if ( do_broadcast ) {
fdm_sock.setBroadcast( true );
ctrls_sock.setBroadcast( true );
}
if ( fdm_sock.connect( out_host.c_str(), fdm_port ) == -1 ) {
perror("connect");
cout << "error connecting to outgoing fdm port: " << out_host
<< ":" << fdm_port << endl;
return -1;
}
cout << "connected outgoing fdm socket" << endl;
if ( ctrls_sock.connect( out_host.c_str(), ctrls_port ) == -1 ) {
perror("connect");
cout << "error connecting to outgoing ctrls port: " << out_host
<< ":" << ctrls_port << endl;
return -1;
}
cout << "connected outgoing ctrls socket" << endl;
int size = track.size();
double current_time = track.get_point(0).get_time();
cout << "Track begin time is " << current_time << endl;
double end_time = track.get_point(size-1).get_time();
cout << "Track end time is " << end_time << endl;
cout << "Duration = " << end_time - current_time << endl;
double frame_us = 1000000.0 / hertz;
if ( frame_us < 0.0 ) {
frame_us = 0.0;
}
SGTimeStamp start_time;
start_time.stamp();
int count = 0;
GPSPoint p, p0, p1;
p0 = p1 = track.get_point( 0 );
while ( current_time < end_time ) {
// cout << "current_time = " << current_time << " end_time = "
// << end_time << endl;
if ( current_time > p1.get_time() ) {
p0 = p1;
++count;
// cout << "count = " << count << endl;
p1 = track.get_point( count );
}
// cout << "p0 = " << p0.get_time() << " p1 = " << p1.get_time()
// << endl;
double percent;
if ( fabs(p1.get_time() - p0.get_time()) < 0.0001 ) {
percent = 0.0;
} else {
percent =
(current_time - p0.get_time()) /
(p1.get_time() - p0.get_time());
}
// cout << "Percent = " << percent << endl;
GPSPoint p = GPSInterpolate( p0, p1, percent );
// cout << current_time << " " << p0.lat_deg << ", " << p0.lon_deg << endl;
// cout << current_time << " " << p1.lat_deg << ", " << p1.lon_deg << endl;
cout << current_time << " " << p.lat_deg << ", " << p.lon_deg << endl;
send_data( p );
// Update the elapsed time.
static bool first_time = true;
if ( first_time ) {
last_time_stamp.stamp();
first_time = false;
}
current_time_stamp.stamp();
/* Convert to ms */
double elapsed_us = (current_time_stamp - last_time_stamp).toUSecs();
if ( elapsed_us < (frame_us - 2000) ) {
double requested_us = (frame_us - elapsed_us) - 2000 ;
ulMilliSecondSleep ( (int)(requested_us / 1000.0) ) ;
}
current_time_stamp.stamp();
while ( (current_time_stamp - last_time_stamp).toUSecs() < frame_us ) {
current_time_stamp.stamp();
}
current_time += (frame_us / 1000000.0);
last_time_stamp = current_time_stamp;
}
cout << "Processed " << count << " entries in "
<< current_time_stamp - start_time << " seconds." << endl;
return 0;
}

10613
utils/GPSsmooth/nmea_data.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/compiler.h>
#include <iostream>
#include <GL/glut.h>
#include <plib/ssg.h>
using std::cerr;
using std::endl;
int
main (int ac, char ** av)
{
if (ac != 3) {
cerr << "Usage: " << av[0] << " <file_in> <file_out>" << endl;
return 1;
}
int fakeac = 1;
char * fakeav[] = { "3dconvert",
"Convert a 3D Model",
0 };
glutInit(&fakeac, fakeav);
glutCreateWindow(fakeav[1]);
ssgInit();
ssgEntity * object = ssgLoad(av[1]);
if (object == 0) {
cerr << "Failed to load " << av[1] << endl;
return 2;
}
ssgSave(av[2], object);
}

193
utils/Modeller/ac3d-despeckle Executable file
View File

@@ -0,0 +1,193 @@
#!/usr/bin/perl -w
# $Id$
# Melchior FRANZ < mfranz # aon : at > Public Domain
#
# Older nVidia cards like the GF4 MX440 don't like UV faces
# where all coordinates are equal, and thus don't have an area.
# This leads to grey spots that are constantly going on and off.
# The script fixes UV faces with 3 and 4 references by assigning
# a one (half)pixel sized area, and moves coordinates into the
# texture area if necessary.
#
use strict;
my $SELF = $0;
$SELF =~ s,.*\/,,;
my $VERBOSE = 0;
my $NOOP = 0;
my $TEXPATH = ".";
my $help = <<EOF;
Usage:
$SELF [-v] [-n] [-t <path>] <infile.ac >outfile.ac
Options:
-h this help
-v verbose output (also shows line numbers if used twice: -v -v)
-n dry run
-t <path> texture directory (default $TEXPATH)
Output:
# BAD (face with identical UV coords; will be fixed)
? CHECK (only two UV coordinates or more than 4; could be a problem)
. OK (wrong coordinates, but no texture assigned; ignore)
EOF
my $OBJECTNAME;
my $TEXTURE;
my $PIXW = 0;
my $PIXH = 0;
sub fatal($) {
die shift;
}
sub report($) {
print STDERR shift;
print STDERR "[$.] " if $VERBOSE > 1;
}
sub parse_args() {
while (@ARGV) {
my $arg = shift @ARGV;
if ($arg eq '-h' or $arg eq '--help') {
print STDERR $help;
exit 0;
} elsif ($arg eq '-v' or $arg eq '--verbose') {
$VERBOSE++;
} elsif ($arg eq '-n' or $arg eq '--dry-run') {
$NOOP = 1;
} elsif ($arg eq '-t') {
$arg = shift @ARGV;
defined $arg or fatal('-t option without <text> argument');
$TEXPATH = $arg;
}
}
}
sub sgisize($) {
my $name = shift;
my $i;
-f $name or fatal("cannot find texture '$name'");
-s $name > 512 or fatal("'$name' cannot be an SGI image (too small)");
open(IMG, "<$name") || fatal("cannot open file '$name': $!");
read(IMG, $i, 2);
unpack("n", $i) == 474 or fatal("'$name' cannot be an SGI image (wrong magic number)");
my ($x, $y);
seek(IMG, 6, 0);
read(IMG, $x, 2);
read(IMG, $y, 2);
close IMG or fatal("cannot close file '$name': $!");
return (unpack("n", $x), unpack("n", $y));
}
# main()
parse_args();
print STDERR "\033[35mTEXTURE PATH: $TEXPATH\033[m" if $VERBOSE;
while (<>) {
if (/^OBJECT /) {
undef $TEXTURE;
} elsif (/^name\s+(.*)/) {
$OBJECTNAME = $1;
$OBJECTNAME =~ /"(.*)"/ and $OBJECTNAME = $1;
print STDERR "\033[35;1m\nNAME: $OBJECTNAME\033[m " if $VERBOSE;
} elsif (/^texture\s+(.*)/) {
$TEXTURE = $1;
$TEXTURE =~ /"(.*)"/ and $TEXTURE = $1;
my ($WIDTH, $HEIGHT) = sgisize($TEXPATH . '/' . $TEXTURE);
print STDERR "\033[33;1m\nTEXTURE: $TEXTURE ($WIDTH x $HEIGHT)\033[m " if $VERBOSE;
$PIXW = 1.0 / $WIDTH;
$PIXH = 1.0 / $HEIGHT;
} elsif (/^SURF /) {
print $_ unless $NOOP;
my $mat = <>;
$mat =~ /^mat / or fatal("line $.: mat entry missing");
print $mat unless $NOOP;
my $refs = <>;
$refs =~ /^refs\s+(\d+)/ or fatal("line $.: refs entry missing");
print $refs unless $NOOP;
my $n = $1;
if ($n < 3 or $n > 4) {
report("?") if $VERBOSE;
unless ($NOOP) {
print scalar(<>) foreach (1 .. $n);
}
next;
}
my (@ref0, @ref1, @ref2, @ref3);
@ref0 = split /\s+/, <>;
@ref1 = split /\s+/, <>;
@ref2 = split /\s+/, <>;
@ref3 = split /\s+/, <> if $n == 4;
goto writeuv if $ref0[1] != $ref1[1] or $ref0[2] != $ref1[2] or $ref1[1] != $ref2[1] or $ref1[2] != $ref2[2];
goto writeuv if $n == 4 and ($ref2[1] != $ref3[1] or $ref2[2] != $ref3[2]);
if (defined $TEXTURE) {
report("#") if $VERBOSE;
my ($x, $y) = @ref0[1, 2];
$x = 0.0 if $x < 0.0;
$x = 1.0 - $PIXW if $x > 1.0 - $PIXW;
$y = 0.0 if $y < 0.0;
$y = 1.0 - $PIXH if $y > 1.0 - $PIXH;
$x = int($x / $PIXW) * $PIXW;
$y = int($y / $PIXH) * $PIXH;
$ref0[1] = $x, $ref0[2] = $y;
$ref1[1] = $x + $PIXW;
$ref2[1] = $x + $PIXW, $ref2[2] = $y + $PIXH;
$ref3[2] = $y + $PIXH if $n == 4;
} else {
report(".") if $VERBOSE;
}
writeuv:
unless ($NOOP) {
print join " ", @ref0;
print "\n";
print join " ", @ref1;
print "\n";
print join " ", @ref2;
print "\n";
if ($n == 4) {
print join " ", @ref3;
print "\n";
}
}
next;
}
print unless $NOOP;
}
print STDERR "\n" if $VERBOSE;
exit 0;

View File

@@ -0,0 +1,79 @@
/*
// animassist.c
//
// Jim Wilson 5/8/2003
//
// Copyright (C) 2003 Jim Wilson - jimw@kelcomaine.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
int main( int argc, char **argv ) {
float x1,y1,z1,x2,y2,z2 = 0;
float center_x,center_y,center_z,axis_x,axis_y,axis_z;
float vector_length;
if (argc < 7) {
printf("animassist - utility to calculate axis and center values for SimGear model animation\n\n");
printf("Usage: animassist x1 y1 z1 x2 y2 z2\n\n");
printf("Defining two vectors in SimGear coords (x1,y1,z1) and (x2,y2,z2)\n\n");
printf("Conversion from model to SimGear:\n");
printf("SimGear Coord AC3D Coordinate\n");
printf(" X = X\n");
printf(" Y = Z * -1\n");
printf(" Z = Y\n\n");
printf("Note: no normalization done, so please make the 2nd vector the furthest out\n");
} else {
x1 = atof(argv[1]);
y1 = atof(argv[2]);
z1 = atof(argv[3]);
x2 = atof(argv[4]);
y2 = atof(argv[5]);
z2 = atof(argv[6]);
center_x = (x1+x2)/2;
center_y = (y1+y2)/2;
center_z = (z1+z2)/2;
vector_length = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1));
/* arbitrary axis defined by cos(theta) where theta is angle from x,y or z axis */
axis_x = (x2-x1)/vector_length;
axis_y = (y2-y1)/vector_length;
axis_z = (z2-z1)/vector_length;
printf("Flightgear Model XML for:\n");
printf("Axis from (%f,%f,%f) to (%f,%f,%f)\n", x1,y1,z1,x2,y2,z2);
printf("Assuming units in meters!\n\n");
printf("<center>\n");
printf(" <x-m>%f</x-m>\n",center_x);
printf(" <y-m>%f</y-m>\n",center_y);
printf(" <z-m>%f</z-m>\n",center_z);
printf("</center>\n\n");
printf("<axis>\n");
printf(" <x>%f</x>\n",axis_x);
printf(" <y>%f</y>\n",axis_y);
printf(" <z>%f</z>\n",axis_z);
printf("</axis>\n\n");
}
return 0;
}

68
utils/Modeller/animcheck.pl Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/perl -w
# Quick & dirty script to find booboos in FlightGear animation files
# May 2004 Joshua Babcock jbabcock@atlantech.net
use strict;
if ( $#ARGV != 1 ) {
print "Usage: $0 <xml file> <ac3d file>\n";
exit;
}
my %Objects;
my %References;
my $Key;
my $Num;
my $First;
my $ObjCount=0;
my $CheckForm=1;
# Put whatever you want in here to check for poorly formatted object names.
sub CheckForm {
my $Bad=0;
$_[0] !~ /^[A-Z].*/ && ($Bad=1);
$_[0] =~ /\W/ && ($Bad=1);
print "$_[0] poorly formatted\n" if $Bad;
}
# Make a hash of all the object names in the AC3D file.
open (AC3D, $ARGV[1]) or die "Could not open $ARGV[1]";
while (<AC3D>) {
/^name \"(.*)\"/ && ($Objects{$1}+=1);
}
close AC3D;
# Check for duplicates and proper format.
foreach $Key (keys %Objects) {
print "$Objects{$Key} instances of $1\n" if ($Objects{$Key}>1);
&CheckForm($Key) if $CheckForm;
++$ObjCount;
}
print "$ObjCount objects found.\n\n";
# Make a hash of objects in the XML file that do not reference an object in the AC3D file.
open (XML, $ARGV[0]) or die "Could not open $ARGV[0]";
while (<XML>) {
if (m|<object-name>(.*)</object-name>| && ! exists($Objects{$1})) {
# voodoo, "Perl Cookbook", p140
push( @{$References{$1}}, $.);
}
}
close XML;
# List all the bad referencees.
foreach $Key (keys %References) {
$First=1;
print "Non-existant object $Key at line";
print "s" if (scalar( @{$References{$Key}}) > 1);
print ":";
foreach $Num (@{$References{$Key}}) {
$First ? ($First=0) : (print ",");
print " $Num"
}
print "\n";
}
exit 0;

View File

@@ -0,0 +1,585 @@
#!BPY
# """
# Name: 'FlightGear Animation (.xml)'
# Blender: 243
# Group: 'Export'
# Submenu: 'Generate textured lights' LIGHTS
# Submenu: 'Rotation' ROTATE
# Submenu: 'Range (LOD)' RANGE
# Submenu: 'Translation from origin' TRANS0
# Submenu: 'Translation from cursor' TRANSC
# Tooltip: 'FlightGear Animation'
# """
# BLENDER PLUGIN
#
# Put this file into ~/.blender/scripts/. You'll then find
# it in Blender under "File->Export->FlightGear Animation (*.xml)"
#
# For the script to work properly, make sure a directory
# ~/.blender/scripts/bpydata/config/ exists. To change the
# script parameters, edit file fgfs_animation.cfg in that
# dir, or in blender, switch to "Scripts Window" view, select
# "Scripts->System->Scripts Config Editor" and there select
# "Export->FlightGear Animation (*.xml)". Don't forget to
# "apply" the changes.
#
# This file is Public Domain.
__author__ = "Melchior FRANZ < mfranz # aon : at >"
__url__ = "http://members.aon.at/mfranz/flightgear/"
__version__ = "$Revision$ -- $Date$"
__bpydoc__ = """\
== Generate textured lights ==
Adds linked "light objects" (square consisting of two triangles at
origin) for each selected vertex, and creates FlightGear animation
code that scales all faces according to view distance, moves them to
the vertex location, turns the face towards the viewer and blends
it with other (semi)transparent objects. All lights are turned off
at daylight.
== Translation from origin ==
Generates "translate" animation for each selected vertex and for the
cursor, if it is not at origin. This mode is thought for moving
objects to their proper location after scaling them at origin, a
technique that is used for lights.
== Translation from cursor ==
Same as above, except that movements from the cursor location are
calculated.
== Rotation ==
Generates one "rotate" animation from two selected vertices
(rotation axis), and the cursor (rotation center).
== Range ==
Generates "range" animation skeleton with list of all selected objects.
"""
#==================================================================================================
import sys
import Blender
from math import sqrt
from Blender import Mathutils
from Blender.Mathutils import Vector, VecMultMat
REG_KEY = 'fgfs_animation'
MODE = __script__['arg']
ORIGIN = [0, 0, 0]
FILENAME = Blender.Get("filename")
(BASENAME, EXTNAME) = Blender.sys.splitext(FILENAME)
DIRNAME = Blender.sys.dirname(BASENAME)
FILENAME = Blender.sys.basename(BASENAME) + "-export.xml"
TOOLTIPS = {
'PATHNAME': "where exported data should be saved (current dir if empty)",
'INDENT': "number of spaces per indentation level, or 0 for tabs",
}
reg = Blender.Registry.GetKey(REG_KEY, True)
if not reg:
print "WRITING REGISTRY"
Blender.Registry.SetKey(REG_KEY, {
'INDENT': 0,
'PATHNAME': "",
'tooltips': TOOLTIPS,
}, True)
#==================================================================================================
class Error(Exception):
pass
class BlenderSetup:
def __init__(self):
#Blender.Window.WaitCursor(1)
self.is_editmode = Blender.Window.EditMode()
if self.is_editmode:
Blender.Window.EditMode(0)
def __del__(self):
#Blender.Window.WaitCursor(0)
if self.is_editmode:
Blender.Window.EditMode(1)
class XMLExporter:
def __init__(self, filename):
cursor = Blender.Window.GetCursorPos()
self.file = open(filename, "w")
self.write('<?xml version="1.0"?>\n')
self.write("<!-- project: %s -->\n" % Blender.Get("filename"))
self.write("<!-- cursor: %0.12f %0.12f %0.12f -->\n\n" % (cursor[0], cursor[1], cursor[2]))
self.write("<PropertyList>\n")
self.write("\t<path>%s.ac</path>\n\n" % BASENAME)
def __del__(self):
try:
self.write("</PropertyList>\n")
self.file.close()
except AttributeError: # opening in __init__ failed
pass
def write(self, s):
global INDENT
if INDENT <= 0:
self.file.write(s)
else:
self.file.write(s.expandtabs(INDENT))
def comment(self, s, e = "", a = ""):
self.write(a + "<!-- " + s + " -->\n" + e)
def translate(self, name, va, vb):
x = vb[0] - va[0]
y = vb[1] - va[1]
z = vb[2] - va[2]
length = sqrt(x * x + y * y + z * z)
s = """\
<animation>
<type>translate</type>
<object-name>%s</object-name>
<offset-m>%s</offset-m>
<axis>
<x>%s</x>
<y>%s</y>
<z>%s</z>
</axis>
</animation>\n\n"""
self.write(s % (name, Round(length), Round(x), Round(y), Round(z)))
def rotate(self, name, center, va, vb):
x = Round(vb[0] - va[0])
y = Round(vb[1] - va[1])
z = Round(vb[2] - va[2])
self.write("\t<!-- Vertex 0: %f %f %f -->\n" % (va[0], va[1], va[2]))
self.write("\t<!-- Vertex 1: %f %f %f -->\n" % (vb[0], vb[1], vb[2]))
s = """\
<animation>
<type>rotate</type>
<object-name>%s</object-name>
<property>null</property>
<!--min-deg>0</min-deg-->
<!--max-deg>360</max-deg-->
<!--factor>1</factor-->
<center>
<x-m>%s</x-m>
<y-m>%s</y-m>
<z-m>%s</z-m>
</center>
<axis>
<x>%s</x>
<y>%s</y>
<z>%s</z>
</axis>
</animation>\n\n"""
self.write(s % (name, Round(center[0]), Round(center[1]), Round(center[2]), x, y, z))
def billboard(self, name, spherical = "true"):
s = """\
<animation>
<type>billboard</type>
<object-name>%s</object-name>
<spherical type="bool">%s</spherical>
</animation>\n\n"""
self.write(s % (name, spherical))
def group(self, name, objects):
self.write("\t<animation>\n");
self.write("\t\t<name>%s</name>\n" % name);
for o in objects:
self.write("\t\t<object-name>%s</object-name>\n" % o.name)
self.write("\t</animation>\n\n");
def alphatest(self, name, factor = 0.001):
s = """\
<animation>
<type>alpha-test</type>
<object-name>%s</object-name>
<alpha-factor>%s</alpha-factor>
</animation>\n\n"""
self.write(s % (name, factor))
def sunselect(self, name, angle = 1.57):
s = """\
<animation>
<type>select</type>
<name>%s</name>
<object-name>%s</object-name>
<condition>
<greater-than>
<property>/sim/time/sun-angle-rad</property>
<value>%s</value>
</greater-than>
</condition>
</animation>\n\n"""
self.write(s % (name + "Night", name, angle))
def distscale(self, name, base):
s = """\
<animation>
<type>dist-scale</type>
<object-name>%s</object-name>
<interpolation>
<entry>
<ind>0</ind>
<dep alias="../../../../%sparams/light-near"/>
</entry>
<entry>
<ind>500</ind>
<dep alias="../../../../%sparams/light-med"/>
</entry>
<entry>
<ind>16000</ind>
<dep alias="../../../../%sparams/light-far"/>
</entry>
</interpolation>
</animation>\n\n"""
self.write(s % (name, base, base, base))
def range(self, objects):
self.write("\t<animation>\n")
self.write("\t\t<type>range</type>\n")
for o in objects:
self.write("\t\t<object-name>%s</object-name>\n" % o.name)
self.write("""\
<min-m>0</min-m>
<!--
<max-property>/sim/rendering/static-lod/detailed</max-property>
<min-property>/sim/rendering/static-lod/detailed</min-property>
<max-property>/sim/rendering/static-lod/rough</max-property>
<min-property>/sim/rendering/static-lod/rough</min-property>
-->
<max-property>/sim/rendering/static-lod/bare</max-property>\n""")
self.write("\t</animation>\n\n")
def lightrange(self, dist = 25000):
s = """\
<animation>
<type>range</type>
<min-m>0</min-m>
<max-m>%s</max-m>
</animation>\n\n"""
self.write(s % dist)
#==================================================================================================
def Round(f, digits = 6):
r = round(f, digits)
if r == int(r):
return str(int(r))
else:
return str(r)
def serial(i, max = 0):
if max == 1:
return ""
if max < 10:
return "%d" % i
if max < 100:
return "%02d" % i
if max < 1000:
return "%03d" % i
if max < 10000:
return "%04d" % i
return "%d" % i
def needsObjects(objects, n):
def error(e):
raise Error("wrong number of selected mesh objects: please select " + e)
if n < 0 and len(objects) < -n:
if n == -1:
error("at least one object")
else:
error("at least %d objects" % -n)
elif n > 0 and len(objects) != n:
if n == 1:
error("exactly one object")
else:
error("exactly %d objects" % n)
def checkName(name):
""" check if name is already in use """
try:
Blender.Object.Get(name)
raise Error("can't generate object '" + name + "'; name already in use")
except AttributeError:
pass
except ValueError:
pass
def selectedVertices(object):
verts = []
mat = object.getMatrix('worldspace')
for v in object.getData().verts:
if not v.sel:
continue
vec = Vector([v[0], v[1], v[2]])
vec.resize4D()
vec *= mat
v[0], v[1], v[2] = vec[0], vec[1], vec[2]
verts.append(v)
return verts
def createLightMesh(name, size = 1):
mesh = Blender.NMesh.New(name + "mesh")
mesh.mode = Blender.NMesh.Modes.NOVNORMALSFLIP
mesh.verts.append(Blender.NMesh.Vert(-size, 0, -size))
mesh.verts.append(Blender.NMesh.Vert(size, 0, -size))
mesh.verts.append(Blender.NMesh.Vert(size, 0, size))
mesh.verts.append(Blender.NMesh.Vert(-size, 0, size))
face1 = Blender.NMesh.Face()
face1.v.append(mesh.verts[0])
face1.v.append(mesh.verts[1])
face1.v.append(mesh.verts[2])
mesh.faces.append(face1)
face2 = Blender.NMesh.Face()
face2.v.append(mesh.verts[0])
face2.v.append(mesh.verts[2])
face2.v.append(mesh.verts[3])
mesh.faces.append(face2)
mat = Blender.Material.New(name + "mat")
mat.setRGBCol(1, 1, 1)
mat.setMirCol(1, 1, 1)
mat.setAlpha(1)
mat.setEmit(1)
mat.setSpecCol(0, 0, 0)
mat.setSpec(0)
mat.setAmb(0)
mesh.setMaterials([mat])
return mesh
def createLight(mesh, name):
object = Blender.Object.New("Mesh", name)
object.link(mesh)
Blender.Scene.getCurrent().link(object)
return object
# modes ===========================================================================================
class mode:
def __init__(self, xml = None):
self.cursor = Blender.Window.GetCursorPos()
self.objects = [o for o in Blender.Object.GetSelected() if o.getType() == 'Mesh']
if xml is not None:
BlenderSetup()
self.test()
self.execute(xml)
def test(self):
pass
class translationFromOrigin(mode):
def execute(self, xml):
if self.cursor != ORIGIN:
xml.translate("BlenderCursor", ORIGIN, self.cursor)
needsObjects(self.objects, 1)
object = self.objects[0]
verts = selectedVertices(object)
if len(verts):
xml.comment('[%s] translate from origin: "%s"' % (BASENAME, object.name), '\n')
for i, v in enumerate(verts):
xml.translate("X%d" % i, ORIGIN, v)
class translationFromCursor(mode):
def test(self):
needsObjects(self.objects, 1)
self.object = self.objects[0]
self.verts = selectedVertices(self.object)
if not len(self.verts):
raise Error("no vertex selected")
def execute(self, xml):
xml.comment('[%s] translate from cursor: "%s"' % (BASENAME, self.object.name), '\n')
for i, v in enumerate(self.verts):
xml.translate("X%d" % i, self.cursor, v)
class rotation(mode):
def test(self):
needsObjects(self.objects, 1)
self.object = self.objects[0]
self.verts = selectedVertices(self.object)
if len(self.verts) != 2:
raise Error("you have to select two vertices that define the rotation axis!")
def execute(self, xml):
xml.comment('[%s] rotate "%s"' % (BASENAME, self.object.name), '\n')
if self.cursor == ORIGIN:
Blender.Draw.PupMenu("The cursor is still at origin!%t|"\
"But nevertheless, I pretend it is the rotation center.")
xml.rotate(self.object.name, self.cursor, self.verts[0], self.verts[1])
class levelOfDetail(mode):
def test(self):
needsObjects(self.objects, -1)
def execute(self, xml):
xml.comment('[%s] level of detail' % BASENAME, '\n')
xml.range(self.objects)
class interpolation(mode):
def test(self):
needsObjects(self.objects, -2)
def execute(self, xml):
print
for i, o in enumerate(self.objects):
print "%d: %s" % (i, o.name)
raise Error("this mode doesn't do anything useful yet")
class texturedLights(mode):
def test(self):
needsObjects(self.objects, 1)
self.object = self.objects[0]
self.verts = selectedVertices(self.object)
if not len(self.verts):
raise Error("there are no vertices to put lights at")
self.lightname = self.object.name + "X"
checkName(self.lightname)
for i, v in enumerate(self.verts):
checkName(self.lightname + serial(i, len(self.verts)))
def execute(self, xml):
lightname = self.lightname
verts = self.verts
object = self.object
lightmesh = createLightMesh(lightname)
lights = []
for i, v in enumerate(verts):
lights.append(createLight(lightmesh, lightname + serial(i, len(verts))))
parent = object.getParent()
if parent is not None:
parent.makeParent(lights)
for l in lights:
l.Layer = object.Layer
xml.lightrange()
xml.write("\t<%sparams>\n" % lightname)
xml.write("\t\t<light-near>%s</light-near>\n" % "0.4")
xml.write("\t\t<light-med>%s</light-med>\n" % "0.8")
xml.write("\t\t<light-far>%s</light-far>\n" % "10")
xml.write("\t</%sparams>\n\n" % lightname)
if len(lights) == 1:
xml.sunselect(lightname)
xml.alphatest(lightname)
else:
xml.group(lightname + "Group", lights)
xml.sunselect(lightname + "Group")
xml.alphatest(lightname + "Group")
for i, l in enumerate(lights):
xml.translate(l.name, ORIGIN, verts[i])
for l in lights:
xml.billboard(l.name)
for l in lights:
xml.distscale(l.name, lightname)
Blender.Redraw(-1)
execute = {
'LIGHTS' : texturedLights,
'TRANS0' : translationFromOrigin,
'TRANSC' : translationFromCursor,
'ROTATE' : rotation,
'RANGE' : levelOfDetail,
'INTERPOL' : interpolation
}
# main() ==========================================================================================
def dofile(filename):
try:
xml = XMLExporter(filename)
execute[MODE](xml)
except Error, e:
xml.comment("ERROR: " + e.args[0], '\n')
raise Error(e.args[0])
except IOError, (errno, strerror):
raise Error(strerror)
def main():
try:
global FILENAME, INDENT
reg = Blender.Registry.GetKey(REG_KEY, True)
if reg:
PATHNAME = reg['PATHNAME'] or ""
INDENT = reg['INDENT'] or 0
else:
PATHNAME = ""
INDENT = 0
if PATHNAME:
FILENAME = Blender.sys.join(PATHNAME, FILENAME)
print 'writing to "' + FILENAME + '"'
dofile(FILENAME)
except Error, e:
print "ERROR: " + e.args[0]
Blender.Draw.PupMenu("ERROR%t|" + e.args[0])
if MODE:
main()

View File

@@ -0,0 +1,134 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h> // exit()
#include <simgear/bucket/newbucket.hxx>
#include <simgear/math/sg_geodesy.hxx>
int make_model( const char *texture,
double ll_lon, double ll_lat,
double lr_lon, double lr_lat,
double ur_lon, double ur_lat,
double ul_lon, double ul_lat,
const char *model )
{
// open the output file
FILE *out = fopen( model, "w" );
if ( out == NULL ) {
printf("Error: cannot open %s for writing\n", model);
return 0;
}
// compute polygon boundaries in local XY projection
double cen_lon = (ll_lon + lr_lon + ur_lon + ul_lon) / 4.0;
double cen_lat = (ll_lat + lr_lat + ur_lat + ul_lat) / 4.0;
double az1, az2, dist;
double angle;
geo_inverse_wgs_84( cen_lat, cen_lon, ll_lat, ll_lon, &az1, &az2, &dist );
angle = az1 * SGD_DEGREES_TO_RADIANS;
double llx = sin(angle) * dist;
double lly = cos(angle) * dist;
printf( "az1 = %.3f dx = %.3f dy = %.3f\n",
az1, sin(angle) * dist, cos(angle) * dist );
geo_inverse_wgs_84( cen_lat, cen_lon, lr_lat, lr_lon, &az1, &az2, &dist );
angle = az1 * SGD_DEGREES_TO_RADIANS;
double lrx = sin(angle) * dist;
double lry = cos(angle) * dist;
printf( "az1 = %.3f dx = %.3f dy = %.3f\n",
az1, sin(angle) * dist, cos(angle) * dist );
geo_inverse_wgs_84( cen_lat, cen_lon, ur_lat, ur_lon, &az1, &az2, &dist );
angle = az1 * SGD_DEGREES_TO_RADIANS;
double urx = sin(angle) * dist;
double ury = cos(angle) * dist;
printf( "az1 = %.3f dx = %.3f dy = %.3f\n",
az1, sin(angle) * dist, cos(angle) * dist );
geo_inverse_wgs_84( cen_lat, cen_lon, ul_lat, ul_lon, &az1, &az2, &dist );
angle = az1 * SGD_DEGREES_TO_RADIANS;
double ulx = sin(angle) * dist;
double uly = cos(angle) * dist;
printf( "az1 = %.3f dx = %.3f dy = %.3f\n",
az1, sin(angle) * dist, cos(angle) * dist );
fprintf( out, "AC3Db\n" );
fprintf( out, "MATERIAL \"DefaultWhite\" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0\n" );
fprintf( out, "OBJECT world\n" );
fprintf( out, "kids 1\n" );
fprintf( out, "OBJECT poly\n" );
fprintf( out, "name \"terrain\"\n" );
fprintf( out, "data 9\n" );
fprintf( out, "Plane.073\n" );
fprintf( out, "texture \"%s\"\n", texture );
fprintf( out, "texrep 1 1\n" );
fprintf( out, "crease 30.000000\n" );
fprintf( out, "numvert 4\n" );
fprintf( out, "%.3f 0 %.3f\n", lry, lrx );
fprintf( out, "%.3f 0 %.3f\n", lly, llx );
fprintf( out, "%.3f 0 %.3f\n", uly, ulx );
fprintf( out, "%.3f 0 %.3f\n", ury, urx );
fprintf( out, "numsurf 1\n" );
fprintf( out, "SURF 0x00\n" );
fprintf( out, "mat 0\n" );
fprintf( out, "refs 4\n" );
fprintf( out, "0 0.0 1.0\n" );
fprintf( out, "3 0.0 0.0\n" );
fprintf( out, "2 1.0 0.0\n" );
fprintf( out, "1 1.0 1.0\n" );
fprintf( out, "kids 0\n" );
fclose( out );
SGBucket b( cen_lon, cen_lat );
printf("\n");
printf("Model is created as %s\n", model);
printf("\n");
printf("Now copy the .ac and .rgb file to your Model directory\n");
printf("and add the following line to your .stg file:\n");
printf("\n");
printf(" %s/%s.stg\n",
b.gen_base_path().c_str(), b.gen_index_str().c_str());
printf("\n");
printf("OBJECT_SHARED Models/MNUAV/%s %.6f %.6f ALT_IN_METERS 0.00\n",
model, cen_lon, cen_lat);
return 1;
}
void usage( char *prog ) {
printf("Usage: %s photo.rgb ll_lon ll_lat lr_lon lr_lat ur_lon ur_lat ul_lon ul_lat model.ac\n", prog);
printf("Usage: %s photo.rgb left_lon right_lon lower_lat upper_lat model.ac\n", prog);
exit( 0 );
}
int main( int argc, char **argv ) {
if ( argc == 11 ) {
make_model( argv[1],
atof(argv[2]), atof(argv[3]), atof(argv[4]), atof(argv[5]),
atof(argv[6]), atof(argv[7]), atof(argv[8]), atof(argv[9]),
argv[10] );
} else if ( argc == 7 ) {
make_model( argv[1],
atof(argv[2]), atof(argv[4]), atof(argv[3]), atof(argv[4]),
atof(argv[3]), atof(argv[5]), atof(argv[2]), atof(argv[5]),
argv[6] );
} else {
usage( argv[0] );
}
return 0;
}

View File

@@ -0,0 +1,174 @@
#!BPY
# """
# Name: 'UV: Export to SVG'
# Blender: 245
# Group: 'Image'
# Tooltip: 'Export selected objects to SVG file'
# """
__author__ = "Melchior FRANZ < mfranz # aon : at >"
__url__ = ["http://www.flightgear.org/", "http://cvs.flightgear.org/viewvc/source/utils/Modeller/uv_export_svg.py"]
__version__ = "0.1"
__bpydoc__ = """\
Saves the UV mappings of all selected objects to an SVG file. The uv_import_svg.py
script can be used to re-import such a file. Each object and each group of adjacent
faces therein will be made a separate SVG group.
"""
#--------------------------------------------------------------------------------
# Copyright (C) 2008 Melchior FRANZ < mfranz # aon : at >
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#--------------------------------------------------------------------------------
FILL_COLOR = None # 'yellow' or '#ffa000' e.g. for uni-color, None for random color
ID_SEPARATOR = '_.:._'
import Blender, BPyMessages, sys, random
class Abort(Exception):
def __init__(self, msg):
self.msg = msg
class UVFaceGroups:
def __init__(self, mesh):
faces = dict([(f.index, f) for f in mesh.faces])
self.groups = []
while faces:
self.groups.append(self.adjacent(faces))
def __len__(self):
return len(self.groups)
def __iter__(self):
return self.groups.__iter__()
def adjacent(self, faces):
uvcoords = {}
face = faces.popitem()[1]
group = [face]
for c in face.uv:
uvcoords[tuple(c)] = True
while True:
found = []
for face in faces.itervalues():
for c in face.uv:
if tuple(c) in uvcoords:
for c in face.uv:
uvcoords[tuple(c)] = True
found.append(face)
break
if not found:
return group
for face in found:
group.append(face)
del faces[face.index]
def stringcolor(string):
random.seed(hash(string))
c = [random.randint(220, 255), random.randint(120, 240), random.randint(120, 240)]
random.shuffle(c)
return "#%02x%02x%02x" % tuple(c)
def write_svg(path):
size = Blender.Draw.PupMenu("Image size%t|128|256|512|1024|2048|4096|8192")
if size < 0:
raise Abort('no image size chosen')
size = 1 << (size + 6)
svg = open(path, "w")
svg.write('<?xml version="1.0" standalone="no"?>\n')
svg.write('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n\n')
svg.write('<svg width="%spx" height="%spx" viewBox="0 0 %d %d" xmlns="http://www.w3.org/2000/svg"' \
' version="1.1" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">\n'
% (size, size, size, size))
svg.write("\t<desc>uv_export_svg.py: %s</desc>\n" % path);
svg.write('\t<rect x="0" y="0" width="%d" height="%d" fill="none" stroke="blue" stroke-width="%f"/>\n'
% (size, size, 1.0))
objects = {}
for o in Blender.Scene.GetCurrent().objects.selected:
if o.type != "Mesh":
continue
mesh = o.getData(mesh = 1)
if mesh.faceUV:
objects[mesh.name] = (o.name, mesh)
for meshname, v in objects.iteritems():
objname, mesh = v
color = FILL_COLOR or stringcolor(meshname)
svg.write('\t<g style="fill:%s; stroke:black stroke-width:1px" inkscape:label="%s" ' \
'id="%s">\n' % (color, objname, objname))
facegroups = UVFaceGroups(mesh)
for faces in facegroups:
indent = '\t\t'
if len(facegroups) > 1:
svg.write('\t\t<g>\n')
indent = '\t\t\t'
for f in faces:
svg.write('%s<polygon id="%s%s%d" points="' % (indent, meshname, ID_SEPARATOR, f.index))
for p in f.uv:
svg.write('%.8f,%.8f ' % (p[0] * size, size - p[1] * size))
svg.write('"/>\n')
if len(facegroups) > 1:
svg.write('\t\t</g>\n')
svg.write("\t</g>\n")
svg.write('</svg>\n')
svg.close()
def export(path):
if not BPyMessages.Warning_SaveOver(path):
return
editmode = Blender.Window.EditMode()
if editmode:
Blender.Window.EditMode(0)
try:
write_svg(path)
Blender.Registry.SetKey("UVImportExportSVG", { "path" : path }, False)
except Abort, e:
print "Error:", e.msg, " -> aborting ...\n"
Blender.Draw.PupMenu("Error%t|" + e.msg)
if editmode:
Blender.Window.EditMode(1)
registry = Blender.Registry.GetKey("UVImportExportSVG", False)
if registry and "path" in registry:
path = registry["path"]
else:
active = Blender.Scene.GetCurrent().objects.active
basename = Blender.sys.basename(Blender.sys.splitext(Blender.Get("filename"))[0])
path = basename + "-" + active.name + ".svg"
Blender.Window.FileSelector(export, "Export to SVG", path)

308
utils/Modeller/uv_import_svg.py Executable file
View File

@@ -0,0 +1,308 @@
#!BPY
# """
# Name: 'UV: (Re)Import UV from SVG'
# Blender: 245
# Group: 'Image'
# Tooltip: 'Re-import UV layout from SVG file'
# """
__author__ = "Melchior FRANZ < mfranz # aon : at >"
__url__ = ["http://www.flightgear.org/", "http://cvs.flightgear.org/viewvc/source/utils/Modeller/uv_import_svg.py"]
__version__ = "0.2"
__bpydoc__ = """\
Imports an SVG file containing UV maps, which has been saved by the
uv_export.svg script. This allows to move, scale, and rotate object
mappings in SVG editors like Inkscape. Note that all contained UV maps
will be set, no matter which objects are actually selected at the moment.
The choice has been made when the file was saved!
"""
#--------------------------------------------------------------------------------
# Copyright (C) 2008 Melchior FRANZ < mfranz # aon : at >
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#--------------------------------------------------------------------------------
ID_SEPARATOR = '_.:._'
import Blender, BPyMessages, sys, math, re, xml.sax
numwsp = re.compile('(?<=[\d.])\s+(?=[-+.\d])')
commawsp = re.compile('\s+|\s*,\s*')
istrans = re.compile('^\s*(skewX|skewY|scale|translate|rotate|matrix)\s*\(([^\)]*)\)\s*')
isnumber = re.compile('^[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?$')
class Abort(Exception):
def __init__(self, msg):
self.msg = msg
class Matrix:
def __init__(self, a = 1, b = 0, c = 0, d = 1, e = 0, f = 0):
self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f
def __str__(self):
return "[Matrix %f %f %f %f %f %f]" % (self.a, self.b, self.c, self.d, self.e, self.f)
def multiply(self, mat):
a = mat.a * self.a + mat.c * self.b
b = mat.b * self.a + mat.d * self.b
c = mat.a * self.c + mat.c * self.d
d = mat.b * self.c + mat.d * self.d
e = mat.a * self.e + mat.c * self.f + mat.e
f = mat.b * self.e + mat.d * self.f + mat.f
self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f
def transform(self, u, v):
return u * self.a + v * self.c + self.e, u * self.b + v * self.d + self.f
def translate(self, dx, dy):
self.multiply(Matrix(1, 0, 0, 1, dx, dy))
def scale(self, sx, sy):
self.multiply(Matrix(sx, 0, 0, sy, 0, 0))
def rotate(self, a):
a *= math.pi / 180
self.multiply(Matrix(math.cos(a), math.sin(a), -math.sin(a), math.cos(a), 0, 0))
def skewX(self, a):
a *= math.pi / 180
self.multiply(Matrix(1, 0, math.tan(a), 1, 0, 0))
def skewY(self, a):
a *= math.pi / 180
self.multiply(Matrix(1, math.tan(a), 0, 1, 0, 0))
def parse_transform(s):
matrix = Matrix()
while True:
match = istrans.match(s)
if not match:
break
cmd = match.group(1)
values = commawsp.split(match.group(2).strip())
s = s[len(match.group(0)):]
arg = []
for value in values:
match = isnumber.match(value)
if not match:
raise Abort("bad transform value")
arg.append(float(match.group(0)))
num = len(arg)
if cmd == "skewX":
if num == 1:
matrix.skewX(arg[0])
continue
elif cmd == "skewY":
if num == 1:
matrix.skewY(arg[0])
continue
elif cmd == "scale":
if num == 1:
matrix.scale(arg[0], arg[0])
continue
if num == 2:
matrix.scale(arg[0], arg[1])
continue
elif cmd == "translate":
if num == 1:
matrix.translate(arg[0], 0)
continue
if num == 2:
matrix.translate(arg[0], arg[1])
continue
elif cmd == "rotate":
if num == 1:
matrix.rotate(arg[0])
continue
if num == 3:
matrix.translate(-arg[1], -arg[2])
matrix.rotate(arg[0])
matrix.translate(arg[1], arg[2])
continue
elif cmd == "matrix":
if num == 6:
matrix.multiply(Matrix(*arg))
continue
else:
print "ERROR: unknown transform", cmd
continue
print "ERROR: '%s' with wrong argument number (%d)" % (cmd, num)
if len(s):
print "ERROR: transform with trailing garbage (%s)" % s
return matrix
class import_svg(xml.sax.handler.ContentHandler):
# err_handler
def error(self, exception):
raise Abort(str(exception))
def fatalError(self, exception):
raise Abort(str(exception))
def warning(self, exception):
print "WARNING: " + str(exception)
# doc_handler
def setDocumentLocator(self, whatever):
pass
def startDocument(self):
self.verified = False
self.scandesc = False
self.matrices = [None]
self.meshes = {}
for o in Blender.Scene.GetCurrent().objects:
if o.type != "Mesh":
continue
mesh = o.getData(mesh = 1)
if not mesh.faceUV:
continue
if mesh.name in self.meshes:
continue
self.meshes[mesh.name] = mesh
def endDocument(self):
pass
def characters(self, data):
if not self.scandesc:
return
if data.startswith("uv_export_svg.py"):
self.verified = True
def ignorableWhitespace(self, data, start, length):
pass
def processingInstruction(self, target, data):
pass
def startElement(self, name, attrs):
currmat = self.matrices[-1]
if "transform" in attrs:
m = parse_transform(attrs["transform"])
if currmat is not None:
m.multiply(currmat)
self.matrices.append(m)
else:
self.matrices.append(currmat)
if name == "polygon":
self.handlePolygon(attrs)
elif name == "svg":
if "viewBox" in attrs:
x, y, w, h = commawsp.split(attrs["viewBox"], 4)
if int(x) or int(y):
raise Abort("bad viewBox")
self.width = int(w)
self.height = int(h)
if self.width != self.height:
raise Abort("viewBox isn't a square")
else:
raise Abort("no viewBox")
elif name == "desc" and not self.verified:
self.scandesc = True
def endElement(self, name):
self.scandesc = False
self.matrices.pop()
def handlePolygon(self, attrs):
if not self.verified:
raise Abort("this file wasn't written by uv_export_svg.py")
ident = attrs.get("id", None)
points = attrs.get("points", None)
if not ident or not points:
print('bad polygon "%s"' % ident)
return
try:
meshname, num = ident.strip().split(ID_SEPARATOR, 2)
except:
print('broken id "%s"' % ident)
return
if not meshname in self.meshes:
print('unknown mesh "%s"' % meshname)
return
#print 'mesh %s face %d: ' % (meshname, num)
matrix = self.matrices[-1]
transuv = []
for p in numwsp.split(points.strip()):
u, v = commawsp.split(p.strip(), 2)
u = float(u)
v = float(v)
if matrix:
u, v = matrix.transform(u, v)
transuv.append((u / self.width, 1 - v / self.height))
for i, uv in enumerate(self.meshes[meshname].faces[int(num)].uv):
uv[0] = transuv[i][0]
uv[1] = transuv[i][1]
def run_parser(path):
if BPyMessages.Error_NoFile(path):
return
editmode = Blender.Window.EditMode()
if editmode:
Blender.Window.EditMode(0)
Blender.Window.WaitCursor(1)
try:
xml.sax.parse(path, import_svg(), import_svg())
Blender.Registry.SetKey("UVImportExportSVG", { "path" : path }, False)
except Abort, e:
print "Error:", e.msg, " -> aborting ...\n"
Blender.Draw.PupMenu("Error%t|" + e.msg)
Blender.Window.RedrawAll()
Blender.Window.WaitCursor(0)
if editmode:
Blender.Window.EditMode(1)
registry = Blender.Registry.GetKey("UVImportExportSVG", False)
if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])):
path = registry["path"]
else:
path = ""
Blender.Window.FileSelector(run_parser, "Import SVG", path)

172
utils/Modeller/uv_pack.py Normal file
View File

@@ -0,0 +1,172 @@
#!BPY
# """
# Name: 'Pack selected objects on a square'
# Blender: 245
# Group: 'UV'
# Tooltip: 'Pack UV maps of all selected objects onto an empty square texture'
# """
__author__ = "Melchior FRANZ < mfranz # aon : at >"
__url__ = "http://members.aon.at/mfranz/flightgear/"
__version__ = "0.1"
__bpydoc__ = """\
Script for mapping multiple objects onto one square texture.
Usage:
(1) create new square texture in the UV editor
(2) map all objects individually, choosing the most appropriate technique
(3) scale each of the mappings to the appropriate size, relative to the
other object mappings
(4) select all objects and switch to edit mode (consider to use
Select->Linked->Material or similar methods)
(5) start this script with UVs->Scripts->Pack objects on a square
[now the texture image will first be erased, then colored rectangles
will appear for each object]
(6) rescale and/or remap objects that you aren't happy with (the
relative size of a mapping will be kept)
[continue with (5) until you like the result]
(7) export UV layout to SVG (UVs->Scripts->Save UV Face Layout)
"""
#--------------------------------------------------------------------------------
# Copyright (C) 2008 Melchior FRANZ < mfranz # aon : at >
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#--------------------------------------------------------------------------------
MARGIN = 10 # px
GAP = 10 # px
import Blender
from random import randint as rand
class Abort(Exception):
def __init__(self, msg):
self.msg = msg
def pack():
image = Blender.Image.GetCurrent()
if not image:
raise Abort('No texture image selected')
imgwidth, imgheight = image.getSize()
if imgwidth != imgheight:
Blender.Draw.PupMenu("Warning%t|Image isn't a square!")
gap = (float(GAP) / imgwidth, float(GAP) / imgheight)
margin = (float(MARGIN) / imgwidth - gap[0] * 0.5, float(MARGIN) / imgheight - gap[1] * 0.5)
def drawrect(x0, y0, x1, y1, color = (255, 255, 255, 255)):
x0 *= imgwidth
x1 *= imgwidth
y0 *= imgheight
y1 *= imgheight
for u in range(int(x0 + 0.5), int(x1 - 0.5)):
for v in range(int(y0 + 0.5), int(y1 - 0.5)):
image.setPixelI(u, v, color)
boxes = []
unique_meshes = {}
BIG = 1<<30
Blender.Window.DrawProgressBar(0.0, "packing")
for o in Blender.Scene.GetCurrent().objects.selected:
if o.type != "Mesh":
continue
mesh = o.getData(mesh = 1)
if not mesh.faceUV:
continue
if mesh.name in unique_meshes:
#print "dropping duplicate mesh", mesh.name, "of object", o.name
continue
unique_meshes[mesh.name] = True
print "\tobject '%s'" % o.name
xmin = ymin = BIG
xmax = ymax = -BIG
for f in mesh.faces:
for p in f.uv:
xmin = min(xmin, p[0])
xmax = max(xmax, p[0])
ymin = min(ymin, p[1])
ymax = max(ymax, p[1])
width = xmax - xmin
height = ymax - ymin
boxes.append([0, 0, width + gap[0], height + gap[1], xmin, ymin, mesh])
if not boxes:
raise Abort('No mesh objects selected')
boxwidth, boxheight = Blender.Geometry.BoxPack2D(boxes)
boxmax = max(boxwidth, boxheight)
xscale = (1.0 - 2.0 * margin[0]) / boxmax
yscale = (1.0 - 2.0 * margin[1]) / boxmax
image.reload()
#drawrect(0, 0, 1, 1) # erase texture
for i, box in enumerate(boxes):
Blender.Window.DrawProgressBar(float(i) * len(boxes), "Drawing")
xmin = ymin = BIG
xmax = ymax = -BIG
for f in box[6].faces:
for p in f.uv:
p[0] = (p[0] - box[4] + box[0] + gap[0] * 0.5 + margin[0]) * xscale
p[1] = (p[1] - box[5] + box[1] + gap[1] * 0.5 + margin[1]) * yscale
xmin = min(xmin, p[0])
xmax = max(xmax, p[0])
ymin = min(ymin, p[1])
ymax = max(ymax, p[1])
drawrect(xmin, ymin, xmax, ymax, (rand(128, 255), rand(128, 255), rand(128, 255), 255))
box[6].update()
Blender.Window.RedrawAll()
Blender.Window.DrawProgressBar(1.0, "Finished")
editmode = Blender.Window.EditMode()
if editmode:
Blender.Window.EditMode(0)
Blender.Window.WaitCursor(1)
try:
print "box packing ..."
pack()
print "done\n"
except Abort, e:
print "Error:", e.msg, " -> aborting ...\n"
Blender.Draw.PupMenu("Error%t|" + e.msg)
Blender.Window.WaitCursor(0)
if editmode:
Blender.Window.EditMode(1)

View File

@@ -0,0 +1,827 @@
#!BPY
# """
# Name: 'YASim (.xml)'
# Blender: 245
# Group: 'Import'
# Tooltip: 'Loads and visualizes a YASim FDM geometry'
# """
__author__ = "Melchior FRANZ < mfranz # aon : at >"
__url__ = ["http://www.flightgear.org/", "http://cvs.flightgear.org/viewvc/source/utils/Modeller/yasim_import.py"]
__version__ = "0.2"
__bpydoc__ = """\
yasim_import.py loads and visualizes a YASim FDM geometry
=========================================================
It is recommended to load the model superimposed over a greyed out and immutable copy of the aircraft model:
(0) put this script into ~/.blender/scripts/
(1) load or import aircraft model (menu -> "File" -> "Import" -> "AC3D (.ac) ...")
(2) create new *empty* scene (menu -> arrow button left of "SCE:scene1" combobox -> "ADD NEW" -> "empty")
(3) rename scene to yasim (not required)
(4) link to scene1 (F10 -> "Output" tab in "Buttons Window" -> arrow button left of text entry "No Set Scene" -> "scene1")
(5) now load the YASim config file (menu -> "File" -> "Import" -> "YASim (.xml) ...")
This is good enough for simple checks. But if you are working on the YASim configuration, then you need a
quick and convenient way to reload the file. In that case continue after (4):
(5) switch the button area at the bottom of the blender screen to "Scripts Window" mode (green python snake icon)
(6) load the YASim config file (menu -> "Scripts" -> "Import" -> "YASim (.xml) ...")
(7) make the "Scripts Window" area as small as possible by dragging the area separator down
(8) optionally split the "3D View" area and switch the right part to the "Outliner"
(9) press the "Reload YASim" button in the script area to reload the file
If the 3D model is displaced with respect to the FDM model, then the <offsets> values from the
model animation XML file should be added as comment to the YASim config file, as a line all by
itself, with no spaces surrounding the equal signs. Spaces elsewhere are allowed. For example:
<offsets>
<x-m>3.45</x-m>
<z-m>-0.4</z-m>
<pitch-deg>5</pitch-deg>
</offsets>
becomes:
<!-- offsets: x=3.45 z=-0.4 p=5 -->
Possible variables are:
x ... <x-m>
y ... <y-m>
z ... <z-m>
h ... <heading-deg>
p ... <pitch-deg>
r ... <roll-deg>
Of course, absolute FDM coordinates can then no longer directly be read from Blender's 3D view.
The cursor coordinates display in the script area, however, shows the coordinates in YASim space.
Note that object names don't contain XML indices but element numbers. YASim_flap0#2 is the third
flap0 in the whole file, not necessarily in its parent XML group. A floating point part in the
object name (e.g. YASim_flap0#2.004) only means that the geometry has been reloaded that often.
It's an unavoidable consequence of how Blender deals with meshes.
Elements are displayed as follows:
cockpit -> monkey head
fuselage -> blue "tube" (with only 12 sides for less clutter); center at "a"
vstab -> red with yellow control surfaces (flap0, flap1, slat, spoiler)
wing/mstab/hstab -> green with yellow control surfaces (which are always 20 cm deep);
symmetric surfaces are only displayed on the left side, unless
the "Mirror" button is active
thrusters (jet/propeller/thruster) -> dashed line from center to actionpt;
arrow from actionpt along thrust vector (always 1 m long);
propeller circle
rotor -> radius and rel_len_blade_start circle, normal and forward vector,
one blade at phi0 with direction arrow near blade tip
gear -> contact point and compression vector (no arrow head)
tank -> magenta cube (10 cm side length)
weight -> inverted cyan cone
ballast -> yellow cylinder
hitch -> hexagon (10 cm diameter)
hook -> dashed line for up angle, T-line for down angle
launchbar -> dashed line for up angles, T-line for down angles
(launchbar and holdback each)
The Mirror button complements symmetrical surfaces (wing/hstab/mstab) and control surfaces
(flap0/flap1/slat/spoiler). This is useful for asymmetrical aircraft, but has the disadvantage
that it moves the surfaces' object centers from their usual place, yasim's [x, y, z] value,
to [0, 0, 0]. Turning mirroring off restores the object center.
Environment variable BLENDER_YASIM_IMPORT can be set to a space-separated list of options:
$ BLENDER_YASIM_IMPORT="mirror verbose" blender
whereby:
verbose ... enables verbose logs
mirror ... enables mirroring of symmetric surfaces
"""
#--------------------------------------------------------------------------------
# Copyright (C) 2009 Melchior FRANZ < mfranz # aon : at >
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#--------------------------------------------------------------------------------
import Blender, BPyMessages, string, math, os
from Blender.Mathutils import *
from xml.sax import handler, make_parser
CONFIG = string.split(os.getenv("BLENDER_YASIM_IMPORT") or "")
YASIM_MATRIX = Matrix([-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1])
ORIGIN = Vector(0, 0, 0)
X = Vector(1, 0, 0)
Y = Vector(0, 1, 0)
Z = Vector(0, 0, 1)
DEG2RAD = math.pi / 180
RAD2DEG = 180 / math.pi
NO_EVENT = 0
RELOAD_BUTTON = 1
CURSOR_BUTTON = 2
MIRROR_BUTTON = 3
class Global:
verbose = "verbose" in CONFIG
path = ""
matrix = None
data = None
cursor = ORIGIN
last_cursor = Vector(Blender.Window.GetCursorPos())
mirror_button = Blender.Draw.Create("mirror" in CONFIG)
class Abort(Exception):
def __init__(self, msg, term = None):
self.msg = msg
self.term = term
def log(msg):
if Global.verbose:
print(msg)
def draw_dashed_line(mesh, start, end):
w = 0.04
step = w * (end - start).normalize()
n = len(mesh.verts)
for i in range(int(1 + 0.5 * (end - start).length / w)):
a = start + 2 * i * step
b = a + step
if (b - end).length < step.length:
b = end
mesh.verts.extend([a, b])
mesh.edges.extend([n + 2 * i, n + 2 * i + 1])
def draw_arrow(mesh, start, end):
v = end - start
m = v.toTrackQuat('x', 'z').toMatrix().resize4x4() * TranslationMatrix(start)
v = v.length * X
n = len(mesh.verts)
mesh.verts.extend([ORIGIN * m , v * m, (v - 0.05 * X + 0.05 * Y) * m, (v - 0.05 * X - 0.05 * Y) * m]) # head
mesh.verts.extend([(ORIGIN + 0.05 * Y) * m, (ORIGIN - 0.05 * Y) * m]) # base
mesh.edges.extend([[n, n + 1], [n + 1, n + 2], [n + 1, n + 3], [n + 4, n + 5]])
def draw_circle(mesh, numpoints, radius, matrix):
n = len(mesh.verts)
for i in range(numpoints):
angle = 2.0 * math.pi * i / numpoints
v = Vector(radius * math.cos(angle), radius * math.sin(angle), 0)
mesh.verts.extend([v * matrix])
for i in range(numpoints):
i1 = (i + 1) % numpoints
mesh.edges.extend([[n + i, n + i1]])
class Item:
scene = Blender.Scene.GetCurrent()
def make_twosided(self, mesh):
mesh.faceUV = True
for f in mesh.faces:
f.mode |= Blender.Mesh.FaceModes.TWOSIDE | Blender.Mesh.FaceModes.OBCOL
def set_color(self, obj, color):
mat = Blender.Material.New()
mat.setRGBCol(color[0], color[1], color[2])
mat.setAlpha(color[3])
mat.mode |= Blender.Material.Modes.ZTRANSP | Blender.Material.Modes.TRANSPSHADOW
obj.transp = True
mesh = obj.getData(mesh = True)
mesh.materials += [mat]
for f in mesh.faces:
f.smooth = True
mesh.calcNormals()
class Cockpit(Item):
def __init__(self, center):
mesh = Blender.Mesh.Primitives.Monkey()
mesh.transform(ScaleMatrix(0.13, 4) * Euler(90, 0, 90).toMatrix().resize4x4() * TranslationMatrix(Vector(-0.1, 0, -0.032)))
obj = self.scene.objects.new(mesh, "YASim_cockpit")
obj.setMatrix(TranslationMatrix(center) * Global.matrix)
class Tank(Item):
def __init__(self, name, center):
mesh = Blender.Mesh.Primitives.Cube()
mesh.transform(ScaleMatrix(0.05, 4))
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(TranslationMatrix(center) * Global.matrix)
self.set_color(obj, [1, 0, 1, 0.5])
class Ballast(Item):
def __init__(self, name, center):
mesh = Blender.Mesh.Primitives.Cylinder()
mesh.transform(ScaleMatrix(0.05, 4))
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(TranslationMatrix(center) * Global.matrix)
self.set_color(obj, [1, 1, 0, 0.5])
class Weight(Item):
def __init__(self, name, center):
mesh = Blender.Mesh.Primitives.Cone()
mesh.transform(ScaleMatrix(0.05, 4))
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(TranslationMatrix(center) * Global.matrix)
self.set_color(obj, [0, 1, 1, 0.5])
class Gear(Item):
def __init__(self, name, center, compression):
mesh = Blender.Mesh.New()
mesh.verts.extend([ORIGIN, compression])
mesh.edges.extend([0, 1])
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(TranslationMatrix(center) * Global.matrix)
class Hook(Item):
def __init__(self, name, center, length, up_angle, dn_angle):
mesh = Blender.Mesh.New()
up = ORIGIN - length * math.cos(up_angle * DEG2RAD) * X - length * math.sin(up_angle * DEG2RAD) * Z
dn = ORIGIN - length * math.cos(dn_angle * DEG2RAD) * X - length * math.sin(dn_angle * DEG2RAD) * Z
mesh.verts.extend([ORIGIN, dn, dn + 0.05 * Y, dn - 0.05 * Y])
mesh.edges.extend([[0, 1], [2, 3]])
draw_dashed_line(mesh, ORIGIN, up)
draw_dashed_line(mesh, ORIGIN, dn)
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(TranslationMatrix(center) * Global.matrix)
class Launchbar(Item):
def __init__(self, name, lb, lb_length, hb, hb_length, up_angle, dn_angle):
mesh = Blender.Mesh.New()
hb = hb - lb
lb_tip = ORIGIN + lb_length * math.cos(dn_angle * DEG2RAD) * X - lb_length * math.sin(dn_angle * DEG2RAD) * Z
hb_tip = hb - hb_length * math.cos(dn_angle * DEG2RAD) * X - hb_length * math.sin(dn_angle * DEG2RAD) * Z
mesh.verts.extend([lb_tip, ORIGIN, hb, hb_tip, lb_tip + 0.05 * Y, lb_tip - 0.05 * Y, hb_tip + 0.05 * Y, hb_tip - 0.05 * Y])
mesh.edges.extend([[0, 1], [1, 2], [2, 3], [4, 5], [6, 7]])
draw_dashed_line(mesh, ORIGIN, lb_length * math.cos(up_angle * DEG2RAD) * X - lb_length * math.sin(up_angle * DEG2RAD) * Z)
draw_dashed_line(mesh, hb, hb - hb_length * math.cos(up_angle * DEG2RAD) * X - hb_length * math.sin(up_angle * DEG2RAD) * Z)
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(TranslationMatrix(lb) * Global.matrix)
class Hitch(Item):
def __init__(self, name, center):
mesh = Blender.Mesh.Primitives.Circle(6, 0.1)
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(RotationMatrix(90, 4, "x") * TranslationMatrix(center) * Global.matrix)
class Thrust:
def set_actionpt(self, p):
self.actionpt = p
def set_dir(self, d):
self.thrustvector = d
class Thruster(Thrust, Item):
def __init__(self, name, center, thrustvector):
(self.name, self.center, self.actionpt, self.thrustvector) = (name, center, center, thrustvector)
def __del__(self):
a = self.actionpt - self.center
mesh = Blender.Mesh.New()
draw_dashed_line(mesh, ORIGIN, a)
draw_arrow(mesh, a, a + self.thrustvector.normalize())
obj = self.scene.objects.new(mesh, self.name)
obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
class Propeller(Thrust, Item):
def __init__(self, name, center, radius):
(self.name, self.center, self.radius, self.actionpt, self.thrustvector) = (name, center, radius, center, -X)
def __del__(self):
a = self.actionpt - self.center
matrix = self.thrustvector.toTrackQuat('z', 'x').toMatrix().resize4x4() * TranslationMatrix(a)
mesh = Blender.Mesh.New()
mesh.verts.extend([ORIGIN * matrix, (ORIGIN + self.radius * X) * matrix])
mesh.edges.extend([[0, 1]])
draw_dashed_line(mesh, ORIGIN, a)
draw_arrow(mesh, a, a + self.thrustvector.normalize())
draw_circle(mesh, 128, self.radius, matrix)
obj = self.scene.objects.new(mesh, self.name)
obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
class Jet(Thrust, Item):
def __init__(self, name, center, rotate):
(self.name, self.center, self.actionpt) = (name, center, center)
self.thrustvector = -X * RotationMatrix(rotate, 4, "y")
def __del__(self):
a = self.actionpt - self.center
mesh = Blender.Mesh.New()
draw_dashed_line(mesh, ORIGIN, a)
draw_arrow(mesh, a, a + self.thrustvector.normalize())
obj = self.scene.objects.new(mesh, self.name)
obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
class Fuselage(Item):
def __init__(self, name, a, b, width, taper, midpoint):
numvert = 12
angle = []
for i in range(numvert):
alpha = i * 2 * math.pi / float(numvert)
angle.append([math.cos(alpha), math.sin(alpha)])
axis = b - a
length = axis.length
mesh = Blender.Mesh.New()
for i in range(numvert):
mesh.verts.extend([[0, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
for i in range(numvert):
mesh.verts.extend([[midpoint * length, 0.5 * width * angle[i][0], 0.5 * width * angle[i][1]]])
for i in range(numvert):
mesh.verts.extend([[length, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
for i in range(numvert):
i1 = (i + 1) % numvert
mesh.faces.extend([[i, i1, i1 + numvert, i + numvert]])
mesh.faces.extend([[i + numvert, i1 + numvert, i1 + 2 * numvert, i + 2 * numvert]])
mesh.verts.extend([ORIGIN, length * X])
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(axis.toTrackQuat('x', 'y').toMatrix().resize4x4() * TranslationMatrix(a) * Global.matrix)
self.set_color(obj, [0, 0, 0.5, 0.4])
class Rotor(Item):
def __init__(self, name, center, up, fwd, numblades, radius, chord, twist, taper, rel_len_blade_start, phi0, ccw):
matrix = RotationMatrix(phi0, 4, "z") * up.toTrackQuat('z', 'x').toMatrix().resize4x4()
invert = matrix.copy().invert()
direction = [-1, 1][ccw]
twist *= DEG2RAD
a = ORIGIN + rel_len_blade_start * radius * X
b = ORIGIN + radius * X
tw = 0.5 * chord * taper * math.cos(twist) * Y + 0.5 * direction * chord * taper * math.sin(twist) * Z
mesh = Blender.Mesh.New()
mesh.verts.extend([ORIGIN, a, b, a + 0.5 * chord * Y, a - 0.5 * chord * Y, b + tw, b - tw])
mesh.edges.extend([[0, 1], [1, 2], [1, 3], [1, 4], [3, 5], [4, 6], [5, 6]])
draw_circle(mesh, 64, rel_len_blade_start * radius, Matrix())
draw_circle(mesh, 128, radius, Matrix())
draw_arrow(mesh, ORIGIN, up * invert)
draw_arrow(mesh, ORIGIN, fwd * invert)
b += 0.1 * X + direction * chord * Y
draw_arrow(mesh, b, b + min(0.5 * radius, 1) * direction * Y)
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(matrix * TranslationMatrix(center) * Global.matrix)
class Wing(Item):
def __init__(self, name, root, length, chord, incidence, twist, taper, sweep, dihedral):
# <1--0--2
# \ | /
# 4-3-5
self.is_symmetric = not name.startswith("YASim_vstab#")
mesh = Blender.Mesh.New()
mesh.verts.extend([ORIGIN, ORIGIN + 0.5 * chord * X, ORIGIN - 0.5 * chord * X])
tip = ORIGIN + math.cos(sweep * DEG2RAD) * length * Y - math.sin(sweep * DEG2RAD) * length * X
tipfore = tip + 0.5 * taper * chord * math.cos(twist * DEG2RAD) * X + 0.5 * taper * chord * math.sin(twist * DEG2RAD) * Z
tipaft = tip + tip - tipfore
mesh.verts.extend([tip, tipfore, tipaft])
mesh.faces.extend([[0, 1, 4, 3], [2, 0, 3, 5]])
self.make_twosided(mesh)
obj = self.scene.objects.new(mesh, name)
mesh.transform(Euler(dihedral, -incidence, 0).toMatrix().resize4x4())
self.set_color(obj, [[0.5, 0.0, 0, 0.5], [0.0, 0.5, 0, 0.5]][self.is_symmetric])
(self.obj, self.mesh) = (obj, mesh)
if self.is_symmetric and Global.mirror_button.val:
mod = obj.modifiers.append(Blender.Modifier.Type.MIRROR)
mod[Blender.Modifier.Settings.AXIS_X] = False
mod[Blender.Modifier.Settings.AXIS_Y] = True
mod[Blender.Modifier.Settings.AXIS_Z] = False
mesh.transform(TranslationMatrix(root)) # must move object center to x axis
obj.setMatrix(Global.matrix)
else:
obj.setMatrix(TranslationMatrix(root) * Global.matrix)
def add_flap(self, name, start, end):
a = Vector(self.mesh.verts[2].co)
b = Vector(self.mesh.verts[5].co)
c = 0.2 * (Vector(self.mesh.verts[0].co - a)).normalize()
m = self.obj.getMatrix()
mesh = Blender.Mesh.New()
i0 = a + start * (b - a)
i1 = a + end * (b - a)
mesh.verts.extend([i0, i1, i0 + c, i1 + c])
mesh.faces.extend([[0, 1, 3, 2]])
self.make_twosided(mesh)
obj = self.scene.objects.new(mesh, name)
obj.setMatrix(m)
self.set_color(obj, [0.8, 0.8, 0, 0.9])
if self.is_symmetric and Global.mirror_button.val:
mod = obj.modifiers.append(Blender.Modifier.Type.MIRROR)
mod[Blender.Modifier.Settings.AXIS_X] = False
mod[Blender.Modifier.Settings.AXIS_Y] = True
mod[Blender.Modifier.Settings.AXIS_Z] = False
class import_yasim(handler.ErrorHandler, handler.ContentHandler):
ignored = ["cruise", "approach", "control-input", "control-output", "control-speed", \
"control-setting", "stall", "airplane", "piston-engine", "turbine-engine", \
"rotorgear", "tow", "winch", "solve-weight"]
# err_handler
def warning(self, exception):
print((self.error_string("Warning", exception)))
def error(self, exception):
print((self.error_string("Error", exception)))
def fatalError(self, exception):
raise Abort(str(exception), self.error_string("Fatal", exception))
def error_string(self, tag, e):
(column, line) = (e.getColumnNumber(), e.getLineNumber())
return "%s: %s\n%s%s^" % (tag, str(e), Global.data[line - 1], column * ' ')
# doc_handler
def setDocumentLocator(self, locator):
self.locator = locator
def startDocument(self):
self.tags = []
self.counter = {}
self.items = [None]
def endDocument(self):
for o in Item.scene.objects:
o.sel = True
def startElement(self, tag, attrs):
if len(self.tags) == 0 and tag != "airplane":
raise Abort("this isn't a YASim config file (bad root tag at line %d)" % self.locator.getLineNumber())
self.tags.append(tag)
path = string.join(self.tags, '/')
item = Item()
parent = self.items[-1]
if self.counter.has_key(tag):
self.counter[tag] += 1
else:
self.counter[tag] = 0
if tag == "cockpit":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\033[31mcockpit x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
item = Cockpit(c)
elif tag == "fuselage":
a = Vector(float(attrs["ax"]), float(attrs["ay"]), float(attrs["az"]))
b = Vector(float(attrs["bx"]), float(attrs["by"]), float(attrs["bz"]))
width = float(attrs["width"])
taper = float(attrs.get("taper", 1))
midpoint = float(attrs.get("midpoint", 0.5))
log("\033[32mfuselage ax=%f ay=%f az=%f bx=%f by=%f bz=%f width=%f taper=%f midpoint=%f\033[m" % \
(a[0], a[1], a[2], b[0], b[1], b[2], width, taper, midpoint))
item = Fuselage("YASim_%s#%d" % (tag, self.counter[tag]), a, b, width, taper, midpoint)
elif tag == "gear":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
compression = float(attrs.get("compression", 1))
up = Z * compression
if attrs.has_key("upx"):
up = Vector(float(attrs["upx"]), float(attrs["upy"]), float(attrs["upz"])).normalize() * compression
log("\033[35;1mgear x=%f y=%f z=%f compression=%f upx=%f upy=%f upz=%f\033[m" \
% (c[0], c[1], c[2], compression, up[0], up[1], up[2]))
item = Gear("YASim_gear#%d" % self.counter[tag], c, up)
elif tag == "jet":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
rotate = float(attrs.get("rotate", 0))
log("\033[36;1mjet x=%f y=%f z=%f rotate=%f\033[m" % (c[0], c[1], c[2], rotate))
item = Jet("YASim_jet#%d" % self.counter[tag], c, rotate)
elif tag == "propeller":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
radius = float(attrs["radius"])
log("\033[36;1m%s x=%f y=%f z=%f radius=%f\033[m" % (tag, c[0], c[1], c[2], radius))
item = Propeller("YASim_propeller#%d" % self.counter[tag], c, radius)
elif tag == "thruster":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
v = Vector(float(attrs["vx"]), float(attrs["vy"]), float(attrs["vz"]))
log("\033[36;1m%s x=%f y=%f z=%f vx=%f vy=%f vz=%f\033[m" % (tag, c[0], c[1], c[2], v[0], v[1], v[2]))
item = Thruster("YASim_thruster#%d" % self.counter[tag], c, v)
elif tag == "actionpt":
if not isinstance(parent, Thrust):
raise Abort("%s is not part of a thruster/propeller/jet at line %d" \
% (path, self.locator.getLineNumber()))
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\t\033[36mactionpt x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
parent.set_actionpt(c)
elif tag == "dir":
if not isinstance(parent, Thrust):
raise Abort("%s is not part of a thruster/propeller/jet at line %d" \
% (path, self.locator.getLineNumber()))
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\t\033[36mdir x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
parent.set_dir(c)
elif tag == "tank":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\033[34;1m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
item = Tank("YASim_tank#%d" % self.counter[tag], c)
elif tag == "ballast":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
item = Ballast("YASim_ballast#%d" % self.counter[tag], c)
elif tag == "weight":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
item = Weight("YASim_weight#%d" % self.counter[tag], c)
elif tag == "hook":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
length = float(attrs.get("length", 1))
up_angle = float(attrs.get("up-angle", 0))
down_angle = float(attrs.get("down-angle", 70))
log("\033[35m%s x=%f y=%f z=%f length=%f up-angle=%f down-angle=%f\033[m" \
% (tag, c[0], c[1], c[2], length, up_angle, down_angle))
item = Hook("YASim_hook#%d" % self.counter[tag], c, length, up_angle, down_angle)
elif tag == "hitch":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
log("\033[35m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
item = Hitch("YASim_hitch#%d" % self.counter[tag], c)
elif tag == "launchbar":
c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
length = float(attrs.get("length", 1))
up_angle = float(attrs.get("up-angle", -45))
down_angle = float(attrs.get("down-angle", 45))
holdback = Vector(float(attrs.get("holdback-x", c[0])), float(attrs.get("holdback-y", c[1])), float(attrs.get("holdback-z", c[2])))
holdback_length = float(attrs.get("holdback-length", 2))
log("\033[35m%s x=%f y=%f z=%f length=%f down-angle=%f up-angle=%f holdback-x=%f holdback-y=%f holdback-z+%f holdback-length=%f\033[m" \
% (tag, c[0], c[1], c[2], length, down_angle, up_angle, \
holdback[0], holdback[1], holdback[2], holdback_length))
item = Launchbar("YASim_launchbar#%d" % self.counter[tag], c, length, holdback, holdback_length, up_angle, down_angle)
elif tag == "wing" or tag == "hstab" or tag == "vstab" or tag == "mstab":
root = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
length = float(attrs["length"])
chord = float(attrs["chord"])
incidence = float(attrs.get("incidence", 0))
twist = float(attrs.get("twist", 0))
taper = float(attrs.get("taper", 1))
sweep = float(attrs.get("sweep", 0))
dihedral = float(attrs.get("dihedral", [0, 90][tag == "vstab"]))
log("\033[33;1m%s x=%f y=%f z=%f length=%f chord=%f incidence=%f twist=%f taper=%f sweep=%f dihedral=%f\033[m" \
% (tag, root[0], root[1], root[2], length, chord, incidence, twist, taper, sweep, dihedral))
item = Wing("YASim_%s#%d" % (tag, self.counter[tag]), root, length, chord, incidence, twist, taper, sweep, dihedral)
elif tag == "flap0" or tag == "flap1" or tag == "slat" or tag == "spoiler":
if not isinstance(parent, Wing):
raise Abort("%s is not part of a wing or stab at line %d" \
% (path, self.locator.getLineNumber()))
start = float(attrs["start"])
end = float(attrs["end"])
log("\t\033[33m%s start=%f end=%f\033[m" % (tag, start, end))
parent.add_flap("YASim_%s#%d" % (tag, self.counter[tag]), start, end)
elif tag == "rotor":
c = Vector(float(attrs.get("x", 0)), float(attrs.get("y", 0)), float(attrs.get("z", 0)))
norm = Vector(float(attrs.get("nx", 0)), float(attrs.get("ny", 0)), float(attrs.get("nz", 1)))
fwd = Vector(float(attrs.get("fx", 1)), float(attrs.get("fy", 0)), float(attrs.get("fz", 0)))
diameter = float(attrs.get("diameter", 10.2))
numblades = int(attrs.get("numblades", 4))
chord = float(attrs.get("chord", 0.3))
twist = float(attrs.get("twist", 0))
taper = float(attrs.get("taper", 1))
rel_len_blade_start = float(attrs.get("rel-len-blade-start", 0))
phi0 = float(attrs.get("phi0", 0))
ccw = not not int(attrs.get("ccw", 0))
log(("\033[36;1mrotor x=%f y=%f z=%f nx=%f ny=%f nz=%f fx=%f fy=%f fz=%f numblades=%d diameter=%f " \
+ "chord=%f twist=%f taper=%f rel_len_blade_start=%f phi0=%f ccw=%d\033[m") \
% (c[0], c[1], c[2], norm[0], norm[1], norm[2], fwd[0], fwd[1], fwd[2], numblades, \
diameter, chord, twist, taper, rel_len_blade_start, phi0, ccw))
item = Rotor("YASim_rotor#%d" % self.counter[tag], c, norm, fwd, numblades, 0.5 * diameter, chord, \
twist, taper, rel_len_blade_start, phi0, ccw)
elif tag not in self.ignored:
log("\033[30;1m%s\033[m" % path)
self.items.append(item)
def endElement(self, tag):
self.tags.pop()
self.items.pop()
def extract_matrix(filedata, tag):
v = { 'x': 0.0, 'y': 0.0, 'z': 0.0, 'h': 0.0, 'p': 0.0, 'r': 0.0 }
has_offsets = False
for line in filedata:
line = string.strip(line)
if not line.startswith("<!--") or not line.endswith("-->"):
continue
line = string.strip(line[4:-3])
if not string.lower(line).startswith("%s:" % tag):
continue
line = string.strip(line[len(tag) + 1:])
for assignment in string.split(line):
(key, value) = string.split(assignment, '=', 2)
v[string.strip(key)] = float(string.strip(value))
has_offsets = True
if not has_offsets:
return None
print(("using offsets: x=%f y=%f z=%f h=%f p=%f r=%f" % (v['x'], v['y'], v['z'], v['h'], v['p'], v['r'])))
return Euler(v['r'], v['p'], v['h']).toMatrix().resize4x4() * TranslationMatrix(Vector(v['x'], v['y'], v['z']))
def load_yasim_config(path):
if BPyMessages.Error_NoFile(path):
return
Blender.Window.WaitCursor(1)
Blender.Window.EditMode(0)
print(("loading '%s'" % path))
try:
for o in Item.scene.objects:
if o.name.startswith("YASim_"):
Item.scene.objects.unlink(o)
try:
f = open(path)
Global.data = f.readlines()
finally:
f.close()
Global.path = path
Global.matrix = YASIM_MATRIX
matrix = extract_matrix(Global.data, "offsets")
if matrix:
Global.matrix *= matrix.invert()
Global.yasim.parse(path)
Blender.Registry.SetKey("FGYASimImportExport", { "path": path }, False)
Global.data = None
except Abort, e:
print(("%s\nAborting ..." % (e.term or e.msg)))
Blender.Draw.PupMenu("Error%t|" + e.msg)
Blender.Window.RedrawAll()
Blender.Window.WaitCursor(0)
def gui_draw():
from Blender import BGL, Draw
(width, height) = Blender.Window.GetAreaSize()
BGL.glClearColor(0.4, 0.4, 0.45, 1)
BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
BGL.glColor3f(1, 1, 1)
BGL.glRasterPos2f(5, 55)
Draw.Text("FlightGear YASim Import: '%s'" % Global.path)
Draw.PushButton("Reload", RELOAD_BUTTON, 5, 5, 80, 32, "reload YASim config file")
Global.mirror_button = Draw.Toggle("Mirror", MIRROR_BUTTON, 100, 5, 50, 16, Global.mirror_button.val, \
"show symmetric surfaces on both sides (reloads config)")
Draw.PushButton("Update Cursor", CURSOR_BUTTON, width - 650, 5, 100, 32, "update cursor display (in YASim coordinate system)")
BGL.glRasterPos2f(width - 530 + Blender.Draw.GetStringWidth("Vector from last") - Blender.Draw.GetStringWidth("Current"), 24)
Draw.Text("Current cursor pos: x = %+.3f y = %+.3f z = %+.3f" % tuple(Global.cursor))
c = Global.cursor - Global.last_cursor
BGL.glRasterPos2f(width - 530, 7)
Draw.Text("Vector from last cursor pos: x = %+.3f y = %+.3f z = %+.3f length = %.3f m" % (c[0], c[1], c[2], c.length))
def gui_event(ev, value):
if ev == Blender.Draw.ESCKEY:
Blender.Draw.Exit()
def gui_button(n):
if n == NO_EVENT:
return
elif n == RELOAD_BUTTON:
load_yasim_config(Global.path)
elif n == CURSOR_BUTTON:
Global.last_cursor = Global.cursor
Global.cursor = Vector(Blender.Window.GetCursorPos()) * Global.matrix.invert()
d = Global.cursor - Global.last_cursor
print(("cursor: x=\"%f\" y=\"%f\" z=\"%f\" dx=%f dy=%f dz=%f length=%f" \
% (Global.cursor[0], Global.cursor[1], Global.cursor[2], d[0], d[1], d[2], d.length)))
elif n == MIRROR_BUTTON:
load_yasim_config(Global.path)
Blender.Draw.Redraw(1)
def main():
log(6 * "\n")
registry = Blender.Registry.GetKey("FGYASimImportExport", False)
if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])):
path = registry["path"]
else:
path = ""
xml_handler = import_yasim()
Global.yasim = make_parser()
Global.yasim.setContentHandler(xml_handler)
Global.yasim.setErrorHandler(xml_handler)
if Blender.Window.GetScreenInfo(Blender.Window.Types.SCRIPT):
Blender.Draw.Register(gui_draw, gui_event, gui_button)
Blender.Window.FileSelector(load_yasim_config, "Import YASim Configuration File", path)
main()

View File

@@ -0,0 +1,6 @@
#add_executable(terrasync terrasync.cxx)
#target_link_libraries(terrasync SimGearCore)
#install(TARGETS terrasync RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

View File

@@ -0,0 +1,65 @@
TerraSync
=========
Usage:
terrasync -p <port> [ -R ] [ -s <rsync_source> ] -d <dest>
terrasync -p <port> -S [ -s <svn_source> ] -d <dest>
Example:
$ fgfs --atlas=socket,out,1,localhost,5500,udp --fg-scenery=/usr/local/FlightGear/data/scenery:/data1/Scenery-0.9.2
$ nice terrasync -p 5500 -d /data1/Scenery-0.9.2
Requirements:
- rsync util installed in your path.
or - svn util installed in your path
NOTE!!!! Do not run terrasync against the default
$FG_ROOT/data/Scenery/ directory. I recommend creating a separate
scenery directory and specifying both in your ~/.fgfsrc file or via
the command line.
TerraSync is a utility that is intended to run as a light weight
background process. It's purpose is to monitor the position of a
FlightGear flight and pre-fetch scenery tiles from a remote server
based on the current FlightGear position.
This allows you to do a base install of FlightGear with no add on
scenery. Now just go and fly anywhere. Scenery is fetched "just in
time" and accumulated on your HD so it is already there next time you
fly. You can fly anywhere and essentially just the scenery you need
is auto-installed as you fly.
Terrasync runs as a separate process and expects the --atlas=port
format to be sent from fgfs. The fgfs output tells the terrasync util
where FlightGear is currently flying. Terrasync will then issue the
appropriate commands to rsync the surrounding areas to your local
scenery directory. The user need to choose a port for
FlightGear->TerraSync communication and then specify the server
location and destation scenery tree.
As you fly, terrasync will periodically refresh and pull any new
scenery tiles that you need for your position from the server.
This also works if the scenery on the scenery server is updated.
Rsync ( or svn ) will pull any missing files, or any newly updated files.
There is a chicken/egg problem when you first start up in a brand new
area. FlightGear is expecting the scenery to be there *now* but it
may not have been fetched yet. I suppose without making a more
complex protocol, the user will need to be aware of this. For now I
suggest exiting FlightGear after terrasync gets caught up, and
restarting FlightGear. The second time FlightGear starts up
everything should be good.
Final notes:
I have set up the scenery server at
scenery.flightgear.org::Scenery-0.9.2. This is the latest 0.9.2 build
of the world.
Note to self:
nice ./terrasync -p 5500 -s baron.flightgear.org:/stage/fgfs05/curt/Scenery-1.0 -d /stage/catalina3/Scenery-1.0/

View File

@@ -0,0 +1,515 @@
// terrasync.cxx -- "JIT" scenery fetcher
//
// Written by Curtis Olson, started November 2002.
//
// Copyright (C) 2002 Curtis L. Olson - http://www.flightgear.org/~curt
// Copyright (C) 2008 Alexander R. Perry <alex.perry@ieee.org>
// Copyright (C) 2011 Thorsten Brehm <brehmt@gmail.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <simgear/simgear_config.h>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#ifdef HAVE_UNISTD_H
#include "unistd.h"
#endif
#ifdef __MINGW32__
# include <time.h>
#elif defined(_MSC_VER)
# include <io.h>
#endif
#include <stdlib.h> // atoi() atof() abs() system()
#include <signal.h> // signal()
#include <simgear/compiler.h>
#include <iostream>
#include <fstream>
#include <string>
#include <simgear/io/raw_socket.hxx>
#include <simgear/scene/tsync/terrasync.hxx>
#if defined(_MSC_VER) || defined(__MINGW32__)
typedef void (__cdecl * sighandler_t)(int);
#elif defined( __APPLE__ ) || defined (__FreeBSD__)
typedef sig_t sighandler_t;
#endif
int termination_triggering_signals[] = {
#if defined(_MSC_VER) || defined(__MINGW32__)
SIGINT, SIGILL, SIGFPE, SIGSEGV, SIGTERM, SIGBREAK, SIGABRT,
#else
SIGHUP, SIGINT, SIGQUIT, SIGKILL, SIGTERM,
#endif
0}; // zero terminated
using namespace std;
const char* svn_base =
"http://terrascenery.googlecode.com/svn/trunk/data/Scenery";
const char* rsync_base = "scenery.flightgear.org::Scenery";
bool terminating = false;
sighandler_t prior_signal_handlers[32];
simgear::Socket theSocket;
simgear::SGTerraSync* pTerraSync = NULL;
/** display usage information */
static void
usage( const string& prog ) {
cout << "Usage: terrasync [options]\n"
"Options:\n"
" -d <dest> destination directory [required]\n"
" -R transport using pipe to rsync\n"
" -S transport using built-in svn\n"
" -e transport using external svn client\n"
" -p <port> listen on UDP port [default: 5501]\n"
" -s <source> source base [default: '']\n"
#ifndef _MSC_VER
" -pid <pidfile> write PID to file\n"
#endif
" -v be more verbose\n";
#ifndef _MSC_VER
cout << "\n"
"Example:\n"
" pid=$(cat $pidfile 2>/dev/null)\n"
" if test -n \"$pid\" && kill -0 $pid ; then\n"
" echo \"terrasync already running: $pid\"\n"
" else\n"
" nice /games/sport/fgs/utils/TerraSync/terrasync \\\n"
" -v -pid $pidfile -S -p 5500 -d /games/orig/terrasync &\n"
" fi\n";
#endif
}
/** Signal handler for termination requests (Ctrl-C) */
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;
if (write(1, msg, sizeof(msg) - 1) == -1)
{
// need to act write's return value to avoid GCC compiler warning
// "'write' declared with attribute warn_unused_result"
terminating = true; // dummy operation
}
terminating = true;
signal(param, prior_signal_handlers[param]);
theSocket.close();
if (pTerraSync)
pTerraSync->unbind();
}
/** Parse command-line options. */
void
parseOptions(int argc, char** argv, SGPropertyNode_ptr config,
bool& testing, int& verbose, int& port, const char* &pidfn)
{
// parse arguments
for (int i=1; i < argc; ++i )
{
string arg = argv[i];
if ( arg == "-p" ) {
++i;
port = atoi( argv[i] );
} else if ( arg.find("-pid") == 0 ) {
++i;
pidfn = argv[i];
cout << "pidfn: " << pidfn << endl;
} else if ( arg == "-s" ) {
++i;
config->setStringValue("svn-server", argv[i]);
} else if ( arg == "-d" ) {
++i;
config->setStringValue("scenery-dir", argv[i]);
} else if ( arg == "-R" ) {
config->setBoolValue("use-svn", false);
} else if ( arg == "-S" ) {
config->setBoolValue("use-built-in-svn", true);
} else if ( arg == "-e" ) {
config->setBoolValue("use-built-in-svn", false);
} else if ( arg == "-v" ) {
verbose++;
} else if ( arg == "-T" ) {
testing = true;
} else if ( arg == "-h" ) {
usage( argv[0] );
exit(0);
} else {
cerr << "Unrecognized command-line option '" << arg << "'" << endl;
usage( argv[0] );
exit(-1);
}
}
}
/** parse a single NMEA-0183 position message */
static void
parse_message( int verbose, const string &msg, int *lat, int *lon )
{
double dlat, dlon;
string text = msg;
if (verbose>=3)
cout << "message: '" << text << "'" << endl;
// find GGA string and advance to start of lat (see NMEA-0183 for protocol specs)
string::size_type pos = text.find( "$GPGGA" );
if ( pos == string::npos )
{
*lat = simgear::NOWHERE;
*lon = simgear::NOWHERE;
return;
}
string tmp = text.substr( pos + 7 );
pos = tmp.find( "," );
tmp = tmp.substr( pos + 1 );
// cout << "-> " << tmp << endl;
// find lat then advance to start of hemisphere
pos = tmp.find( "," );
string lats = tmp.substr( 0, pos );
dlat = atof( lats.c_str() ) / 100.0;
tmp = tmp.substr( pos + 1 );
// find N/S hemisphere and advance to start of lon
if ( tmp.substr( 0, 1 ) == "S" ) {
dlat = -dlat;
}
pos = tmp.find( "," );
tmp = tmp.substr( pos + 1 );
// find lon
pos = tmp.find( "," );
string lons = tmp.substr( 0, pos );
dlon = atof( lons.c_str() ) / 100.0;
tmp = tmp.substr( pos + 1 );
// find E/W hemisphere and advance to start of lon
if ( tmp.substr( 0, 1 ) == "W" ) {
dlon = -dlon;
}
if ( dlat < 0 ) {
*lat = (int)dlat - 1;
} else {
*lat = (int)dlat;
}
if ( dlon < 0 ) {
*lon = (int)dlon - 1;
} else {
*lon = (int)dlon;
}
if ((dlon == 0) && (dlat == 0)) {
*lon = simgear::NOWHERE;
*lat = simgear::NOWHERE;
}
}
void
syncArea( int lat, int lon )
{
if ( lat < -90 || lat > 90 || lon < -180 || lon > 180 )
return;
char NS, EW;
int baselat, baselon;
if ( lat < 0 ) {
int base = (int)(lat / 10);
if ( lat == base * 10 ) {
baselat = base * 10;
} else {
baselat = (base - 1) * 10;
}
NS = 's';
} else {
baselat = (int)(lat / 10) * 10;
NS = 'n';
}
if ( lon < 0 ) {
int base = (int)(lon / 10);
if ( lon == base * 10 ) {
baselon = base * 10;
} else {
baselon = (base - 1) * 10;
}
EW = 'w';
} else {
baselon = (int)(lon / 10) * 10;
EW = 'e';
}
ostringstream dir;
dir << setfill('0')
<< EW << setw(3) << abs(baselon) << NS << setw(2) << abs(baselat) << "/"
<< EW << setw(3) << abs(lon) << NS << setw(2) << abs(lat);
pTerraSync->syncAreaByPath(dir.str());
}
void
syncAreas( int lat, int lon, int lat_dir, int lon_dir )
{
if ( lat_dir == 0 && lon_dir == 0 ) {
// do surrounding 8 1x1 degree areas.
for ( int i = lat - 1; i <= lat + 1; ++i ) {
for ( int j = lon - 1; j <= lon + 1; ++j ) {
if ( i != lat || j != lon ) {
syncArea( i, j );
}
}
}
} else {
if ( lat_dir != 0 ) {
syncArea( lat + lat_dir, lon - 1 );
syncArea( lat + lat_dir, lon + 1 );
syncArea( lat + lat_dir, lon );
}
if ( lon_dir != 0 ) {
syncArea( lat - 1, lon + lon_dir );
syncArea( lat + 1, lon + lon_dir );
syncArea( lat, lon + lon_dir );
}
}
// do current 1x1 degree area first
syncArea( lat, lon );
}
bool
schedulePosition(int lat, int lon)
{
bool Ok = false;
if ((lat == simgear::NOWHERE) || (lon == simgear::NOWHERE)) {
return Ok;
}
static int last_lat = simgear::NOWHERE;
static int last_lon = simgear::NOWHERE;
if ((lat == last_lat) && (lon == last_lon)) {
Ok = true;
return Ok;
}
int lat_dir=0;
int lon_dir=0;
if ( last_lat != simgear::NOWHERE && last_lon != simgear::NOWHERE )
{
int dist = lat - last_lat;
if ( dist != 0 )
{
lat_dir = dist / abs(dist);
}
else
{
lat_dir = 0;
}
dist = lon - last_lon;
if ( dist != 0 )
{
lon_dir = dist / abs(dist);
} else
{
lon_dir = 0;
}
}
cout << "Scenery update for " <<
"lat = " << lat << ", lon = " << lon <<
", lat_dir = " << lat_dir << ", " <<
"lon_dir = " << lon_dir << endl;
syncAreas( lat, lon, lat_dir, lon_dir );
Ok = true;
last_lat = lat;
last_lon = lon;
return Ok;
}
/** Monitor UDP socket and process NMEA position updates. */
int
processRequests(SGPropertyNode_ptr config, bool testing, int verbose, int port)
{
const char* host = "localhost";
char msg[256];
int len;
int lat, lon;
bool connected = false;
// Must call this before any other net stuff
simgear::Socket::initSockets();
// open UDP socket
if ( !theSocket.open( false ) )
{
cerr << "error opening socket" << endl;
return -1;
}
if ( theSocket.bind( host, port ) == -1 )
{
cerr << "error binding to port " << port << endl;
return -1;
}
theSocket.setBlocking(true);
while ( (!terminating)&&
(!config->getBoolValue("stalled", false)) )
{
if (verbose >= 4)
cout << "main loop" << endl;
// main loop
bool recv_msg = false;
if ( testing )
{
// Testing without FGFS communication
lat = 37;
lon = -123;
recv_msg = true;
} else
{
if (verbose && pTerraSync->isIdle())
{
cout << "Idle; waiting for FlightGear position data" << endl;
}
len = theSocket.recv(msg, sizeof(msg)-1, 0);
if (len >= 0)
{
msg[len] = '\0';
recv_msg = true;
if (verbose>=2)
cout << "recv length: " << len << endl;
parse_message( verbose, msg, &lat, &lon );
if ((!connected)&&
(lat != simgear::NOWHERE)&&
(lon != simgear::NOWHERE))
{
cout << "Valid position data received. Connected successfully." << endl;
connected = true;
}
}
}
if ( recv_msg )
{
schedulePosition(lat, lon);
}
if ( testing )
{
if (pTerraSync->isIdle())
terminating = true;
else
SGTimeStamp::sleepForMSec(1000);
}
} // while !terminating
return 0;
}
int main( int argc, char **argv )
{
int port = 5501;
int verbose = 0;
int exit_code = 0;
bool testing = false;
const char* pidfn = "";
// default configuration
sglog().setLogLevels( SG_ALL, SG_ALERT);
SGPropertyNode_ptr root = new SGPropertyNode();
SGPropertyNode_ptr config = root->getNode("/sim/terrasync", true);
config->setStringValue("scenery-dir", "terrasyncdir");
config->setStringValue("svn-server", svn_base);
config->setStringValue("rsync-server", rsync_base);
config->setBoolValue("use-built-in-svn", true);
config->setBoolValue("use-svn", true);
config->setBoolValue("enabled", true);
config->setIntValue("max-errors", -1); // -1 = infinite
// parse command-line arguments
parseOptions(argc, argv, config, testing, verbose, port, pidfn);
if (verbose)
sglog().setLogLevels( SG_ALL, SG_INFO);
#ifndef _MSC_VER
// create PID file
if (*pidfn)
{
ofstream pidstream;
pidstream.open(pidfn);
if (!pidstream.good())
{
cerr << "Cannot open pid file '" << pidfn << "': ";
perror(0);
exit(2);
}
pidstream << getpid() << endl;
pidstream.close();
}
#endif
// install signal handlers
for (int* sigp=termination_triggering_signals; *sigp; sigp++)
{
prior_signal_handlers[*sigp] =
signal(*sigp, *terminate_request_handler);
if (verbose>=2)
cout << "Intercepting signal " << *sigp << endl;
}
{
pTerraSync = new simgear::SGTerraSync;
pTerraSync->setRoot(root);
pTerraSync->bind();
pTerraSync->init();
// now monitor and process position updates
exit_code = processRequests(config, testing, verbose, port);
pTerraSync->unbind();
delete pTerraSync;
pTerraSync = NULL;
}
return exit_code;
}

21
utils/climate/README Normal file
View File

@@ -0,0 +1,21 @@
-- Data Source:
http://koeppen-geiger.vu-wien.ac.at/present.htm
Download "code with raster file: Map_KG-Global.R"
-- Bulding:
g++ asc2a.cpp texture.cxx -o asc2a -lGL -lz -losg
-- Workflow:
1. Convert the ESRI grid to ASCII using R:
https://groups.google.com/forum/#!topic/maxent/OrTBUvqOXaE
2. Run asc2a to convert the ASCII file to a single channel rgb file:
./asc2a <infile>.asc <outfile>.rgb
3. Use your favorite image processing package to convert from rgb to png
to compress it 40x compared to the ESRI grid image and 110x compared to
the ASCII file.

77
utils/climate/asc2a.cpp Normal file
View File

@@ -0,0 +1,77 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <climits>
#include <string>
#include "texture.hxx"
#define NCOLS 4320
#define NROWS 2160
enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };
STR2INT_ERROR str2int (int &i, char const *s, int base = 0)
{
char *end;
long l;
errno = 0;
l = strtol(s, &end, base);
if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
std::cerr << "OVERFLOW" << std::endl;
return OVERFLOW;
}
if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
std::cerr << "UNDERFLOW" << std::endl;
return UNDERFLOW;
}
if (*s == '\0' || end == s) {
std::cerr << "INCONVERTIBLE: " << s << std::endl;
return INCONVERTIBLE;
}
i = l;
return SUCCESS;
}
int main(int argc, char **argv)
{
if (argc < 3) {
printf("Uage: %s <infile>.asc <outfile>.rgb\n\n", argv[0]);
exit(-1);
}
const char *infile = argv[1];
const char *outfile = argv[2];
SGTexture texture(NCOLS, NROWS);
std::ifstream file(infile);
std::string str;
texture.prepare(NCOLS, NROWS);
texture.set_colors(1);
GLuint row = 0;
unsigned int line = 0;
while (std::getline(file, str))
{
GLuint col = 0;
int num = 0;
if (++line < 7) continue;
std::istringstream iss(str);
std::string token;
while (std::getline(iss, token, ' '))
{
if (str2int(num, token.c_str(), 10) == SUCCESS)
{
GLubyte val = (num == 32) ? 0 : 4*num;
texture.set_pixel(col++, NROWS-row, &val);
}
}
row++;
}
texture.write_texture(outfile);
}

289
utils/climate/colours.h Normal file
View File

@@ -0,0 +1,289 @@
// colours.h -- This header file contains colour definitions in the
// same way as MS FS5 does
//
// Contributed by "Christian Mayer" <Vader@t-online.de>, started April 1999.
//
// Copyright (C) 1998 Christian Mayer - Vader@t-online.de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef COLOURS_H
#define COLOURS_H
unsigned char msfs_colour[256][3]=
{
{ 0, 0, 0},
{ 8, 8, 8},
{ 16, 16, 16},
{ 24, 24, 24},
{ 32, 32, 32},
{ 40, 40, 40},
{ 48, 48, 48},
{ 56, 56, 56},
{ 65, 65, 65},
{ 73, 73, 73},
{ 81, 81, 81},
{ 89, 89, 89},
{ 97, 97, 97},
{105, 105, 105},
{113, 113, 113},
{121, 121, 121},
{130, 130, 130},
{138, 138, 138},
{146, 146, 146},
{154, 154, 154},
{162, 162, 162},
{170, 170, 170},
{178, 178, 178},
{186, 186, 186},
{195, 195, 195},
{203, 203, 203},
{211, 211, 211},
{219, 219, 219},
{227, 227, 227},
{235, 235, 235},
{247, 247, 247},
{255, 255, 255},
{ 21, 5, 5},
{ 42, 10, 10},
{ 63, 15, 15},
{ 84, 20, 20},
{105, 25, 25},
{126, 30, 30},
{147, 35, 35},
{168, 40, 40},
{189, 45, 45},
{210, 50, 50},
{231, 55, 55},
{252, 60, 60},
{ 5, 21, 5},
{ 10, 42, 10},
{ 15, 63, 15},
{ 20, 84, 20},
{ 25, 105, 25},
{ 30, 126, 30},
{ 35, 147, 35},
{ 40, 168, 40},
{ 45, 189, 45},
{ 50, 210, 50},
{ 55, 231, 55},
{ 60, 252, 60},
{ 0, 7, 23},
{ 0, 15, 40},
{ 0, 23, 58},
{ 0, 40, 84},
{ 0, 64, 104},
{ 0, 71, 122},
{ 0, 87, 143},
{ 0, 99, 156},
{ 0, 112, 179},
{ 0, 128, 199},
{ 0, 143, 215},
{ 0, 153, 230},
{ 28, 14, 0},
{ 56, 28, 0},
{ 84, 42, 0},
{112, 56, 0},
{140, 70, 0},
{168, 84, 0},
{196, 98, 0},
{224, 112, 0},
{252, 126, 0},
{ 28, 28, 0},
{ 56, 56, 0},
{ 84, 84, 0},
{112, 112, 0},
{140, 140, 0},
{168, 168, 0},
{196, 196, 0},
{224, 224, 0},
{252, 252, 0},
{ 25, 21, 16},
{ 50, 42, 32},
{ 75, 63, 48},
{100, 84, 64},
{125, 105, 80},
{150, 126, 96},
{175, 147, 112},
{200, 168, 128},
{225, 189, 144},
{ 28, 11, 7},
{ 56, 22, 14},
{ 84, 33, 21},
{112, 44, 28},
{140, 55, 35},
{168, 66, 42},
{196, 77, 49},
{224, 88, 56},
{252, 99, 63},
{ 17, 22, 9},
{ 34, 44, 18},
{ 51, 66, 27},
{ 68, 88, 36},
{ 85, 110, 45},
{102, 132, 54},
{119, 154, 63},
{136, 176, 72},
{153, 198, 81},
{ 0, 58, 104},
{ 0, 87, 112},
{ 43, 112, 128},
{255, 0, 0},
{ 64, 255, 64},
{ 0, 0, 192},
{ 0, 105, 105},
{255, 128, 0},
{255, 255, 0},
{ 81, 81, 81},
{121, 121, 121},
{146, 146, 146},
{170, 170, 170},
{195, 195, 195},
{227, 227, 227},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{192, 192, 255},
{200, 200, 255},
{208, 208, 255},
{216, 216, 255},
{224, 224, 255},
{232, 232, 255},
{240, 240, 255},
{248, 248, 255},
{255, 255, 255},
{255, 255, 255},
{255, 255, 255},
{255, 255, 255},
{255, 255, 255},
{255, 255, 255},
{255, 255, 255},
{255, 255, 255},
{ 16, 72, 16},
{ 32, 80, 32},
{ 48, 88, 48},
{ 64, 96, 64},
{ 80, 104, 80},
{ 96, 112, 96},
{112, 120, 112},
{120, 124, 120},
{128, 128, 128},
{128, 128, 128},
{128, 128, 128},
{128, 128, 128},
{128, 128, 128},
{128, 128, 128},
{128, 128, 128},
{128, 128, 128},
{ 33, 140, 189},
{ 57, 132, 165},
{189, 66, 66},
{156, 66, 66},
{132, 74, 74},
{ 33, 82, 107},
{214, 90, 82},
{189, 90, 82},
{165, 90, 82},
{123, 57, 49},
{ 99, 57, 49},
{107, 74, 66},
{123, 90, 82},
{181, 90, 66},
{ 74, 49, 41},
{189, 115, 90},
{140, 90, 49},
{ 33, 49, 74},
{181, 115, 49},
{ 99, 66, 33},
{165, 115, 66},
{ 49, 41, 33},
{165, 140, 115},
{189, 165, 140},
{ 57, 99, 123},
{181, 107, 24},
{206, 123, 33},
{156, 99, 33},
{148, 107, 49},
{107, 82, 49},
{ 33, 33, 57},
{ 33, 115, 165},
{214, 214, 33},
{173, 173, 33},
{198, 198, 41},
{140, 140, 33},
{115, 115, 33},
{189, 189, 57},
{156, 156, 49},
{173, 173, 57},
{123, 123, 49},
{123, 123, 66},
{ 74, 74, 49},
{123, 123, 90},
{ 41, 41, 33},
{ 90, 99, 57},
{107, 115, 74},
{123, 148, 82},
{140, 173, 99},
{132, 156, 99},
{ 49, 66, 41},
{ 99, 165, 90},
{ 74, 214, 74},
{ 57, 140, 57},
{ 74, 181, 74},
{ 90, 198, 90},
{ 57, 123, 57},
{ 49, 99, 49},
{ 90, 165, 90},
{ 82, 148, 82},
{ 74, 99, 74},
{ 57, 115, 132},
{ 33, 99, 123},
{ 74, 115, 132}
};
#endif

970
utils/climate/texture.cxx Normal file
View File

@@ -0,0 +1,970 @@
/*
* Texture manipulation routines
*
* Copyright (c) Mark J. Kilgard, 1997.
* Code added in april 2003 by Erik Hofman
*
* This program is freely distributable without licensing fees
* and is provided without guarantee or warrantee expressed or
* implied. This program is -not- in the public domain.
*
* $Id$
*/
#include <simgear/compiler.h>
#ifdef WIN32
# include <windows.h>
#endif
#include <osg/Matrixf>
#include <math.h>
#include <zlib.h>
#include "texture.hxx"
#include "colours.h"
const char *FILE_OPEN_ERROR = "Unable to open file.";
const char *WRONG_COUNT = "Unsupported number of color channels.";
const char *NO_TEXTURE = "No texture data resident.";
const char *OUT_OF_MEMORY = "Out of memory.";
SGTexture::SGTexture()
: texture_id(0),
texture_data(0),
num_colors(3),
file(0)
{
}
SGTexture::SGTexture(unsigned int width, unsigned int height)
: texture_id(0),
errstr("")
{
texture_data = new GLubyte[ width * height * 3 ];
}
SGTexture::~SGTexture()
{
delete[] texture_data;
if ( texture_id != 0 ) {
free_id();
}
}
void
SGTexture::bind()
{
bool gen = false;
if (!texture_id) {
#ifdef GL_VERSION_1_1
glGenTextures(1, &texture_id);
#elif GL_EXT_texture_object
glGenTexturesEXT(1, &texture_id);
#endif
gen = true;
}
#ifdef GL_VERSION_1_1
glBindTexture(GL_TEXTURE_2D, texture_id);
#elif GL_EXT_texture_object
glBindTextureEXT(GL_TEXTURE_2D, texture_id);
#endif
if (gen) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
}
/**
* A function to resize the OpenGL window which will be used by
* the dynamic texture generating routines.
*
* @param width The width of the new window
* @param height The height of the new window
*/
void
SGTexture::resize(unsigned int width, unsigned int height)
{
using namespace osg;
GLfloat aspect;
// Make sure that we don't get a divide by zero exception
if (height == 0)
height = 1;
// Set the viewport for the OpenGL window
glViewport(0, 0, width, height);
// Calculate the aspect ratio of the window
aspect = width/height;
// Go to the projection matrix, this gets modified by the perspective
// calulations
glMatrixMode(GL_PROJECTION);
// Do the perspective calculations
Matrixf proj = Matrixf::perspective(45.0, aspect, 1.0, 400.0);
glLoadMatrix(proj.ptr());
// Return to the modelview matrix
glMatrixMode(GL_MODELVIEW);
}
/**
* A function to prepare the OpenGL state machine for dynamic
* texture generation.
*
* @param width The width of the texture
* @param height The height of the texture
*/
void
SGTexture::prepare(unsigned int width, unsigned int height) {
texture_width = width;
texture_height = height;
// Resize the OpenGL window to the size of our dynamic texture
resize(texture_width, texture_height);
glClearColor(0.0, 0.0, 0.0, 1.0);
}
/**
* A function to generate the dynamic texture.
*
* The actual texture can be accessed by calling get_texture()
*
* @param width The width of the previous OpenGL window
* @param height The height of the previous OpenGL window
*/
void
SGTexture::finish(unsigned int width, unsigned int height) {
// If a texture hasn't been created then it gets created, and the contents
// of the frame buffer gets copied into it. If the texture has already been
// created then its contents just get updated.
bind();
if (!texture_data)
{
// Copies the contents of the frame buffer into the texture
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0,
texture_width, texture_height, 0);
} else {
// Copies the contents of the frame buffer into the texture
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
texture_width, texture_height);
}
// Set the OpenGL window back to its previous size
resize(width, height);
// Clear the window back to black
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void
SGTexture::read_alpha_texture(const char *name)
{
GLubyte *lptr;
SGTexture::ImageRec *image;
int y;
delete[] texture_data;
image = ImageOpen(name);
if(!image) {
errstr = FILE_OPEN_ERROR;
return;
}
texture_width = image->xsize;
texture_height = image->ysize;
// printf("image->zsize = %d\n", image->zsize);
if (image->zsize != 1) {
ImageClose(image);
errstr = WRONG_COUNT;
return;
}
texture_data = new GLubyte[ image->xsize * image->ysize ];
num_colors = 1;
if (!texture_data) {
errstr = NO_TEXTURE;
return;
}
lptr = texture_data;
for(y=0; y<image->ysize; y++) {
ImageGetRow(image,lptr,y,0);
lptr += image->xsize;
}
ImageClose(image);
}
void
SGTexture::read_rgb_texture(const char *name)
{
GLubyte *ptr;
GLubyte *rbuf, *gbuf, *bbuf;
SGTexture::ImageRec *image;
int y;
delete[] texture_data;
image = ImageOpen(name);
if(!image) {
errstr = FILE_OPEN_ERROR;
return;
}
texture_width = image->xsize;
texture_height = image->ysize;
if (image->zsize < 1 || image->zsize > 4) {
ImageClose(image);
errstr = WRONG_COUNT;
return;
}
num_colors = 3;
texture_data = new GLubyte[ image->xsize * image->ysize * num_colors ];
rbuf = new GLubyte[ image->xsize ];
gbuf = new GLubyte[ image->xsize ];
bbuf = new GLubyte[ image->xsize ];
if(!texture_data || !rbuf || !gbuf || !bbuf) {
delete[] texture_data;
delete[] rbuf;
delete[] gbuf;
delete[] bbuf;
errstr = OUT_OF_MEMORY;
return;
}
ptr = texture_data;
for(y=0; y<image->ysize; y++) {
if(image->zsize == 4 || image->zsize == 3) {
ImageGetRow(image,rbuf,y,0);
ImageGetRow(image,gbuf,y,1);
ImageGetRow(image,bbuf,y,2);
} else {
ImageGetRow(image,rbuf,y,0);
memcpy(gbuf,rbuf,image->xsize);
memcpy(bbuf,rbuf,image->xsize);
}
rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
ptr += (image->xsize * num_colors);
}
ImageClose(image);
delete[] rbuf;
delete[] gbuf;
delete[] bbuf;
}
void
SGTexture::read_rgba_texture(const char *name)
{
GLubyte *ptr;
GLubyte *rbuf, *gbuf, *bbuf, *abuf;
SGTexture::ImageRec *image;
int y;
delete[] texture_data;
image = ImageOpen(name);
if(!image) {
errstr = FILE_OPEN_ERROR;
return;
}
texture_width = image->xsize;
texture_height = image->ysize;
if (image->zsize < 1 || image->zsize > 4) {
ImageClose(image);
errstr = WRONG_COUNT;
return;
}
num_colors = 4;
texture_data = new GLubyte[ image->xsize * image->ysize * num_colors ];
rbuf = new GLubyte[ image->xsize ];
gbuf = new GLubyte[ image->xsize ];
bbuf = new GLubyte[ image->xsize ];
abuf = new GLubyte[ image->xsize ];
if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
delete[] texture_data;
delete[] rbuf;
delete[] gbuf;
delete[] bbuf;
delete[] abuf;
errstr = OUT_OF_MEMORY;
return;
}
ptr = texture_data;
for(y=0; y<image->ysize; y++) {
if(image->zsize == 4) {
ImageGetRow(image,rbuf,y,0);
ImageGetRow(image,gbuf,y,1);
ImageGetRow(image,bbuf,y,2);
ImageGetRow(image,abuf,y,3);
} else if(image->zsize == 3) {
ImageGetRow(image,rbuf,y,0);
ImageGetRow(image,gbuf,y,1);
ImageGetRow(image,bbuf,y,2);
memset(abuf, 255, image->xsize);
} else if(image->zsize == 2) {
ImageGetRow(image,rbuf,y,0);
memcpy(gbuf,rbuf,image->xsize);
memcpy(bbuf,rbuf,image->xsize);
ImageGetRow(image,abuf,y,1);
} else {
ImageGetRow(image,rbuf,y,0);
memcpy(gbuf,rbuf,image->xsize);
memcpy(bbuf,rbuf,image->xsize);
memset(abuf, 255, image->xsize);
}
rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
ptr += (image->xsize * num_colors);
}
ImageClose(image);
delete[] rbuf;
delete[] gbuf;
delete[] bbuf;
delete[] abuf;
}
void
SGTexture::read_raw_texture(const char *name)
{
GLubyte *ptr;
SGTexture::ImageRec *image;
int y;
delete[] texture_data;
image = RawImageOpen(name);
if(!image) {
errstr = FILE_OPEN_ERROR;
return;
}
texture_width = 256;
texture_height = 256;
texture_data = new GLubyte[ 256 * 256 * 3 ];
if(!texture_data) {
errstr = OUT_OF_MEMORY;
return;
}
ptr = texture_data;
for(y=0; y<256; y++) {
gzread(image->file, ptr, 256*3);
ptr+=256*3;
}
ImageClose(image);
}
void
SGTexture::read_r8_texture(const char *name)
{
unsigned char c[1];
GLubyte *ptr;
SGTexture::ImageRec *image;
int xy;
delete[] texture_data;
//it wouldn't make sense to write a new function ...
image = RawImageOpen(name);
if(!image) {
errstr = FILE_OPEN_ERROR;
return;
}
texture_width = 256;
texture_height = 256;
texture_data = new GLubyte [ 256 * 256 * 3 ];
if(!texture_data) {
errstr = OUT_OF_MEMORY;
return;
}
ptr = texture_data;
for(xy=0; xy<(256*256); xy++) {
gzread(image->file, c, 1);
//look in the table for the right colours
ptr[0]=msfs_colour[c[0]][0];
ptr[1]=msfs_colour[c[0]][1];
ptr[2]=msfs_colour[c[0]][2];
ptr+=3;
}
ImageClose(image);
}
void
SGTexture::write_texture(const char *name) {
SGTexture::ImageRec *image = ImageWriteOpen(name);
printf("colors: %i, height: %i, width: %i\n", num_colors, texture_height, texture_width);
for (int c=0; c<num_colors; c++) {
GLubyte *ptr = texture_data + c;
for (int y=0; y<texture_height; y++) {
for (int x=0; x<texture_width; x++) {
image->tmp[x]=*ptr;
ptr = ptr + num_colors;
}
fwrite(image->tmp, 1, texture_width, file);
}
}
ImageClose(image);
}
void
SGTexture::set_pixel(GLuint x, GLuint y, GLubyte *c)
{
if (!texture_data) {
errstr = NO_TEXTURE;
return;
}
unsigned int pos = (x + y*texture_width) * num_colors;
memcpy(texture_data+pos, c, num_colors);
}
GLubyte *
SGTexture::get_pixel(GLuint x, GLuint y)
{
static GLubyte c[4] = {0, 0, 0, 0};
if (!texture_data) {
errstr = NO_TEXTURE;
return c;
}
unsigned int pos = (x + y*texture_width)*num_colors;
memcpy(c, texture_data + pos, num_colors);
return c;
}
SGTexture::ImageRec *
SGTexture::ImageOpen(const char *fileName)
{
union {
int testWord;
char testByte[4];
} endianTest;
SGTexture::ImageRec *image;
int swapFlag;
int x;
endianTest.testWord = 1;
if (endianTest.testByte[0] == 1) {
swapFlag = 1;
} else {
swapFlag = 0;
}
image = new SGTexture::ImageRec;
memset(image, 0, sizeof(SGTexture::ImageRec));
if (image == 0) {
errstr = OUT_OF_MEMORY;
return 0;
}
if ((image->file = gzopen(fileName, "rb")) == 0) {
errstr = FILE_OPEN_ERROR;
return 0;
}
gzread(image->file, image, 12);
if (swapFlag) {
ConvertShort(&image->imagic, 6);
}
image->tmp = new GLubyte[ image->xsize * 256 ];
if (image->tmp == 0) {
errstr = OUT_OF_MEMORY;
return 0;
}
if ((image->type & 0xFF00) == 0x0100) {
x = image->ysize * image->zsize * (int) sizeof(unsigned);
image->rowStart = new unsigned[x];
image->rowSize = new int[x];
if (image->rowStart == 0 || image->rowSize == 0) {
errstr = OUT_OF_MEMORY;
return 0;
}
image->rleEnd = 512 + (2 * x);
gzseek(image->file, 512, SEEK_SET);
gzread(image->file, image->rowStart, x);
gzread(image->file, image->rowSize, x);
if (swapFlag) {
ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
}
}
return image;
}
void
SGTexture::ImageClose(SGTexture::ImageRec *image) {
if (image->file) gzclose(image->file);
if (file) fclose(file);
delete[] image->tmp;
delete[] image->rowStart;
delete[] image->rowSize;
delete image;
}
SGTexture::ImageRec *
SGTexture::RawImageOpen(const char *fileName)
{
union {
int testWord;
char testByte[4];
} endianTest;
SGTexture::ImageRec *image;
int swapFlag;
endianTest.testWord = 1;
if (endianTest.testByte[0] == 1) {
swapFlag = 1;
} else {
swapFlag = 0;
}
image = new SGTexture::ImageRec;
memset(image, 0, sizeof(SGTexture::ImageRec));
if (image == 0) {
errstr = OUT_OF_MEMORY;
return 0;
}
if ((image->file = gzopen(fileName, "rb")) == 0) {
errstr = FILE_OPEN_ERROR;
return 0;
}
gzread(image->file, image, 12);
if (swapFlag) {
ConvertShort(&image->imagic, 6);
}
//just allocate a pseudo value as I'm too lazy to change ImageClose()...
image->tmp = new GLubyte[1];
if (image->tmp == 0) {
errstr = OUT_OF_MEMORY;
return 0;
}
return image;
}
SGTexture::ImageRec *
SGTexture::ImageWriteOpen(const char *fileName)
{
union {
int testWord;
char testByte[4];
} endianTest;
ImageRec* image;
int swapFlag;
int x;
endianTest.testWord = 1;
if (endianTest.testByte[0] == 1) {
swapFlag = 1;
} else {
swapFlag = 0;
}
image = new SGTexture::ImageRec;
memset(image, 0, sizeof(SGTexture::ImageRec));
if (image == 0) {
errstr = OUT_OF_MEMORY;
return 0;
}
if ((file = fopen(fileName, "wb")) == 0) {
errstr = FILE_OPEN_ERROR;
return 0;
}
image->imagic = 474;
image->type = 0x0001;
image->dim = (num_colors > 1) ? 3 : 2;
image->xsize = texture_width;
image->ysize = texture_height;
image->zsize = num_colors;
if (swapFlag) {
ConvertShort(&image->imagic, 6);
}
fwrite(image, 1, 12, file);
fseek(file, 512, SEEK_SET);
image->tmp = new GLubyte[ image->xsize * 256 ];
if (image->tmp == 0) {
errstr = OUT_OF_MEMORY;
return 0;
}
if ((image->type & 0xFF00) == 0x0100) {
x = image->ysize * image->zsize * (int) sizeof(unsigned);
image->rowStart = new unsigned[x];
image->rowSize = new int[x];
if (image->rowStart == 0 || image->rowSize == 0) {
errstr = OUT_OF_MEMORY;
return 0;
}
image->rleEnd = 512 + (2 * x);
fseek(file, 512, SEEK_SET);
fread(image->rowStart, 1, x, file);
fread(image->rowSize, 1, x, file);
if (swapFlag) {
ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
}
}
return image;
}
void
SGTexture::ImageGetRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
GLubyte *iPtr, *oPtr, pixel;
int count;
if ((image->type & 0xFF00) == 0x0100) {
gzseek(image->file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
int size = image->rowSize[y+z*image->ysize];
gzread(image->file, image->tmp, size);
iPtr = image->tmp;
oPtr = buf;
for (GLubyte *limit = iPtr + size; iPtr < limit;) {
pixel = *iPtr++;
count = (int)(pixel & 0x7F);
if (!count) {
errstr = WRONG_COUNT;
return;
}
if (pixel & 0x80) {
while (iPtr < limit && count--) {
*oPtr++ = *iPtr++;
}
} else if (iPtr < limit) {
pixel = *iPtr++;
while (count--) {
*oPtr++ = pixel;
}
}
}
} else {
gzseek(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
SEEK_SET);
gzread(image->file, buf, image->xsize);
}
}
void
SGTexture::ImagePutRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
GLubyte *iPtr, *oPtr, pixel;
int count;
if ((image->type & 0xFF00) == 0x0100) {
fseek(file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
fread( image->tmp, 1, (unsigned int)image->rowSize[y+z*image->ysize],
file);
iPtr = image->tmp;
oPtr = buf;
for (;;) {
pixel = *iPtr++;
count = (int)(pixel & 0x7F);
if (!count) {
errstr = WRONG_COUNT;
return;
}
if (pixel & 0x80) {
while (count--) {
*oPtr++ = *iPtr++;
}
} else {
pixel = *iPtr++;
while (count--) {
*oPtr++ = pixel;
}
}
}
} else {
fseek(file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
SEEK_SET);
fread(buf, 1, image->xsize, file);
}
}
void
SGTexture::rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n) {
while(n--) {
l[0] = r[0];
l[1] = g[0];
l[2] = b[0];
l += 3; r++; g++; b++;
}
}
void
SGTexture::rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
GLubyte *l, int n) {
while(n--) {
l[0] = r[0];
l[1] = g[0];
l[2] = b[0];
l[3] = a[0];
l += 4; r++; g++; b++; a++;
}
}
void
SGTexture::ConvertShort(unsigned short *array, unsigned int length) {
unsigned short b1, b2;
unsigned char *ptr;
ptr = (unsigned char *)array;
while (length--) {
b1 = *ptr++;
b2 = *ptr++;
*array++ = (b1 << 8) | (b2);
}
}
void
SGTexture::ConvertUint(unsigned *array, unsigned int length) {
unsigned int b1, b2, b3, b4;
unsigned char *ptr;
ptr = (unsigned char *)array;
while (length--) {
b1 = *ptr++;
b2 = *ptr++;
b3 = *ptr++;
b4 = *ptr++;
*array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
}
}
void
SGTexture::make_monochrome(float contrast, GLubyte r, GLubyte g, GLubyte b) {
if (num_colors >= 3)
return;
GLubyte ap[3];
for (int y=0; y<texture_height; y++)
for (int x=0; x<texture_width; x++)
{
GLubyte *rgb = get_pixel(x,y);
GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
if (contrast != 1.0) {
float pixcol = -1.0 + (avg/128);
avg = 128 + int(128*pow(pixcol, contrast));
}
ap[0] = avg*r/255;
ap[1] = avg*g/255;
ap[2] = avg*b/255;
set_pixel(x,y,ap);
}
}
void
SGTexture::make_grayscale(float contrast) {
if (num_colors < 3)
return;
int colors = (num_colors == 3) ? 1 : 2;
GLubyte *map = new GLubyte[ texture_width * texture_height * colors ];
for (int y=0; y<texture_height; y++)
for (int x=0; x<texture_width; x++)
{
GLubyte *rgb = get_pixel(x,y);
GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
if (contrast != 1.0) {
float pixcol = -1.0 + (avg/128);
avg = 128 + int(128*pow(pixcol, contrast));
}
int pos = (x + y*texture_width)*colors;
map[pos] = avg;
if (colors > 1)
map[pos+1] = rgb[3];
}
delete[] texture_data;
texture_data = map;
num_colors = colors;
}
void
SGTexture::make_maxcolorwindow() {
GLubyte minmaxc[2] = {255, 0};
int pos = 0;
int max = num_colors;
if (num_colors == 2) max = 1;
if (num_colors == 4) max = 3;
while (pos < texture_width * texture_height * num_colors) {
for (int i=0; i < max; i++) {
GLubyte c = texture_data[pos+i];
if (c < minmaxc[0]) minmaxc[0] = c;
if (c > minmaxc[1]) minmaxc[1] = c;
}
pos += num_colors;
}
GLubyte offs = minmaxc[0];
float factor = 255.0 / float(minmaxc[1] - minmaxc[0]);
// printf("Min: %i, Max: %i, Factor: %f\n", offs, minmaxc[1], factor);
pos = 0;
while (pos < texture_width * texture_height * num_colors) {
for (int i=0; i < max; i++) {
texture_data[pos+i] -= offs;
texture_data[pos+i] = int(factor * texture_data[pos+i]);
}
pos += num_colors;
}
}
void
SGTexture::make_normalmap(float brightness, float contrast) {
make_grayscale(contrast);
make_maxcolorwindow();
int colors = (num_colors == 1) ? 3 : 4;
bool alpha = (colors > 3);
int tsize = texture_width * texture_height * colors;
GLubyte *map = new GLubyte[ tsize ];
int mpos = 0, dpos = 0;
for (int y=0; y<texture_height; y++) {
int ytw = y*texture_width;
for (int x=0; x<texture_width; x++)
{
int xp1 = (x < (texture_width-1)) ? x+1 : 0;
int yp1 = (y < (texture_height-1)) ? y+1 : 0;
int posxp1 = (xp1 + ytw)*num_colors;
int posyp1 = (x + yp1*texture_width)*num_colors;
float fx,fy;
GLubyte c = texture_data[dpos];
GLubyte cx1 = texture_data[posxp1];
GLubyte cy1 = texture_data[posyp1];
if (alpha) {
GLubyte a = texture_data[dpos+1];
GLubyte ax1 = texture_data[posxp1+1];
GLubyte ay1 = texture_data[posyp1+1];
c = (c + a)/2;
cx1 = (cx1 + ax1)/2;
cy1 = (cy1 + ay1)/2;
map[mpos+3] = a;
}
fx = asin((c/256.0-cx1/256.0))/1.57079633;
fy = asin((cy1/256.0-c/256.0))/1.57079633;
map[mpos+0] = (GLuint)(fx*256.0)-128;
map[mpos+1] = (GLuint)(fy*256.0)-128;
map[mpos+2] = 127+int(brightness*128); // 255-c/2;
mpos += colors;
dpos += num_colors;
}
}
delete[] texture_data;
texture_data = map;
num_colors = colors;
}
void
SGTexture::make_bumpmap(float brightness, float contrast) {
make_grayscale(contrast);
int colors = (num_colors == 1) ? 1 : 2;
GLubyte *map = new GLubyte[ texture_width * texture_height * colors ];
for (int y=0; y<texture_height; y++)
for (int x=0; x<texture_width; x++)
{
int mpos = (x + y*texture_width)*colors;
int dpos = (x + y*texture_width)*num_colors;
int xp1 = (x < (texture_width-1)) ? x+1 : 0;
int yp1 = (y < (texture_height-1)) ? y+1 : 0;
int posxp1 = (xp1 + y*texture_width)*num_colors;
int posyp1 = (x + yp1*texture_width)*num_colors;
map[mpos] = (127 - ((texture_data[dpos]-texture_data[posxp1]) -
((texture_data[dpos]-texture_data[posyp1]))/4))/2;
if (colors > 1)
map[mpos+1] = texture_data[dpos+1];
}
delete[] texture_data;
texture_data = map;
num_colors = colors;
}

153
utils/climate/texture.hxx Normal file
View File

@@ -0,0 +1,153 @@
/*
* \file texture.hxx
* Texture manipulation routines
*
* Copyright (c) Mark J. Kilgard, 1997.
* Code added in april 2003 by Erik Hofman
*
* This program is freely distributable without licensing fees
* and is provided without guarantee or warrantee expressed or
* implied. This program is -not- in the public domain.
*/
#ifndef __SG_TEXTURE_HXX
#define __SG_TEXTURE_HXX 1
#include <simgear/compiler.h>
#include <osg/GL>
#include <zlib.h>
#include <plib/sg.h>
/**
* A class to encapsulate all the info surrounding an OpenGL texture
* and also query various info and load various file formats
*/
class SGTexture {
private:
GLuint texture_id;
GLubyte *texture_data;
GLsizei texture_width;
GLsizei texture_height;
GLsizei num_colors;
void resize(unsigned int width = 256, unsigned int height = 256);
const char *errstr;
protected:
FILE *file;
typedef struct _ImageRec {
_ImageRec(void) : tmp(0), rowStart(0), rowSize(0) {}
unsigned short imagic;
unsigned short type;
unsigned short dim;
unsigned short xsize, ysize, zsize;
unsigned int min, max;
unsigned int wasteBytes;
char name[80];
unsigned long colorMap;
gzFile file;
GLubyte *tmp;
unsigned long rleEnd;
unsigned int *rowStart;
int *rowSize;
} ImageRec;
void ConvertUint(unsigned *array, unsigned int length);
void ConvertShort(unsigned short *array, unsigned int length);
void rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n);
void rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
GLubyte *l, int n);
ImageRec *ImageOpen(const char *fileName);
ImageRec *ImageWriteOpen(const char *fileName);
ImageRec *RawImageOpen(const char *fileName);
void ImageClose(ImageRec *image);
void ImageGetRow(ImageRec *image, GLubyte *buf, int y, int z);
void ImagePutRow(ImageRec *image, GLubyte *buf, int y, int z);
inline void free_id() {
#ifdef GL_VERSION_1_1
glDeleteTextures(1, &texture_id);
#else
glDeleteTexturesEXT(1, &texture_id);
#endif
texture_id = 0;
}
public:
SGTexture();
SGTexture(unsigned int width, unsigned int height);
~SGTexture();
/* Copyright (c) Mark J. Kilgard, 1997. */
void read_alpha_texture(const char *name);
void read_rgb_texture(const char *name);
void read_rgba_texture(const char *name);
void read_raw_texture(const char *name);
void read_r8_texture(const char *name);
void write_texture(const char *name);
inline bool usable() { return (texture_id > 0) ? true : false; }
inline GLuint id() { return texture_id; }
inline GLubyte *texture() { return texture_data; }
inline void set_data(GLubyte *data) { texture_data = data; }
inline void set_colors(int c) { num_colors = c; }
inline int width() { return texture_width; }
inline int height() { return texture_height; }
inline int colors() { return num_colors; }
// dynamic texture functions.
// everything drawn to the OpenGL screen after prepare is
// called and before finish is called will be included
// in the new texture.
void prepare(unsigned int width = 256, unsigned int height = 256);
void finish(unsigned int width, unsigned int height);
// texture pixel manipulation functions.
void set_pixel(GLuint x, GLuint y, GLubyte *c);
GLubyte *get_pixel(GLuint x, GLuint y);
void bind();
inline void select(bool keep_data = false) {
glTexImage2D( GL_TEXTURE_2D, 0, num_colors,
texture_width, texture_height, 0,
(num_colors==1)?GL_LUMINANCE:(num_colors==3)?GL_RGB:GL_RGBA, GL_UNSIGNED_BYTE, texture_data );
if (!keep_data) {
delete[] texture_data;
texture_data = 0;
}
}
// Nowhere does it say that resident textures have to be in video memory!
// NVidia's OpenGL drivers implement glAreTexturesResident() correctly,
// but the Matrox G400, for example, doesn't.
inline bool is_resident() {
GLboolean is_res;
glAreTexturesResident(1, &texture_id, &is_res);
return is_res != 0;
}
inline const char *err_str() { return errstr; }
inline void clear_err_str() { errstr = ""; }
void make_maxcolorwindow();
void make_grayscale(float contrast = 1.0);
void make_monochrome(float contrast = 1.0,
GLubyte r=255, GLubyte g=255, GLubyte b=255);
void make_normalmap(float brightness = 1.0, float contrast = 1.0);
void make_bumpmap(float brightness = 1.0, float contrast = 1.0);
};
#endif

View File

@@ -0,0 +1,5 @@
add_executable(demconvert demconvert.cxx )
target_link_libraries(demconvert SimGearScene SimGearCore)
install(TARGETS demconvert RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

View File

@@ -0,0 +1,219 @@
// demconvert.cxx -- convert dem into lower resolutions
//
// Copyright (C) 2016 Peter Sadrozinski
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <osg/ArgumentParser>
#include <simgear/misc/stdint.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/scene/dem/SGDem.hxx>
#include <simgear/scene/dem/SGDemSession.hxx>
int main(int argc, char** argv)
{
std::string demroot;
std::string inputvfp;
int tileWidth;
int tileHeight;
int resx, resy;
int overlap = 0;
SGDem dem;
osg::ApplicationUsage* usage = new osg::ApplicationUsage();
usage->setApplicationName("demconvert");
usage->setCommandLineUsage(
"Convert high resolution DEM to low res suitable for terrasync dl.");
usage->addCommandLineOption("--inputvfp <dir>", "input VFP root directory");
usage->addCommandLineOption("--demroot <dir>", "input/ouput DEM root directory");
usage->addCommandLineOption("--width <N>", "width (in degrees) of created tiles");
usage->addCommandLineOption("--height <N>", "height (in degrees) of created tiles");
usage->addCommandLineOption("--resx <N>", "resolution of created tiles (w/o overlap)");
usage->addCommandLineOption("--resy <N>", "resolution of created tiles (w/o overlap)");
usage->addCommandLineOption("--overlap <N>", "number of pixels of overlap");
// use an ArgumentParser object to manage the program arguments.
osg::ArgumentParser arguments(&argc, argv);
arguments.setApplicationUsage(usage);
sglog().setLogLevels( SG_TERRAIN, SG_INFO );
arguments.read("--inputvfp", inputvfp);
printf( "--inputvfp is %s\n", inputvfp.c_str() );
arguments.read("--demroot", demroot);
if ( inputvfp.empty() && demroot.empty() ) {
arguments.reportError("--inputvfp or --demroot argument required.");
} else if ( !demroot.empty() ) {
SGPath s(demroot);
if (!s.isDir()) {
arguments.reportError(
"--demroot directory does not exist or is not directory.");
} else if (!s.canRead()) {
arguments.reportError(
"--demroot directory cannot be read. Check permissions.");
} else if (!s.canWrite()) {
arguments.reportError(
"--demroot directory cannot be written. Check permissions.");
} else if ( !dem.addRoot(s) ) {
// see if we specified input as raw directory
if ( inputvfp.empty() ) {
arguments.reportError(
"--demroot directory is not a DEM heiarchy");
} else {
// create a new dem heiarchy
printf("Creating new dem heiarchy at %s\n", s.c_str() );
dem.createRoot(s);
}
}
} else {
SGPath s(inputvfp);
if (!s.isDir()) {
arguments.reportError(
"--inputvfp directory does not exist or is not directory.");
} else if (!s.canRead()) {
arguments.reportError(
"--inputvfp directory cannot be read. Check permissions.");
}
}
if (!arguments.read("--width", tileWidth)) {
arguments.reportError("--width argument required.");
} else {
if ( tileWidth < 1 || tileWidth > 60 )
arguments.reportError(
"--width must be between 1 and 60");
}
if (!arguments.read("--height", tileHeight)) {
arguments.reportError("--height argument required.");
} else {
if ( tileHeight < 1 || tileHeight > 60 )
arguments.reportError(
"--height must be between 1 and 60");
}
if (!arguments.read("--resx", resx)) {
arguments.reportError("--resx argument required.");
} else {
if ( resx < 2 )
arguments.reportError(
"--resx must be between greater than 2");
}
if (!arguments.read("--resy", resy)) {
arguments.reportError("--resy argument required.");
} else {
if ( resy < 2 )
arguments.reportError(
"--resy must be between greater than 2");
}
if (arguments.read("--overlap", overlap)) {
if ( overlap > (resx/2) || overlap > (resy/2) )
arguments.reportError(
"--overlap is greater than half tile");
}
if (arguments.errors()) {
arguments.writeErrorMessages(std::cout);
arguments.getApplicationUsage()->write(std::cout,
osg::ApplicationUsage::COMMAND_LINE_OPTION |
osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE, 80, true);
return EXIT_FAILURE;
}
double lat_dec = (double)tileHeight / (double)resy;
double lon_inc = (double)tileWidth / (double)resx;
SG_LOG( SG_TERRAIN, SG_INFO, "tileWidth: " << tileWidth);
SG_LOG( SG_TERRAIN, SG_INFO, "tileHeight: " << tileHeight);
SG_LOG( SG_TERRAIN, SG_INFO, "lon_inc: " << lon_inc);
SG_LOG( SG_TERRAIN, SG_INFO, "lat_dec: " << lat_dec);
#define MIN_X -180
#define MIN_Y -90
#define MAX_X 180
#define MAX_Y 90
// create a new dem level in demRoot
SGDemRoot* root = dem.getRoot(0);
if ( root ) {
int outLvl = root->createLevel( tileWidth, tileHeight, resx, resy, overlap, ".tiff" );
if ( outLvl >= 0 ) {
printf("SGDem::createLevel success\n");
// traverse the new tiles, 1 at a time
for ( int tilex = MIN_X; tilex < MAX_X; tilex += tileWidth ) {
for ( int tiley = MAX_Y; tiley > MIN_Y; tiley -= tileHeight ) {
// traverse rows from north to south, then columns west to east
double lonmin = (double)tilex;
double lonmax = lonmin + (double)tileWidth;
double latmax = (double)tiley;
double latmin = latmax - (double)tileHeight;
unsigned wo = SGDem::longitudeDegToOffset(lonmin);
unsigned eo = SGDem::longitudeDegToOffset(lonmax);
unsigned so = SGDem::latitudeDegToOffset(latmin);
unsigned no = SGDem::latitudeDegToOffset(latmax);
if ( !inputvfp.empty() ) {
// read from vfp files
printf("open session from raw directory\n");
SGDemSession s = dem.openSession( SGGeod::fromDeg(lonmin, latmin), SGGeod::fromDeg(lonmax, latmax), SGPath(inputvfp) );
printf("opened session from raw directory\n");
// create a new dem tile for the new level
SGDemTileRef tile = root->createTile( outLvl, (int)lonmin, (int)latmin, overlap, s );
s.close();
} else {
// read session from DEM root - don't cache - include adjacent tiles
fprintf( stderr, "open session from DEM level %d\n", outLvl-1);
SGDemSession s = dem.openSession( wo, so, eo, no, outLvl-1, false );
fprintf( stderr, "session has %d tiles\n", s.size() );
// create a new dem tile for the new level
SGDemTileRef tile = root->createTile( outLvl, (int)lonmin, (int)latmin, overlap, s );
s.close();
}
}
}
printf("SGDem::close Level \n");
root->closeLevel( outLvl );
} else {
printf("SGDem::createLevel failed\n");
}
} else {
printf("SGDem::getRoot failed\n");
}
return EXIT_SUCCESS;
}

153
utils/fgai/AIBVHPager.cxx Normal file
View File

@@ -0,0 +1,153 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <osg/Referenced>
#include "AIBVHPager.hxx"
#include <simgear/bvh/BVHPageNode.hxx>
#include <simgear/bvh/BVHSubTreeCollector.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/ResourceManager.hxx>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/model/BVHPageNodeOSG.hxx>
#include <simgear/scene/model/ModelRegistry.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/OptionsReadFileCallback.hxx>
#include <simgear/scene/tgdb/userdata.hxx>
namespace fgai {
// Short circuit reading image files.
class AIBVHPager::ReadFileCallback : public simgear::OptionsReadFileCallback {
public:
virtual ~ReadFileCallback()
{ }
virtual osgDB::ReaderWriter::ReadResult readImage(const std::string& name, const osgDB::Options*)
{ return new osg::Image; }
};
// A sub tree collector that pages in the nodes that it needs
class AIBVHPager::SubTreeCollector : public simgear::BVHSubTreeCollector {
public:
SubTreeCollector(simgear::BVHPager& pager, const SGSphered& sphere) :
BVHSubTreeCollector(sphere),
_pager(pager),
_complete(true)
{ }
virtual ~SubTreeCollector()
{ }
virtual void apply(simgear::BVHPageNode& pageNode)
{
_pager.use(pageNode);
BVHSubTreeCollector::apply(pageNode);
_complete = _complete && 0 != pageNode.getNumChildren();
}
bool complete() const
{ return _complete; }
private:
simgear::BVHPager& _pager;
bool _complete;
};
AIBVHPager::AIBVHPager()
{
}
AIBVHPager::~AIBVHPager()
{
}
void
AIBVHPager::setScenery(const std::string& fg_root, const std::string& fg_scenery)
{
SGSharedPtr<SGPropertyNode> props = new SGPropertyNode;
try {
SGPath preferencesFile = fg_root;
preferencesFile.append("preferences.xml");
readProperties(preferencesFile.str(), props);
} catch (...) {
// In case of an error, at least make summer :)
props->getNode("sim/startup/season", true)->setStringValue("summer");
SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading FlightGear preferences.\n"
<< "Probably FG_ROOT is not properly set.");
}
/// now set up the simgears required model stuff
simgear::ResourceManager::instance()->addBasePath(fg_root, simgear::ResourceManager::PRIORITY_DEFAULT);
// Just reference simgears reader writer stuff so that the globals get
// pulled in by the linker ...
simgear::ModelRegistry::instance();
sgUserDataInit(props.get());
SGMaterialLib* ml = new SGMaterialLib;
SGPath mpath(fg_root);
mpath.append("Materials/default/materials.xml");
try {
ml->load(fg_root, mpath.str(), props);
} catch (...) {
SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading FlightGear materials.\n"
<< "Probably FG_ROOT is not properly set.");
}
simgear::SGModelLib::init(fg_root, props);
// Set up the reader/writer options
osg::ref_ptr<simgear::SGReaderWriterOptions> options;
if (osgDB::Options* ropt = osgDB::Registry::instance()->getOptions())
options = new simgear::SGReaderWriterOptions(*ropt);
else
options = new simgear::SGReaderWriterOptions;
osgDB::convertStringPathIntoFilePathList(fg_scenery,
options->getDatabasePathList());
options->setMaterialLib(ml);
options->setPropertyNode(props);
options->setReadFileCallback(new ReadFileCallback);
options->setPluginStringData("SimGear::FG_ROOT", fg_root);
// we do not need the builtin boundingvolumes
options->setPluginStringData("SimGear::BOUNDINGVOLUMES", "OFF");
// And we only want terrain, no objects on top.
options->setPluginStringData("SimGear::FG_ONLY_TERRAIN", "ON");
// Hmm, ??!!
options->setPluginStringData("SimGear::FG_ONLY_AIRPORTS", "ON");
props->getNode("sim/rendering/random-objects", true)->setBoolValue(false);
props->getNode("sim/rendering/random-vegetation", true)->setBoolValue(false);
// Get the whole world bvh tree
_node = simgear::BVHPageNodeOSG::load("w180s90-360x180.spt", options);
}
SGSharedPtr<simgear::BVHNode>
AIBVHPager::getBoundingVolumes(const SGSphered& sphere)
{
if (!_node.valid())
return SGSharedPtr<simgear::BVHNode>();
SubTreeCollector subTreeCollector(*this, sphere);
_node->accept(subTreeCollector);
return subTreeCollector.getNode();
}
} // namespace fgai

49
utils/fgai/AIBVHPager.hxx Normal file
View File

@@ -0,0 +1,49 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef _AIBVHPAGER_HXX
#define _AIBVHPAGER_HXX
#include <string>
#include <simgear/bvh/BVHNode.hxx>
#include <simgear/bvh/BVHPager.hxx>
namespace fgai {
class AIBVHPager : public simgear::BVHPager {
public:
AIBVHPager();
~AIBVHPager();
/// Load the flightgear scenery into the pager
void setScenery(const std::string& fg_root, const std::string& fg_scenery);
/// Get a bounding volume subtree contained in sphere.
/// This is similar to the not so well known ground cache.
SGSharedPtr<simgear::BVHNode> getBoundingVolumes(const SGSphered& sphere);
private:
class ReadFileCallback;
class SubTreeCollector;
/// The possibly paged root node
SGSharedPtr<simgear::BVHNode> _node;
};
} // namespace fgai
#endif

View File

@@ -0,0 +1,37 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "AIEnvironment.hxx"
#include "AIObject.hxx"
#include "AIManager.hxx"
namespace fgai {
AIEnvironment::~AIEnvironment()
{
}
void
AIEnvironment::update(AIObject& object, const SGTimeStamp& dt)
{
}
} // namespace fgai

View File

@@ -0,0 +1,48 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef AIEnvironment_hxx
#define AIEnvironment_hxx
#include <simgear/bvh/BVHNode.hxx>
#include <simgear/math/SGGeometry.hxx>
#include "AISubsystem.hxx"
namespace fgai {
class AIPhysics;
class AIBVHPager;
class AIEnvironment : public AISubsystem {
public:
virtual ~AIEnvironment();
virtual void update(AIObject& object, const SGTimeStamp& dt);
// Get these at some point from a weather module and an apropriate
// hla attribute
double getDensity() const
{ return 1.29; }
double getTemperature() const
{ return 15 + 273.15; }
/// The wind speed in cartesian coorindates in the earth centered frame
SGVec3d getWindVelocity() const
{ return SGVec3d::zeros(); }
};
} // namespace fgai
#endif

228
utils/fgai/AIManager.cxx Normal file
View File

@@ -0,0 +1,228 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "AIManager.hxx"
#include <cassert>
#include "HLAAirVehicleClass.hxx"
#include "HLAAircraftClass.hxx"
#include "HLABaloonClass.hxx"
#include "HLAMPAircraftClass.hxx"
#include "AIObject.hxx"
namespace fgai {
AIManager::AIManager() :
_maxStep(SGTimeStamp::fromSecMSec(0, 200))
{
// Set sensible defaults
setFederationExecutionName("rti:///FlightGear");
setFederateType("AIFederate");
/// The hla ai module is running ahead of the simulation time of the federation.
/// This way we are sure that all required data has arrived at the client when it is needed.
/// This is the amount of simulation time the ai module leads the federations simulation time.
setLeadTime(SGTimeStamp::fromSec(10));
setTimeConstrainedByLocalClock(true);
}
AIManager::~AIManager()
{
}
simgear::HLAObjectClass*
AIManager::createObjectClass(const std::string& name)
{
// Just there for demonstration.
if (name == "MPAircraft")
return new HLAMPAircraftClass(name, this);
// These should be the future objects
// The air vehicle should be the one an atc looks at
if (name == "AirVehicle")
return new HLAAirVehicleClass(name, this);
// An aircraft with wings and that
if (name == "Aircraft")
return new HLAAircraftClass(name, this);
// A hot air baloon ...
if (name == "Baloon")
return new HLABaloonClass(name, this);
return 0;
}
bool
AIManager::init()
{
if (!simgear::HLAFederate::init())
return false;
SGTimeStamp federateTime;
queryFederateTime(federateTime);
_simTime = federateTime + getLeadTime();
_pager.start();
return true;
}
bool
AIManager::update()
{
// Mark newly requested paged nodes with the current simulation time.
_pager.setUseStamp((unsigned)_simTime.toSecs());
while (!_initObjectList.empty()) {
assert(_currentObject.empty());
_currentObject.splice(_currentObject.end(), _initObjectList, _initObjectList.begin());
_currentObject.front()->init(*this);
// If it did not reschedule itself, immediately delete it
if (_currentObject.empty())
continue;
_currentObject.front()->shutdown(*this);
_currentObject.clear();
}
// Find the first time slot we have anything scheduled for
TimeStampObjectListIteratorMap::iterator i;
i = _timeStampObjectListIteratorMap.begin();
if (i == _timeStampObjectListIteratorMap.end() || _simTime + _maxStep < i->first) {
// If the time slot is too far away, do a _maxStep time advance.
_simTime += _maxStep;
} else {
// Process the list object updates scheduled for this time slot
_simTime = i->first;
// Call the updates
while (_objectList.begin() != i->second) {
assert(_currentObject.empty());
_currentObject.splice(_currentObject.end(), _objectList, _objectList.begin());
_currentObject.front()->update(*this, _simTime);
// If it did not reschedule itself, immediately delete it
if (_currentObject.empty())
continue;
_currentObject.front()->shutdown(*this);
_currentObject.clear();
}
// get rid of the null element
assert(!_objectList.front().valid());
_objectList.pop_front();
// The timestep has passed now
_timeStampObjectListIteratorMap.erase(i);
}
if (!timeAdvance(_simTime - getLeadTime()))
return false;
// Expire bounding volume nodes older than 120 seconds
_pager.update(120);
return true;
}
bool
AIManager::shutdown()
{
// don't care anmore
_timeStampObjectListIteratorMap.clear();
// Nothing has ever happened with these, just get rid of them
_initObjectList.clear();
// Call shutdown on them
while (!_objectList.empty()) {
SGSharedPtr<AIObject> object;
object.swap(_objectList.front());
_objectList.pop_front();
if (!object.valid())
continue;
object->shutdown(*this);
}
// Then do the hla shutdown part
if (!simgear::HLAFederate::shutdown())
return false;
// Expire bounding volume nodes
_pager.update(0);
_pager.stop();
return true;
}
void
AIManager::insert(const SGSharedPtr<AIObject>& object)
{
if (!object.valid())
return;
/// Note that this iterator is consistently spliced through the various lists,
/// This must stay stable.
object->_objectListIterator = _initObjectList.insert(_initObjectList.end(), object);
}
void
AIManager::schedule(AIObject& object, const SGTimeStamp& simTime)
{
if (simTime <= _simTime)
return;
if (_currentObject.empty())
return;
TimeStampObjectListIteratorMap::iterator i;
i = _timeStampObjectListIteratorMap.lower_bound(simTime);
if (i == _timeStampObjectListIteratorMap.end()) {
ObjectList::iterator j = _objectList.insert(_objectList.end(), 0);
typedef TimeStampObjectListIteratorMap::value_type value_type;
i = _timeStampObjectListIteratorMap.insert(value_type(simTime, j)).first;
} else if (i->first != simTime) {
if (i == _timeStampObjectListIteratorMap.begin()) {
ObjectList::iterator j = _objectList.insert(_objectList.begin(), 0);
typedef TimeStampObjectListIteratorMap::value_type value_type;
i = _timeStampObjectListIteratorMap.insert(value_type(simTime, j)).first;
} else {
--i;
ObjectList::iterator k = i->second;
ObjectList::iterator j = _objectList.insert(++k, 0);
typedef TimeStampObjectListIteratorMap::value_type value_type;
i = _timeStampObjectListIteratorMap.insert(value_type(simTime, j)).first;
}
}
// Note that the iterator stays stable
_objectList.splice(i->second, _currentObject, _currentObject.begin());
}
const AIBVHPager&
AIManager::getPager() const
{
return _pager;
}
AIBVHPager&
AIManager::getPager()
{
return _pager;
}
} // namespace fgai

77
utils/fgai/AIManager.hxx Normal file
View File

@@ -0,0 +1,77 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef AIManager_hxx
#define AIManager_hxx
#include <simgear/hla/HLAFederate.hxx>
#include "AIBVHPager.hxx"
namespace fgai {
class AIObject;
class AIManager : public simgear::HLAFederate {
public:
AIManager();
virtual ~AIManager();
virtual simgear::HLAObjectClass* createObjectClass(const std::string& name);
virtual bool init();
virtual bool update();
virtual bool shutdown();
void insert(const SGSharedPtr<AIObject>& aiObject);
void schedule(AIObject& object, const SGTimeStamp& simTime);
const SGTimeStamp& getSimTime() const
{ return _simTime; }
const AIBVHPager& getPager() const;
AIBVHPager& getPager();
/// For the list of ai object that are active
typedef std::list<SGSharedPtr<AIObject> > ObjectList;
/// For the schedule of the next update of an ai object
typedef std::map<SGTimeStamp, ObjectList> TimeStampObjectListMap;
typedef std::map<SGTimeStamp, ObjectList::iterator> TimeStampObjectListIteratorMap;
private:
/// The current simulation time
SGTimeStamp _simTime;
/// The maximum time advance step size that is taken
SGTimeStamp _maxStep;
/// Single element list that is used to store the currently worked on
/// ai object. This is a helper for any work method in the object.
ObjectList _currentObject;
/// List of objects that are inserted and need to be initialized
ObjectList _initObjectList;
/// List of running and ready to use objects, is in execution order
ObjectList _objectList;
/// Map of insert points for specific simulation times
TimeStampObjectListIteratorMap _timeStampObjectListIteratorMap;
/// for paging bounding volume trees
AIBVHPager _pager;
};
} // namespace fgai
#endif

103
utils/fgai/AIObject.cxx Normal file
View File

@@ -0,0 +1,103 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "AIObject.hxx"
#include <simgear/bvh/BVHLineSegmentVisitor.hxx>
#include <simgear/bvh/BVHNode.hxx>
#include <simgear/math/SGGeometry.hxx>
#include "AIManager.hxx"
namespace fgai {
AIObject::AIObject() :
_environment(new AIEnvironment),
_subsystemGroup(new AISubsystemGroup)
{
}
AIObject::~AIObject()
{
}
void
AIObject::init(AIManager& manager)
{
_simTime = manager.getSimTime();
}
void
AIObject::update(AIManager& manager, const SGTimeStamp& simTime)
{
_simTime = simTime;
}
void
AIObject::shutdown(AIManager& manager)
{
_simTime = SGTimeStamp();
}
void
AIObject::setGroundCache(const AIPhysics& physics, AIBVHPager& pager, const SGTimeStamp& dt)
{
SGVec3d point = physics.getLocation().getPosition();
double linearVelocity = norm(physics.getLinearBodyVelocity());
// The 2 is a security factor for accelerations, but at least 100 meters
double radius = std::max(100.0, 2*dt.toSecs()*linearVelocity);
SGSphered requiredSphere(point, radius);
/// Are we already good enough?
if (requiredSphere.inside(_querySphere))
return;
// Now query something somehow bigger to avoid querying again in the next frame
SGSphered sphere(point, 4*radius);
_node = pager.getBoundingVolumes(sphere);
if (!_node.valid())
return;
_querySphere = sphere;
}
bool
AIObject::getGroundIntersection(SGVec3d& point, SGVec3d& normal, const SGLineSegmentd& lineSegment) const
{
if (!_node.valid())
return false;
simgear::BVHLineSegmentVisitor lineSegmentVisitor(lineSegment);
_node->accept(lineSegmentVisitor);
if (lineSegmentVisitor.empty())
return false;
normal = lineSegmentVisitor.getNormal();
point = lineSegmentVisitor.getPoint();
return true;
}
bool
AIObject::getGroundIntersection(SGPlaned& plane, const SGLineSegmentd& lineSegment) const
{
SGVec3d point;
SGVec3d normal;
if (!getGroundIntersection(point, normal, lineSegment))
return false;
plane = SGPlaned(normal, point);
return true;
}
} // namespace fgai

92
utils/fgai/AIObject.hxx Normal file
View File

@@ -0,0 +1,92 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef AIObject_hxx
#define AIObject_hxx
#include <list>
#include <string>
#include <simgear/math/SGGeometry.hxx>
#include <simgear/structure/SGWeakReferenced.hxx>
#include <simgear/timing/timestamp.hxx>
#include "AIEnvironment.hxx"
#include "AIManager.hxx"
#include "AIPhysics.hxx"
namespace fgai {
class AIBVHPager;
class AIObject : public SGWeakReferenced {
public:
AIObject();
virtual ~AIObject();
// also register the required hla objects here
virtual void init(AIManager& manager);
virtual void update(AIManager& manager, const SGTimeStamp& simTime);
virtual void shutdown(AIManager& manager);
void setGroundCache(const AIPhysics& physics, AIBVHPager& pager, const SGTimeStamp& dt);
bool getGroundIntersection(SGVec3d& point, SGVec3d& normal, const SGLineSegmentd& lineSegment) const;
bool getGroundIntersection(SGPlaned& plane, const SGLineSegmentd& lineSegment) const;
const SGTimeStamp& getSimTime() const
{ return _simTime; }
const AIEnvironment& getEnvironment() const
{ return *_environment; }
AIEnvironment& getEnvironment()
{ return *_environment; }
const AISubsystemGroup& getSubsystemGroup() const
{ return *_subsystemGroup; }
AISubsystemGroup& getSubsystemGroup()
{ return *_subsystemGroup; }
const AIPhysics& getPhysics() const
{ return *_physics; }
AIPhysics& getPhysics()
{ return *_physics; }
protected:
void setPhysics(AIPhysics* physics)
{ _physics = physics; }
private:
friend class AIManager;
// The simulation time
SGTimeStamp _simTime;
// The iterator to our own list entry in the manager class
AIManager::ObjectList::iterator _objectListIterator;
// The components we have for an ai object
SGSharedPtr<AIEnvironment> _environment;
SGSharedPtr<AISubsystemGroup> _subsystemGroup;
SGSharedPtr<AIPhysics> _physics;
/// The equivalent of the ground cache for FGInterface.
SGSharedPtr<simgear::BVHNode> _node;
/// The sphere we really queried for the ground cache
SGSphered _querySphere;
};
} // namespace fgai
#endif

104
utils/fgai/AIPhysics.cxx Normal file
View File

@@ -0,0 +1,104 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "AIPhysics.hxx"
#include <simgear/math/SGGeometry.hxx>
#include "AIEnvironment.hxx"
namespace fgai {
AIPhysics::AIPhysics(const AIPhysics& physics) :
_location(physics._location),
_linearBodyVelocity(physics._linearBodyVelocity),
_angularBodyVelocity(physics._angularBodyVelocity),
_geodPosition(physics._geodPosition),
_horizontalLocalOrientation(physics._horizontalLocalOrientation)
{
}
AIPhysics::AIPhysics(const SGLocationd& location, const SGVec3d& linearBodyVelocity,
const SGVec3d& angularBodyVelocity) :
_location(location),
_linearBodyVelocity(linearBodyVelocity),
_angularBodyVelocity(angularBodyVelocity)
{
_geodPosition = SGGeod::fromCart(_location.getPosition());
_horizontalLocalOrientation = SGQuatd::fromLonLat(_geodPosition);
}
AIPhysics::~AIPhysics()
{
}
void
AIPhysics::update(AIObject& object, const SGTimeStamp& dt)
{
advanceByBodyVelocity(dt.toSecs(), _linearBodyVelocity, _angularBodyVelocity);
}
void
AIPhysics::advanceByBodyAcceleration(const double& dt,
const SGVec3d& linearAcceleration,
const SGVec3d& angularAcceleration)
{
// The current linear and angular velocity
SGVec3d linearVelocity = getLinearBodyVelocity();
SGVec3d angularVelocity = getAngularBodyVelocity();
// an euler step for the velocities, the positions get upgraded below
linearVelocity += dt*linearAcceleration;
angularVelocity += dt*angularAcceleration;
advanceByBodyVelocity(dt, linearVelocity, angularVelocity);
}
void
AIPhysics::advanceByBodyVelocity(const double& dt,
const SGVec3d& linearVelocity,
const SGVec3d& angularVelocity)
{
// Do an euler step with the derivatives at mSimTime
_location.eulerStepBodyVelocities(dt, _linearBodyVelocity, _angularBodyVelocity);
_geodPosition = SGGeod::fromCart(_location.getPosition());
_horizontalLocalOrientation = SGQuatd::fromLonLat(_geodPosition);
// Store the new velocities for the next interval at mSimTim + dt
_linearBodyVelocity = linearVelocity;
_angularBodyVelocity = angularVelocity;
}
void
AIPhysics::advanceToLocation(const double& dt, const SGLocationd& location)
{
// At first we need to move along with the announced velocities, so:
// Do an euler step with the derivatives at mSimTime.
_location.eulerStepBodyVelocities(dt, _linearBodyVelocity, _angularBodyVelocity);
_geodPosition = SGGeod::fromCart(_location.getPosition());
_horizontalLocalOrientation = SGQuatd::fromLonLat(_geodPosition);
// Now compute velocities that will move to the desired position, orientation if the next
// advance method is called with the same dt value
SGVec3d positionDifference = location.getPosition() - _location.getPosition();
_linearBodyVelocity = (1/dt)*_location.getOrientation().transform(positionDifference);
_angularBodyVelocity = SGQuatd::forwardDifferenceVelocity(_location.getOrientation(), location.getOrientation(), dt);
}
} // namespace fgai

108
utils/fgai/AIPhysics.hxx Normal file
View File

@@ -0,0 +1,108 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef AIPhysics_hxx
#define AIPhysics_hxx
#include <simgear/math/SGMath.hxx>
#include "AISubsystem.hxx"
namespace fgai {
class AIPhysics : public AISubsystem {
public:
/// Initial conditions need to be set at creation time.
/// Just setting the position underway will result in unphysical motion.
AIPhysics(const AIPhysics& physics);
AIPhysics(const SGLocationd& location, const SGVec3d& linearBodyVelocity = SGVec3d::zeros(),
const SGVec3d& angularBodyVelocity = SGVec3d::zeros());
virtual ~AIPhysics();
/// The default is unaccelerated movement
virtual void update(AIObject& object, const SGTimeStamp& dt);
/// The current state
const SGLocationd& getLocation() const
{ return _location; }
const SGVec3d& getPosition() const
{ return _location.getPosition(); }
const SGQuatd& getOrientation() const
{ return _location.getOrientation(); }
const SGGeod& getGeodPosition() const
{ return _geodPosition; }
const SGQuatd& getHorizontalLocalOrientation() const
{ return _horizontalLocalOrientation; }
/// The orientation of the body wrt the geodetic ned coordinate system
SGQuatd getGeodOrientation() const
{ return inverse(_horizontalLocalOrientation)*_location.getOrientation(); }
const SGVec3d& getLinearBodyVelocity() const
{ return _linearBodyVelocity; }
const SGVec3d& getAngularBodyVelocity() const
{ return _angularBodyVelocity; }
/// The velocity in global cartesian coordinates
SGVec3d getLinearVelocity() const
{ return _location.getOrientation().backTransform(_linearBodyVelocity); }
/// The velocity in the north east down coordinate system
/// Note that this gets undefined at the poles.
SGVec3d getGeodVelocity() const
{ return getGeodOrientation().backTransform(getLinearBodyVelocity()); }
double getDownVelocity() const
{ return getGeodVelocity()[2]; }
SGVec2d getHorizontalVelocity() const
{ SGVec3d v = getGeodVelocity(); return SGVec2d(v[0], v[1]); }
protected:
/// The below methods change the position and velocity of the vehicle
/// in a way that is consistent in that it matches position, velocity
/// values to keep the velocity a numerical derivative of the position.
/// Given the accelerations at the current simulation time mSimTime,
/// update the position and velocity to mSimTime + dt.
/// This is the primary advance mode for physically simulated motion.
/// Compute the forces on the single body, compute the accelerations
/// from the forces by newtons law and accelerate by this amount.
void advanceByBodyAcceleration(const double& dt,
const SGVec3d& linearAcceleration,
const SGVec3d& angularAcceleration);
/// Given the velocities at the next simulation time mSimTime,
/// update the position and velocity to mSimTime + dt.
void advanceByBodyVelocity(const double& dt,
const SGVec3d& linearVelocity,
const SGVec3d& angularVelocity);
/// Given the desired position and orientation, a pair of velocities
/// is computed to reach that position and orientation in the
/// next advance step. This one advance step latency is important
/// for other participants to correctly extrapolate the position
/// and orientation based on the velocities.
/// Note that this only works when the next update is called with
/// the same time increment.
void advanceToLocation(const double& dt, const SGLocationd& location);
private:
AIPhysics& operator=(const AIPhysics& physics);
SGLocationd _location;
SGVec3d _linearBodyVelocity;
SGVec3d _angularBodyVelocity;
SGGeod _geodPosition;
SGQuatd _horizontalLocalOrientation;
};
} // namespace fgai
#endif

View File

@@ -0,0 +1,60 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef AISubsystem_hxx
#define AISubsystem_hxx
#include <list>
#include <simgear/structure/SGWeakReferenced.hxx>
#include <simgear/timing/timestamp.hxx>
namespace fgai {
class AIObject;
class AISubsystem : public SGWeakReferenced {
public:
AISubsystem()
{ }
virtual ~AISubsystem()
{ }
virtual void update(AIObject& object, const SGTimeStamp& dt) = 0;
};
class AISubsystemGroup : public AISubsystem {
public:
AISubsystemGroup()
{ }
virtual ~AISubsystemGroup()
{ }
virtual void update(AIObject& object, const SGTimeStamp& dt)
{
for (SubsystemList::iterator i = _subsystemList.begin();
i != _subsystemList.end(); ++i) {
(*i)->update(object, dt);
}
}
private:
typedef std::list<SGSharedPtr<AISubsystem> > SubsystemList;
SubsystemList _subsystemList;
};
} // namespace fgai
#endif

27
utils/fgai/CMakeLists.txt Normal file
View File

@@ -0,0 +1,27 @@
add_executable(fgai
fgai.cxx
HLAAircraft.cxx
HLAAircraftClass.cxx
HLAAirVehicle.cxx
HLAAirVehicleClass.cxx
AIBVHPager.cxx
AIEnvironment.cxx
AIManager.cxx
AIObject.cxx
AIPhysics.cxx
HLABaloon.cxx
HLABaloonClass.cxx
HLAMPAircraft.cxx
HLAMPAircraftClass.cxx
HLASceneObject.cxx
HLASceneObjectClass.cxx
)
target_link_libraries(fgai
SimGearCore SimGearScene
${OPENSCENEGRAPH_LIBRARIES}
${OPENGL_LIBRARIES}
${RTI_LDFLAGS}
)
install(TARGETS fgai RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

View File

@@ -0,0 +1,49 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "HLAAirVehicle.hxx"
#include "HLAAirVehicleClass.hxx"
namespace fgai {
HLAAirVehicle::HLAAirVehicle(HLAAirVehicleClass* objectClass) :
HLASceneObject(objectClass)
{
}
HLAAirVehicle::~HLAAirVehicle()
{
}
void
HLAAirVehicle::createAttributeDataElements()
{
HLASceneObject::createAttributeDataElements();
assert(dynamic_cast<HLAAirVehicleClass*>(getObjectClass().get()));
HLAAirVehicleClass& objectClass = static_cast<HLAAirVehicleClass&>(*getObjectClass());
setAttributeDataElement(objectClass.getCallSignIndex(), _callSign.getDataElement());
// setAttributeDataElement(objectClass.getSquawkIndex(), _squawk.getDataElement());
}
} // namespace fgai

View File

@@ -0,0 +1,54 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef HLAAirVehicle_hxx
#define HLAAirVehicle_hxx
#include <simgear/hla/HLAArrayDataElement.hxx>
#include <simgear/hla/HLABasicDataElement.hxx>
#include "HLASceneObject.hxx"
namespace fgai {
class HLAAirVehicleClass;
class HLAAirVehicle : public HLASceneObject {
public:
HLAAirVehicle(HLAAirVehicleClass* objectClass = 0);
virtual ~HLAAirVehicle();
virtual void createAttributeDataElements();
void setCallSign(const std::string& callSign)
{ _callSign.setValue(callSign); }
const std::string& getCallSign() const
{ return _callSign.getValue(); }
/// FIXME encode Mode a/b/c/whatever into a variant and make this backward compatible for all time!!!
// void setTransponder(unsigned short transponder)
// { _transponder.setValue(transponder); }
// unsigned short getTransponder() const
// { return _transponder.getValue(); }
private:
simgear::HLAStringData _callSign;
// simgear::HLAUShortData _transponder;
};
} // namespace fgai
#endif

View File

@@ -0,0 +1,54 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "HLAAirVehicleClass.hxx"
#include "HLAAirVehicle.hxx"
namespace fgai {
HLAAirVehicleClass::HLAAirVehicleClass(const std::string& name, simgear::HLAFederate* federate) :
HLASceneObjectClass(name, federate)
{
}
HLAAirVehicleClass::~HLAAirVehicleClass()
{
}
simgear::HLAObjectInstance*
HLAAirVehicleClass::createObjectInstance(const std::string& name)
{
return new HLAAirVehicle(this);
}
void
HLAAirVehicleClass::createAttributeDataElements(simgear::HLAObjectInstance& objectInstance)
{
/// FIXME resolve these indices somewhere else!
if (_callSignIndex.empty())
_callSignIndex = getDataElementIndex("callSign");
if (_transponderIndex.empty())
_transponderIndex = getDataElementIndex("transponder");
HLASceneObjectClass::createAttributeDataElements(objectInstance);
}
} // namespace fgai

View File

@@ -0,0 +1,52 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef HLAAirVehicleClass_hxx
#define HLAAirVehicleClass_hxx
#include "HLASceneObjectClass.hxx"
namespace fgai {
class HLAAirVehicleClass : public HLASceneObjectClass {
public:
HLAAirVehicleClass(const std::string& name, simgear::HLAFederate* federate);
virtual ~HLAAirVehicleClass();
/// Create a new instance of this class.
virtual simgear::HLAObjectInstance* createObjectInstance(const std::string& name);
virtual void createAttributeDataElements(simgear::HLAObjectInstance& objectInstance);
bool setCallSignIndex(const std::string& path)
{ return getDataElementIndex(_callSignIndex, path); }
const simgear::HLADataElementIndex& getCallSignIndex() const
{ return _callSignIndex; }
bool setTransponderIndex(const std::string& path)
{ return getDataElementIndex(_transponderIndex, path); }
const simgear::HLADataElementIndex& getTransponderIndex() const
{ return _transponderIndex; }
private:
simgear::HLADataElementIndex _callSignIndex;
simgear::HLADataElementIndex _transponderIndex;
};
} // namespace fgai
#endif

View File

@@ -0,0 +1,50 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "HLAAircraft.hxx"
#include "HLAAircraftClass.hxx"
namespace fgai {
HLAAircraft::HLAAircraft(HLAAircraftClass* objectClass) :
HLAAirVehicle(objectClass)
{
}
HLAAircraft::~HLAAircraft()
{
}
void
HLAAircraft::createAttributeDataElements()
{
HLAAirVehicle::createAttributeDataElements();
assert(dynamic_cast<HLAAircraftClass*>(getObjectClass().get()));
//HLAAircraftClass& objectClass = static_cast<HLAAircraftClass&>(*getObjectClass());
// setAttributeDataElement(objectClass.getModelPathIndex(), _modelPath.getDataElement());
// setAttributeDataElement(objectClass.getModelLiveryIndex(), _modelLivery.getDataElement());
// setAttributeDataElement(objectClass.getSimTimeIndex(), _simTime.getDataElement());
}
} // namespace fgai

View File

@@ -0,0 +1,46 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef HLAAircraft_hxx
#define HLAAircraft_hxx
#include <simgear/hla/HLAArrayDataElement.hxx>
#include "HLAAirVehicle.hxx"
namespace fgai {
class HLAAircraftClass;
class HLAAircraft : public HLAAirVehicle {
public:
HLAAircraft(HLAAircraftClass* objectClass = 0);
virtual ~HLAAircraft();
virtual void createAttributeDataElements();
private:
// // The model stuff
// simgear::HLAStringData _modelPath;
// simgear::HLAStringData _modelLivery;
// // The current simTime
// simgear::HLADoubleData _simTime;
};
} // namespace fgai
#endif

View File

@@ -0,0 +1,52 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "HLAAircraftClass.hxx"
#include "HLAAircraft.hxx"
namespace fgai {
HLAAircraftClass::HLAAircraftClass(const std::string& name, simgear::HLAFederate* federate) :
HLAAirVehicleClass(name, federate)
{
}
HLAAircraftClass::~HLAAircraftClass()
{
}
simgear::HLAObjectInstance*
HLAAircraftClass::createObjectInstance(const std::string& name)
{
return new HLAAircraft(this);
}
void
HLAAircraftClass::createAttributeDataElements(simgear::HLAObjectInstance& objectInstance)
{
/// FIXME resolve these indices somewhere else!
// if (_simTimeIndex.empty())
// _simTimeIndex = getDataElementIndex("simTime");
HLAAirVehicleClass::createAttributeDataElements(objectInstance);
}
} // namespace fgai

View File

@@ -0,0 +1,46 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef HLAAircraftClass_hxx
#define HLAAircraftClass_hxx
#include "HLAAirVehicleClass.hxx"
namespace fgai {
class HLAAircraftClass : public HLAAirVehicleClass {
public:
HLAAircraftClass(const std::string& name, simgear::HLAFederate* federate);
virtual ~HLAAircraftClass();
/// Create a new instance of this class.
virtual simgear::HLAObjectInstance* createObjectInstance(const std::string& name);
virtual void createAttributeDataElements(simgear::HLAObjectInstance& objectInstance);
// bool setSimTimeIndex(const std::string& path)
// { return getDataElementIndex(_simTimeIndex, path); }
// const simgear::HLADataElementIndex& getSimTimeIndex() const
// { return _simTimeIndex; }
private:
// simgear::HLADataElementIndex _simTimeIndex;
};
} // namespace fgai
#endif

50
utils/fgai/HLABaloon.cxx Normal file
View File

@@ -0,0 +1,50 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "HLABaloon.hxx"
#include "HLABaloonClass.hxx"
namespace fgai {
HLABaloon::HLABaloon(HLABaloonClass* objectClass) :
HLAAirVehicle(objectClass)
{
}
HLABaloon::~HLABaloon()
{
}
void
HLABaloon::createAttributeDataElements()
{
HLAAirVehicle::createAttributeDataElements();
assert(dynamic_cast<HLABaloonClass*>(getObjectClass().get()));
//HLABaloonClass& objectClass = static_cast<HLABaloonClass&>(*getObjectClass());
// setAttributeDataElement(objectClass.getModelPathIndex(), _modelPath.getDataElement());
// setAttributeDataElement(objectClass.getModelLiveryIndex(), _modelLivery.getDataElement());
// setAttributeDataElement(objectClass.getSimTimeIndex(), _simTime.getDataElement());
}
} // namespace fgai

41
utils/fgai/HLABaloon.hxx Normal file
View File

@@ -0,0 +1,41 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef HLABaloon_hxx
#define HLABaloon_hxx
#include "HLAAirVehicle.hxx"
namespace fgai {
class HLABaloonClass;
class HLABaloon : public HLAAirVehicle {
public:
HLABaloon(HLABaloonClass* objectClass = 0);
virtual ~HLABaloon();
virtual void createAttributeDataElements();
private:
// // The current simTime
// simgear::HLADoubleData _simTime;
};
} // namespace fgai
#endif

View File

@@ -0,0 +1,52 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "HLABaloonClass.hxx"
#include "HLABaloon.hxx"
namespace fgai {
HLABaloonClass::HLABaloonClass(const std::string& name, simgear::HLAFederate* federate) :
HLAAirVehicleClass(name, federate)
{
}
HLABaloonClass::~HLABaloonClass()
{
}
simgear::HLAObjectInstance*
HLABaloonClass::createObjectInstance(const std::string& name)
{
return new HLABaloon(this);
}
void
HLABaloonClass::createAttributeDataElements(simgear::HLAObjectInstance& objectInstance)
{
/// FIXME resolve these indices somewhere else!
// if (_simTimeIndex.empty())
// _simTimeIndex = getDataElementIndex("simTime");
HLAAirVehicleClass::createAttributeDataElements(objectInstance);
}
} // namespace fgai

View File

@@ -0,0 +1,46 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef HLABaloonClass_hxx
#define HLABaloonClass_hxx
#include "HLAAirVehicleClass.hxx"
namespace fgai {
class HLABaloonClass : public HLAAirVehicleClass {
public:
HLABaloonClass(const std::string& name, simgear::HLAFederate* federate);
virtual ~HLABaloonClass();
/// Create a new instance of this class.
virtual simgear::HLAObjectInstance* createObjectInstance(const std::string& name);
virtual void createAttributeDataElements(simgear::HLAObjectInstance& objectInstance);
// bool setSimTimeIndex(const std::string& path)
// { return getDataElementIndex(_simTimeIndex, path); }
// const simgear::HLADataElementIndex& getSimTimeIndex() const
// { return _simTimeIndex; }
private:
// simgear::HLADataElementIndex _simTimeIndex;
};
} // namespace fgai
#endif

View File

@@ -0,0 +1,50 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "HLAMPAircraft.hxx"
#include "HLAMPAircraftClass.hxx"
namespace fgai {
HLAMPAircraft::HLAMPAircraft(HLAMPAircraftClass* objectClass) :
HLASceneObject(objectClass)
{
}
HLAMPAircraft::~HLAMPAircraft()
{
}
void
HLAMPAircraft::createAttributeDataElements()
{
HLASceneObject::createAttributeDataElements();
assert(dynamic_cast<HLAMPAircraftClass*>(getObjectClass().get()));
HLAMPAircraftClass& objectClass = static_cast<HLAMPAircraftClass&>(*getObjectClass());
setAttributeDataElement(objectClass.getModelPathIndex(), _modelPath.getDataElement());
setAttributeDataElement(objectClass.getModelLiveryIndex(), _modelLivery.getDataElement());
setAttributeDataElement(objectClass.getSimTimeIndex(), _simTime.getDataElement());
}
} // namespace fgai

View File

@@ -0,0 +1,62 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef HLAMPAircraft_hxx
#define HLAMPAircraft_hxx
#include <simgear/hla/HLAArrayDataElement.hxx>
#include "HLASceneObject.hxx"
namespace fgai {
class HLAMPAircraftClass;
class HLAMPAircraft : public HLASceneObject {
public:
HLAMPAircraft(HLAMPAircraftClass* objectClass = 0);
virtual ~HLAMPAircraft();
virtual void createAttributeDataElements();
void setModelPath(const std::string& modelPath)
{ _modelPath.setValue(modelPath); }
const std::string& getModelPath() const
{ return _modelPath.getValue(); }
void setModelLivery(const std::string& modelLivery)
{ _modelLivery.setValue(modelLivery); }
const std::string& getModelLivery() const
{ return _modelLivery.getValue(); }
/// FIXME feed this by the simtime of the positon?!
void setSimTime(double simTime)
{ _simTime.setValue(simTime); }
double getSimTime() const
{ return _simTime.getValue(); }
private:
// The model stuff
simgear::HLAStringData _modelPath;
simgear::HLAStringData _modelLivery;
// The current simTime
simgear::HLADoubleData _simTime;
};
} // namespace fgai
#endif

View File

@@ -0,0 +1,56 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "HLAMPAircraftClass.hxx"
#include "HLAMPAircraft.hxx"
namespace fgai {
HLAMPAircraftClass::HLAMPAircraftClass(const std::string& name, simgear::HLAFederate* federate) :
HLASceneObjectClass(name, federate)
{
}
HLAMPAircraftClass::~HLAMPAircraftClass()
{
}
simgear::HLAObjectInstance*
HLAMPAircraftClass::createObjectInstance(const std::string&)
{
return new HLAMPAircraft(this);
}
void
HLAMPAircraftClass::createAttributeDataElements(simgear::HLAObjectInstance& objectInstance)
{
/// FIXME resolve these indices somewhere else!
if (_modelPathIndex.empty())
_modelPathIndex = getDataElementIndex("model.path");
if (_modelLiveryIndex.empty())
_modelLiveryIndex = getDataElementIndex("model.livery");
if (_simTimeIndex.empty())
_simTimeIndex = getDataElementIndex("simTime");
HLASceneObjectClass::createAttributeDataElements(objectInstance);
}
} // namespace fgai

View File

@@ -0,0 +1,59 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef HLAMPAircraftClass_hxx
#define HLAMPAircraftClass_hxx
#include "HLASceneObjectClass.hxx"
namespace fgai {
/// For compatibility with the old mp stuff??!!
class HLAMPAircraftClass : public HLASceneObjectClass {
public:
HLAMPAircraftClass(const std::string& name, simgear::HLAFederate* federate);
virtual ~HLAMPAircraftClass();
/// Create a new instance of this class.
virtual simgear::HLAObjectInstance* createObjectInstance(const std::string& name);
virtual void createAttributeDataElements(simgear::HLAObjectInstance& objectInstance);
bool setModelPathIndex(const std::string& path)
{ return getDataElementIndex(_modelPathIndex, path); }
const simgear::HLADataElementIndex& getModelPathIndex() const
{ return _modelPathIndex; }
bool setModelLiveryIndex(const std::string& path)
{ return getDataElementIndex(_modelLiveryIndex, path); }
const simgear::HLADataElementIndex& getModelLiveryIndex() const
{ return _modelLiveryIndex; }
bool setSimTimeIndex(const std::string& path)
{ return getDataElementIndex(_simTimeIndex, path); }
const simgear::HLADataElementIndex& getSimTimeIndex() const
{ return _simTimeIndex; }
private:
simgear::HLADataElementIndex _modelPathIndex;
simgear::HLADataElementIndex _modelLiveryIndex;
simgear::HLADataElementIndex _simTimeIndex;
};
} // namespace fgai
#endif

View File

@@ -0,0 +1,67 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "HLASceneObject.hxx"
#include <simgear/hla/HLAArrayDataElement.hxx>
#include "HLASceneObjectClass.hxx"
#include "AIPhysics.hxx"
namespace fgai {
HLASceneObject::HLASceneObject(HLASceneObjectClass* objectClass) :
HLAObjectInstance(objectClass)
{
}
HLASceneObject::~HLASceneObject()
{
}
void
HLASceneObject::createAttributeDataElements()
{
/// FIXME at some point we should not need that anymore
HLAObjectInstance::createAttributeDataElements();
assert(dynamic_cast<HLASceneObjectClass*>(getObjectClass().get()));
HLASceneObjectClass& objectClass = static_cast<HLASceneObjectClass&>(*getObjectClass());
simgear::HLACartesianLocation* location = new simgear::HLACartesianLocation;
setAttributeDataElement(objectClass.getPositionIndex(), location->getPositionDataElement());
setAttributeDataElement(objectClass.getOrientationIndex(), location->getOrientationDataElement());
setAttributeDataElement(objectClass.getAngularVelocityIndex(), location->getAngularVelocityDataElement());
setAttributeDataElement(objectClass.getLinearVelocityIndex(), location->getLinearVelocityDataElement());
_location = location;
}
void
HLASceneObject::setLocation(const AIPhysics& physics)
{
if (!_location.valid())
return;
/// In the long term do not just copy, but also decide how long to skip updates to the hla attributes ...
_location->setLocation(physics.getLocation());
_location->setAngularBodyVelocity(physics.getAngularBodyVelocity());
_location->setLinearBodyVelocity(physics.getLinearBodyVelocity());
}
} // namespace fgai

View File

@@ -0,0 +1,45 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef HLASceneObject_hxx
#define HLASceneObject_hxx
#include <simgear/hla/HLAObjectClass.hxx>
#include <simgear/hla/HLALocation.hxx>
namespace fgai {
class AIPhysics;
class HLASceneObjectClass;
class HLASceneObject : public simgear::HLAObjectInstance {
public:
HLASceneObject(HLASceneObjectClass* objectClass = 0);
virtual ~HLASceneObject();
virtual void createAttributeDataElements();
void setLocation(const AIPhysics& physics);
private:
// The location of this object
SGSharedPtr<simgear::HLAAbstractLocation> _location;
};
} // namespace fgai
#endif

View File

@@ -0,0 +1,58 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "HLASceneObjectClass.hxx"
#include "HLASceneObject.hxx"
namespace fgai {
HLASceneObjectClass::HLASceneObjectClass(const std::string& name, simgear::HLAFederate* federate) :
HLAObjectClass(name, federate)
{
}
HLASceneObjectClass::~HLASceneObjectClass()
{
}
simgear::HLAObjectInstance*
HLASceneObjectClass::createObjectInstance(const std::string& name)
{
return new HLASceneObject(this);
}
void
HLASceneObjectClass::createAttributeDataElements(simgear::HLAObjectInstance& objectInstance)
{
/// FIXME resolve these indices somewhere else!
if (_positionIndex.empty())
_positionIndex = getDataElementIndex("location.position");
if (_orientationIndex.empty())
_orientationIndex = getDataElementIndex("location.orientation");
if (_angularVelocityIndex.empty())
_angularVelocityIndex = getDataElementIndex("velocity.angular");
if (_linearVelocityIndex.empty())
_linearVelocityIndex = getDataElementIndex("velocity.linear");
HLAObjectClass::createAttributeDataElements(objectInstance);
}
} // namespace fgai

View File

@@ -0,0 +1,67 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef HLASceneObjectClass_hxx
#define HLASceneObjectClass_hxx
#include <string>
#include <simgear/hla/HLAObjectClass.hxx>
namespace fgai {
class HLASceneObjectClass : public simgear::HLAObjectClass {
public:
HLASceneObjectClass(const std::string& name, simgear::HLAFederate* federate);
virtual ~HLASceneObjectClass();
/// Create a new instance of this class.
virtual simgear::HLAObjectInstance* createObjectInstance(const std::string& name);
virtual void createAttributeDataElements(simgear::HLAObjectInstance& objectInstance);
bool setPositionIndex(const std::string& path)
{ return getDataElementIndex(_positionIndex, path); }
const simgear::HLADataElementIndex& getPositionIndex() const
{ return _positionIndex; }
bool setOrientationIndex(const std::string& path)
{ return getDataElementIndex(_orientationIndex, path); }
const simgear::HLADataElementIndex& getOrientationIndex() const
{ return _orientationIndex; }
bool setAngularVelocityIndex(const std::string& path)
{ return getDataElementIndex(_angularVelocityIndex, path); }
const simgear::HLADataElementIndex& getAngularVelocityIndex() const
{ return _angularVelocityIndex; }
bool setLinearVelocityIndex(const std::string& path)
{ return getDataElementIndex(_linearVelocityIndex, path); }
const simgear::HLADataElementIndex& getLinearVelocityIndex() const
{ return _linearVelocityIndex; }
private:
/// FIXME use a location factory for that?!
simgear::HLADataElementIndex _positionIndex;
simgear::HLADataElementIndex _orientationIndex;
simgear::HLADataElementIndex _angularVelocityIndex;
simgear::HLADataElementIndex _linearVelocityIndex;
};
} // namespace fgai
#endif

608
utils/fgai/fgai.cxx Normal file
View File

@@ -0,0 +1,608 @@
// Copyright (C) 2009 - 2012 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <cstdio>
#include <simgear/misc/sg_path.hxx>
#include <simgear/debug/logstream.hxx>
#include "AIObject.hxx"
#include "AIManager.hxx"
#include "HLAMPAircraft.hxx"
#include "HLAMPAircraftClass.hxx"
#include "AIPhysics.hxx"
namespace fgai {
#if 0
class AIVehiclePhysics : public AIPhysics {
public:
AIVehiclePhysics(const SGLocationd& location, const SGVec3d& linearBodyVelocity = SGVec3d::zeros(),
const SGVec3d& angularBodyVelocity = SGVec3d::zeros()) :
AIPhysics(location, linearBodyVelocity, angularBodyVelocity)
{
setMass(1);
setInertia(1, 1, 1);
}
virtual ~AIVehiclePhysics()
{ }
double getMass() const
{ return _mass; }
void setMass(double mass)
{ _mass = mass; }
void setInertia(double ixx, double iyy, double izz, double ixy = 0, double ixz = 0, double iyz = 0)
{
_inertia = SGMatrixd(ixx, ixy, ixz, 0,
ixy, iyy, iyz, 0,
ixz, iyz, izz, 0,
0, 0, 0, 1);
invert(_inertiaInverse, _inertia);
}
protected:
void advanceByBodyForce(const double& dt,
const SGVec3d& force,
const SGVec3d& torque)
{
SGVec3d linearVelocity = getLinearBodyVelocity();
SGVec3d angularVelocity = getAngularBodyVelocity();
SGVec3d linearAcceleration = (1/_mass)*(force - cross(angularVelocity, linearVelocity));
SGVec3d angularAcceleration = _inertiaInverse.xformVec(torque - cross(angularVelocity, _inertia.xformVec(angularVelocity)));
advanceByBodyAcceleration(dt, linearAcceleration, angularAcceleration);
}
SGVec3d getGravityAcceleration() const
{
return SGQuatd::fromLonLat(getGeodPosition()).backTransform(SGVec3d(0, 0, 9.81));
}
private:
// unsimulated motion, hide this for this kind of class here
using AIPhysics::advanceByBodyAcceleration;
using AIPhysics::advanceByBodyVelocity;
using AIPhysics::advanceToCartPosition;
double _mass;
// FIXME this is a symmetric 3x3 matrix ...
SGMatrixd _inertia;
SGMatrixd _inertiaInverse;
};
// FIXME Totally unfinished simple aerodynamics model for an ai aircraft
// also just here for a sketch of an idea
class AIAircraftPhysics : public AIVehiclePhysics {
public:
AIAircraftPhysics(const SGLocationd& location, const SGVec3d& linearBodyVelocity = SGVec3d::zeros(),
const SGVec3d& angularBodyVelocity = SGVec3d::zeros()) :
AIVehiclePhysics(location, linearBodyVelocity, angularBodyVelocity)
{ }
virtual ~AIAircraftPhysics()
{ }
double getElevatorPosition() const
{ return _elevatorPosition; }
void setElevatorPosition(double elevatorPosition)
{ _elevatorPosition = elevatorPosition; }
double getAileronPosition() const
{ return _aileronPosition; }
void setAileronPosition(double aileronPosition)
{ _aileronPosition = aileronPosition; }
double getRudderPosition() const
{ return _rudderPosition; }
void setRudderPosition(double rudderPosition)
{ _rudderPosition = rudderPosition; }
// double getFlapsPosition() const
// { return _flapsPosition; }
// void setFlapsPosition(double flapsPosition)
// { _flapsPosition = flapsPosition; }
double getThrust() const
{ return _thrust; }
void setThrust(double thrust)
{ _thrust = thrust; }
virtual void update(AIObject& object, const SGTimeStamp& dt)
{
// const AIEnvironment& environment = object.getEnvironment();
const AIEnvironment environment;
/// Compute the forces on the aircraft. This is a very simple fdm.
// The velocity of the aircraft wrt the surrounding fluid
SGVec3d windVelocity = getLinearBodyVelocity();
windVelocity -= getOrientation().transform(environment.getWindVelocity());
// The true airspeed of the bird
double airSpeed = norm(windVelocity);
// simple density with(out FIXME) altitude
double density = environment.getDensity();
// The dynaimc pressure - most important value in aerodynamics
double dynamicPressure = 0.5*density*airSpeed*airSpeed;
// The angle of attack and sideslip angle
double alpha = 0, beta = 0;
if (1e-3 < SGMiscd::max(fabs(windVelocity[0]), fabs(windVelocity[2])))
alpha = atan2(windVelocity[2], windVelocity[0]);
double uw = sqrt(windVelocity[0]*windVelocity[0] + windVelocity[2]*windVelocity[2]);
if (1e-3 < SGMiscd::max(fabs(windVelocity[1]), fabs(uw)))
beta = atan2(windVelocity[1], uw);
// Transform from the stability axis to body axis
SGQuatd stabilityToBody = SGQuatd::fromEulerRad(beta, alpha, 0);
// We assume a simple angular dependency for the
// lift, drag and side force coefficients.
// lift for alpha = 0
double _Cl0 = 0.5;
// lift at stall angle of attack
double _ClStall = 2;
// stall angle of attack
double _alphaStall = 18;
// Drag for alpha = 0
double _Cd0 = 0.05;
// Drag for alpha = 90
double _Cd90 = 1.05;
// Side force coefficient for maximum side angle
double _Cs90 = 1.05;
/// FIXME
double _area = 1;
SGVec3d _aerodynamicReferencePoint(0, 0, 0);
SGVec3d _centerOfGravity(0, 0, 0);
// So compute the lift drag and side force coefficient for the
// current stream conditions.
double Cl = _Cl0 + (_ClStall - _Cl0)*sin(SGMiscd::clip(90/_alphaStall*alpha, -0.5*SGMiscd::pi(), SGMiscd::pi()));
double Cd = _Cd0 + (_Cd90 - _Cd0)*(0.5 - 0.5*cos(2*alpha));
double Cs = _Cs90*sin(beta);
// Forces in the stability axes
double lift = Cl*dynamicPressure*_area;
double drag = Cd*dynamicPressure*_area;
double side = Cs*dynamicPressure*_area;
// Torque in body axes
double p = 0;
double q = 0;
double r = 0;
// Compute the force in stability coordinates ...
SGVec3d stabilityForce(-drag, side, -lift);
// ... and torque in body coordinates
SGVec3d torque(p, q, r);
SGVec3d force = stabilityToBody.transform(stabilityForce);
torque += cross(force, _aerodynamicReferencePoint);
std::pair<SGVec3d, SGVec3d> velocity;
for (_GearVector::iterator i = _gearVector.begin(); i != _gearVector.end(); ++i) {
std::pair<SGVec3d, SGVec3d> torqueForce;
torqueForce = i->force(getLocation(), velocity, object);
torque += torqueForce.first;
force += torqueForce.second;
}
// Transform the torque back to the center of gravity
torque -= cross(force, _centerOfGravity);
// Advance the equations of motion.
advanceByBodyForce(dt.toSecs(), force, torque);
}
/// The normalized elevator position
double _elevatorPosition;
/// The normalized aileron position
double _aileronPosition;
/// The normalized rudder position
double _rudderPosition;
// /// The normalized flaps position
// double _flapsPosition;
/// Normalized thrust
double _thrust;
struct _Gear {
SGVec3d _position;
SGVec3d _direction;
double _spring;
double _damping;
std::pair<SGVec3d, SGVec3d>
force(const SGLocationd& location, const std::pair<SGVec3d, SGVec3d>& velocity, AIObject& object)
{
SGVec3d start = location.getAbsolutePosition(_position - _direction);
SGVec3d end = location.getAbsolutePosition(_position);
SGLineSegmentd lineSegment(start, end);
SGVec3d point;
SGVec3d normal;
// if (!object.getGroundIntersection(point, normal, lineSegment))
// return std::pair<SGVec3d, SGVec3d>(SGVec3d(0, 0, 0), SGVec3d(0, 0, 0));
// FIXME just now only the spring force ...
// The compression length
double compressLength = norm(point - start);
SGVec3d springForce = -_spring*compressLength*_direction;
SGVec3d dampingForce(0, 0, 0);
SGVec3d force = springForce + dampingForce;
SGVec3d torque(0, 0, 0);
return std::pair<SGVec3d, SGVec3d>(torque, force);
}
};
typedef std::vector<_Gear> _GearVector;
_GearVector _gearVector;
/// The total mass of the bird in kg. No fluel is burned.
/// Some sensible inertia values are derived from the mass.
// double _mass;
/// The thrust to mass ratio which tells us someting about
/// the possible accelerations.
// double _thrustMassRatio;
/// The stall speed
// double _stallSpeed;
// double _maximumSpeed;
// // double _approachSpeed;
// // double _takeoffSpeed;
// // double _cruiseSpeed;
/// The maximum density altitude this aircraft can fly
// double _densityAltitudeLimit;
/// statistical evaluation shows:
/// wingarea = C*wingspan^2, C in [0.1, 0.4], say 0.2
/// ixx = C*wingarea*mass, C in [1e-3, 1e-2]
/// iyy = C*wingarea*mass, C in [1e-2, 0.02]
/// izz = C*wingarea*mass, C in [1e-2, 0.02]
/// Hmm, let's say, weight relates to wingarea?
/// probably, since lift is linear dependent on wingarea
/// So, define a 'size' in meters.
/// the derive
/// wingspan = size
/// wingarea = 0.2*size*size
/// mass = C*wingarea
/// ixx = 0.005*wingarea*mass
/// iyy = 0.05*wingarea*mass
/// izz = 0.05*wingarea*mass
/// an other idea:
/// define a bird of some weight class. That means mass given.
/// Then derive
/// i* ??? must be mass^2 accodring to the thoughts above
/// Then do Cl, Cd, Cs.
/// according to approach speed at sea level with 5 deg aoa and 2,5 deg glideslope and 25 % thrust.
/// according to cruise altitude and cruise speed at 75% thrust compute this at altitude
/// interpolate between these two sets of C*'s based on altitude.
};
#endif
/// An automated lego aircraft, constant linear and angular speed
class AIOgel : public AIObject {
public:
AIOgel(const SGGeod& geod) :
_geod(geod),
_radius(500),
_turnaroundTime(3*60),
_velocity(10)
{ }
virtual ~AIOgel()
{ }
virtual void init(AIManager& manager)
{
AIObject::init(manager);
SGLocationd location(SGVec3d::fromGeod(_geod), SGQuatd::fromLonLat(_geod));
SGVec3d linearVelocity(_velocity, 0, 0);
SGVec3d angularVelocity(0, 0, SGMiscd::twopi()/_turnaroundTime);
setPhysics(new AIPhysics(location, linearVelocity, angularVelocity));
HLAMPAircraftClass* objectClass = dynamic_cast<HLAMPAircraftClass*>(manager.getObjectClass("MPAircraft"));
if (!objectClass)
return;
_objectInstance = new HLAMPAircraft(objectClass);
if (!_objectInstance.valid())
return;
_objectInstance->registerInstance();
_objectInstance->setModelPath("Aircraft/ogel/Models/SinglePiston.xml");
manager.schedule(*this, getSimTime() + SGTimeStamp::fromSecMSec(0, 1));
}
virtual void update(AIManager& manager, const SGTimeStamp& simTime)
{
SGTimeStamp dt = simTime - getSimTime();
setGroundCache(getPhysics(), manager.getPager(), dt);
getEnvironment().update(*this, dt);
getSubsystemGroup().update(*this, dt);
getPhysics().update(*this, dt);
AIObject::update(manager, simTime);
if (!_objectInstance.valid())
return;
_objectInstance->setLocation(getPhysics());
_objectInstance->setSimTime(getSimTime().toSecs());
_objectInstance->updateAttributeValues(getSimTime(), simgear::RTIData("update"));
manager.schedule(*this, getSimTime() + SGTimeStamp::fromSecMSec(0, 100));
}
virtual void shutdown(AIManager& manager)
{
if (_objectInstance.valid())
_objectInstance->removeInstance(simgear::RTIData("shutdown"));
_objectInstance = 0;
AIObject::shutdown(manager);
}
private:
SGGeod _geod;
double _radius;
double _turnaroundTime;
double _velocity;
SGSharedPtr<HLAMPAircraft> _objectInstance;
};
/// an ogle in a traffic circuit at lowi
class AIOgelInTrafficCircuit : public AIObject {
public:
/// Also nothing to really use for a long time, but to demonstrate how it basically works
class Physics : public AIPhysics {
public:
Physics(const SGLocationd& location, const SGVec3d& linearBodyVelocity = SGVec3d::zeros(),
const SGVec3d& angularBodyVelocity = SGVec3d::zeros()) :
AIPhysics(location, linearBodyVelocity, angularBodyVelocity),
_targetVelocity(30),
_gearOffset(2.5)
{ }
virtual ~Physics()
{ }
virtual void update(AIObject& object, const SGTimeStamp& dt)
{
SGVec3d down = getHorizontalLocalOrientation().backTransform(SGVec3d(0, 0, 1));
SGVec3d distToAimingPoint = getAimingPoint() - getPosition();
if (norm(distToAimingPoint - down*dot(down, distToAimingPoint)) <= 10*dt.toSecs()*norm(getLinearVelocity()))
rotateAimingPoint();
SGVec3d aimingVector = normalize(getAimingPoint() - getPosition());
SGVec3d bodyAimingVector = getLocation().getOrientation().transform(aimingVector);
SGVec3d angularVelocity = 0.2*cross(SGVec3d(1, 0, 0), bodyAimingVector);
SGVec3d bodyDownVector = getLocation().getOrientation().transform(down);
// keep an upward orientation
angularVelocity += cross(SGVec3d(0, 0, 1), SGVec3d(0, bodyDownVector[1], bodyDownVector[2]));
SGVec3d linearVelocity(_targetVelocity, 0, 0);
SGVec3d gearPosition = getPosition() + _gearOffset*down;
SGLineSegmentd lineSegment(gearPosition - 10*down, gearPosition + 10*down);
SGVec3d point, normal;
if (object.getGroundIntersection(point, normal, lineSegment)) {
double agl = dot(down, point - gearPosition);
if (agl < 0)
linearVelocity -= down*(0.5*agl/dt.toSecs());
}
advanceByBodyVelocity(dt.toSecs(), linearVelocity, angularVelocity);
}
const SGVec3d& getAimingPoint() const
{ return _waypoints.front(); }
void rotateAimingPoint()
{ _waypoints.splice(_waypoints.end(), _waypoints, _waypoints.begin()); }
std::list<SGVec3d> _waypoints;
double _targetVelocity;
double _gearOffset;
};
AIOgelInTrafficCircuit()
{ }
virtual ~AIOgelInTrafficCircuit()
{ }
virtual void init(AIManager& manager)
{
AIObject::init(manager);
/// Put together somw waypoints
/// This needs to be replaced by something generic
SGGeod rwyStart = SGGeod::fromDegM(11.35203755, 47.26109606, 574);
SGGeod rwyEnd = SGGeod::fromDegM(11.33741688, 47.25951967, 576);
SGQuatd hl = SGQuatd::fromLonLat(rwyStart);
SGVec3d down = hl.backTransform(SGVec3d(0, 0, 1));
SGVec3d cartRwyStart = SGVec3d::fromGeod(rwyStart);
SGVec3d cartRwyEnd = SGVec3d::fromGeod(rwyEnd);
SGVec3d centerline = normalize(cartRwyEnd - cartRwyStart);
SGVec3d left = normalize(cross(centerline, down));
SGGeod endClimb = SGGeod::fromGeodM(SGGeod::fromCart(cartRwyEnd + 500*centerline), 700);
SGGeod startDescend = SGGeod::fromGeodM(SGGeod::fromCart(cartRwyStart - 500*centerline + 150*left), 650);
SGGeod startDownwind = SGGeod::fromGeodM(SGGeod::fromCart(cartRwyEnd + 500*centerline + 800*left), 750);
SGGeod endDownwind = SGGeod::fromGeodM(SGGeod::fromCart(cartRwyStart - 500*centerline + 800*left), 750);
SGLocationd location(SGVec3d::fromGeod(rwyStart), SGQuatd::fromLonLat(rwyStart)*SGQuatd::fromEulerDeg(-100, 0, 0));
Physics* physics = new Physics(location, SGVec3d(0, 0, 0), SGVec3d(0, 0, 0));
physics->_waypoints.push_back(SGVec3d::fromGeod(rwyStart));
physics->_waypoints.push_back(SGVec3d::fromGeod(rwyEnd));
physics->_waypoints.push_back(SGVec3d::fromGeod(endClimb));
physics->_waypoints.push_back(SGVec3d::fromGeod(startDownwind));
physics->_waypoints.push_back(SGVec3d::fromGeod(endDownwind));
physics->_waypoints.push_back(SGVec3d::fromGeod(startDescend));
setPhysics(physics);
/// Ok, this is part of the official sketch
HLAMPAircraftClass* objectClass = dynamic_cast<HLAMPAircraftClass*>(manager.getObjectClass("MPAircraft"));
if (!objectClass)
return;
_objectInstance = new HLAMPAircraft(objectClass);
if (!_objectInstance.valid())
return;
_objectInstance->registerInstance();
_objectInstance->setModelPath("Aircraft/ogel/Models/SinglePiston.xml");
/// Need to schedule something else we get deleted
manager.schedule(*this, getSimTime() + SGTimeStamp::fromSecMSec(0, 100));
}
virtual void update(AIManager& manager, const SGTimeStamp& simTime)
{
SGTimeStamp dt = simTime - getSimTime();
setGroundCache(getPhysics(), manager.getPager(), dt);
getEnvironment().update(*this, dt);
getSubsystemGroup().update(*this, dt);
getPhysics().update(*this, dt);
AIObject::update(manager, simTime);
if (!_objectInstance.valid())
return;
_objectInstance->setLocation(getPhysics());
_objectInstance->setSimTime(getSimTime().toSecs());
_objectInstance->updateAttributeValues(getSimTime(), simgear::RTIData("update"));
/// Need to schedule something else we get deleted
manager.schedule(*this, getSimTime() + SGTimeStamp::fromSecMSec(0, 100));
}
virtual void shutdown(AIManager& manager)
{
if (_objectInstance.valid())
_objectInstance->removeInstance(simgear::RTIData("shutdown"));
_objectInstance = 0;
AIObject::shutdown(manager);
}
private:
SGSharedPtr<HLAMPAircraft> _objectInstance;
};
} // namespace fgai
// getopt
#include <unistd.h>
int
main(int argc, char* argv[])
{
SGSharedPtr<fgai::AIManager> manager = new fgai::AIManager;
/// FIXME include some argument parsing stuff
std::string fg_root;
std::string fg_scenery;
int c;
while ((c = getopt(argc, argv, "cCf:F:n:O:p:RsS")) != EOF) {
switch (c) {
case 'c':
manager->setCreateFederationExecution(true);
break;
case 'C':
manager->setTimeConstrained(true);
break;
case 'f':
manager->setFederateType(optarg);
break;
case 'F':
manager->setFederationExecutionName(optarg);
break;
case 'O':
manager->setFederationObjectModel(optarg);
break;
case 'p':
sglog().set_log_classes(SG_ALL);
sglog().set_log_priority(sgDebugPriority(atoi(optarg)));
break;
case 'R':
manager->setTimeRegulating(true);
break;
case 's':
manager->setTimeConstrainedByLocalClock(false);
break;
case 'S':
manager->setTimeConstrainedByLocalClock(true);
break;
case 'r':
fg_root = optarg;
break;
case 'y':
fg_scenery = optarg;
break;
}
}
if (fg_root.empty()) {
if (const char *fg_root_env = std::getenv("FG_ROOT")) {
fg_root = fg_root_env;
} else {
fg_root = PKGLIBDIR;
}
}
if (fg_scenery.empty()) {
if (const char *fg_scenery_env = std::getenv("FG_SCENERY")) {
fg_scenery = fg_scenery_env;
} else if (!fg_root.empty()) {
SGPath path(fg_root);
path.append("Scenery");
fg_scenery = path.str();
}
}
manager->getPager().setScenery(fg_root, fg_scenery);
if (manager->getFederationObjectModel().empty()) {
SGPath path(fg_root);
path.append("HLA");
path.append("fg-local-fom.xml");
manager->setFederationObjectModel(path.local8BitStr());
}
/// EDDS
manager->insert(new fgai::AIOgel(SGGeod::fromDegFt(9.19298, 48.6895, 2000)));
/// LOWI
manager->insert(new fgai::AIOgel(SGGeod::fromDegFt(11.344, 47.260, 2500)));
/// LOWI traffic circuit
manager->insert(new fgai::AIOgelInTrafficCircuit);
return manager->exec();
}

View File

@@ -0,0 +1,32 @@
set(name fgcom)
# Copy positions.txt content in const char* _positionsData[];
file(READ utils/positions.txt POSITIONS_DATA)
string(REGEX REPLACE "\n" "\"%
\"" POSITIONS_DATA ${POSITIONS_DATA})
string(REGEX REPLACE "%" "," POSITIONS_DATA ${POSITIONS_DATA})
set(out_file ${CMAKE_BINARY_DIR}/positions.hxx)
file(WRITE ${out_file} "const char* _positionsData[] = {\n\"")
file(APPEND ${out_file} ${POSITIONS_DATA})
file(APPEND ${out_file} "\"\n};")
if(MSVC)
set(RESOURCE_FILE fgcom.rc)
endif(MSVC)
set(SOURCES fgcom_external.cxx)
set(HEADERS fgcom_external.hxx ${out_file})
add_executable(${name}
${SOURCES}
${HEADERS}
${RESOURCE_FILE}
)
target_link_Libraries(${name}
iaxclient_lib
SimGearCore)
install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

BIN
utils/fgcom/fgcom.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

1
utils/fgcom/fgcom.rc Normal file
View File

@@ -0,0 +1 @@
FGCOM ICON "fgcom.ico"

View File

@@ -0,0 +1,644 @@
/*
* fgcom - VoIP-Client for the FlightGear-Radio-Infrastructure
*
* This program realizes the usage of the VoIP infractructure based
* on flight data which is send from FlightGear with an external
* protocol to this application.
*
* Clement de l'Hamaide - Jan 2014
* Re-writting of FGCom standalone
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include "config.h"
#include <simgear/sg_inlines.h>
#include <simgear/math/SGMath.hxx>
#include <simgear/io/raw_socket.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/timing/timestamp.hxx>
#include <iaxclient.h>
#include "fgcom_external.hxx"
#include "positions.hxx" // provides _positionsData[];
int _port = 16661;
int _callId = -1;
int _currentFreqKhz = -1;
int _maxRange = 100;
int _minRange = 10;
int _registrationId = -1;
bool _libInitialized = false;
bool _running = true;
bool _debug = false;
bool _connected = false;
double _frequency = -1;
double _atis = -1;
double _silenceThd = -35.0;
std::string _app = "FGCOM-";
std::string _host = "127.0.0.1";
std::string _server = "fgcom.flightgear.org";
std::string _airport = "ZZZZ";
std::string _callsign = "guest";
std::string _username = "guest";
std::string _password = "guest";
SGGeod _airportPos;
SGTimeStamp _p;
std::multimap<int, Airport> _airportsData;
const int special_freq[] = { // Define some freq who need to be used with icao = ZZZZ
910000,
911000,
700000,
123450,
122750,
121500,
123500 };
//
// Main loop
//
int main(int argc, char** argv)
{
signal(SIGINT, quit);
signal(SIGTERM, quit);
simgear::requestConsole(true);
sglog().setLogLevels(SG_ALL, SG_INFO);
_app += FGCOM_VERSION;
Modes mode = PILOT;
std::string num = "";
for(int count = 1; count < argc; count++) {
std::string item = argv[count];
std::string option = item.substr(2, item.find("=")-2);
std::string value = item.substr(item.find("=")+1, item.size());
if(option == "server") _server = value;
if(option == "host") _host = value;
if(option == "port") _port = atoi(value.c_str());
if(option == "callsign") _callsign = value;
if(option == "frequency") _frequency = atof(value.c_str());
if(option == "atis") _atis = atof(value.c_str());
if(option == "airport") _airport = simgear::strutils::uppercase(value);
if(option == "username") _username = value;
if(option == "password") _password = value;
if(option == "silence-threshold") _silenceThd = atof(value.c_str());
if(option == "debug") sglog().setLogLevels(SG_ALL, SG_DEBUG);
if(option == "help") return usage();
if(option == "version") return version();
}
if(_frequency == 910.000)
mode = TEST;
if(_frequency <= 136.975 && _frequency >= 118.000)
mode = OBS;
if(_atis <= 136.975 && _atis >= 118.000 && _airport != "ZZZZ")
mode = ATC;
SG_LOG(SG_GENERAL, SG_INFO, "FGCom " << FGCOM_VERSION << " compiled " << __DATE__
<< ", at " << __TIME__ );
SG_LOG(SG_GENERAL, SG_INFO, "For help usage, use --help");
SG_LOG(SG_GENERAL, SG_INFO, "Starting FGCom session as " << _username << ":xxxxxxxxx@" << _server);
if( !(_libInitialized = lib_init()) )
return EXIT_FAILURE;
if (_username != "guest" && _password != "guest")
_registrationId = lib_registration();
if(mode == PILOT) {
SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode PILOT" );
simgear::Socket::initSockets();
simgear::Socket sgSocket;
sgSocket.open(false);
sgSocket.bind(_host.c_str(), _port);
sgSocket.setBlocking(false);
lib_setVolume(0.0, 1.0);
static char currentPacket[MAXBUFLEN+2], previousPacket[MAXBUFLEN+2];
struct Data currentData, previousData, previousPosData;
double currentFreq = -1, previousFreq = -1;
std::string currentIcao = "";
ActiveComm activeComm = COM1;
_airportsData = getAirportsData();
SG_LOG(SG_GENERAL, SG_INFO, "");
while(_running) {
int bytes = sgSocket.recv(currentPacket, sizeof(currentPacket)-1, 0);
if (bytes == -1) {
SGTimeStamp::sleepForMSec(1); // Prevent full CPU usage (loop)
continue;
}
currentPacket[bytes] = '\0';
if( strcmp(currentPacket, previousPacket) != 0 ) {
std::string packet(currentPacket);
std::vector<std::string> properties = simgear::strutils::split(packet, ",");
for(size_t i=0; i < properties.size(); i++) {
std::vector<std::string> prop = simgear::strutils::split(properties[i], "=");
if(prop[0] == "PTT") currentData.ptt = atoi(prop[1].c_str());
if(prop[0] == "LAT") currentData.lat = atof(prop[1].c_str());
if(prop[0] == "LON") currentData.lon = atof(prop[1].c_str());
if(prop[0] == "ALT") currentData.alt = atof(prop[1].c_str());
if(prop[0] == "COM1_FRQ") currentData.com1 = atof(prop[1].c_str());
if(prop[0] == "COM2_FRQ") currentData.com2 = atof(prop[1].c_str());
if(prop[0] == "OUTPUT_VOL") currentData.outputVol = atof(prop[1].c_str());
if(prop[0] == "SILENCE_THD") currentData.silenceThd = atof(prop[1].c_str());
if(prop[0] == "CALLSIGN") currentData.callsign = prop[1];
}
if(currentData.ptt != previousData.ptt) {
if(currentData.ptt == 2) {
if(activeComm == COM1) {
activeComm = COM2;
currentFreq = currentData.com2;
} else {
activeComm = COM1;
currentFreq = currentData.com1;
}
SG_LOG( SG_GENERAL, SG_INFO, "Select radio " << activeComm << " on " << currentFreq << " MHz" );
} else if(currentData.ptt) {
SG_LOG( SG_GENERAL, SG_INFO, "[SPEAK] unmute mic, mute speaker" );
lib_setVolume(1.0, 0.0);
} else {
SG_LOG( SG_GENERAL, SG_INFO, "[LISTEN] mute mic, unmute speaker" );
lib_setVolume(0.0, currentData.outputVol);
}
}
if(currentData.outputVol != previousData.outputVol)
lib_setVolume(0.0, currentData.outputVol);
if(currentData.silenceThd != previousData.silenceThd)
lib_setSilenceThreshold(currentData.silenceThd);
if(currentData.callsign != previousData.callsign)
lib_setCallerId(currentData.callsign);
if(currentData.com1 != previousData.com1 && activeComm == COM1) {
currentFreq = currentData.com1;
SG_LOG( SG_GENERAL, SG_INFO, "Select frequency " << currentFreq << " MHz on radio " << activeComm );
}
if(currentData.com2 != previousData.com2 && activeComm == COM2) {
currentFreq = currentData.com2;
SG_LOG( SG_GENERAL, SG_INFO, "Select frequency " << currentFreq << " MHz on radio " << activeComm );
}
if(previousFreq != currentFreq || currentData.callsign != previousData.callsign) {
_currentFreqKhz = 10 * static_cast<int>(currentFreq * 100 + 0.25);
currentIcao = getClosestAirportForFreq(currentFreq, currentData.lat, currentData.lon, currentData.alt);
if(isInRange(currentIcao, currentData.lat, currentData.lon, currentData.alt)) {
_connected = lib_call(currentIcao, currentFreq);
SG_LOG( SG_GENERAL, SG_INFO, "Connecting " << currentIcao << " on " << currentFreq << " MHz" );
} else {
if(_connected) {
_connected = lib_hangup();
SG_LOG( SG_GENERAL, SG_INFO, "Disconnecting " << currentIcao << " on " << currentFreq << " MHz (out of range)" );
}
}
}
if( currentData.lat <= previousPosData.lat - 0.05 ||
currentData.lon <= previousPosData.lon - 0.05 ||
currentData.alt <= previousPosData.alt - 50.0 ||
currentData.lat >= previousPosData.lat + 0.05 ||
currentData.lon >= previousPosData.lon + 0.05 ||
currentData.alt >= previousPosData.alt + 50.0) {
currentIcao = getClosestAirportForFreq(currentFreq, currentData.lat, currentData.lon, currentData.alt);
if(_connected) {
if(!isInRange(currentIcao, currentData.lat, currentData.lon, currentData.alt)) {
_connected = lib_hangup();
SG_LOG( SG_GENERAL, SG_INFO, "Disconnecting " << currentIcao << " on " << currentFreq << " MHz (out of range)" );
}
} else {
if(isInRange(currentIcao, currentData.lat, currentData.lon, currentData.alt)) {
_connected = lib_call(currentIcao, currentFreq);
SG_LOG( SG_GENERAL, SG_INFO, "Connecting " << currentIcao << " on " << currentFreq << " MHz" );
}
}
previousPosData = currentData;
}
previousFreq = currentFreq;
previousData = currentData;
}
strcpy(previousPacket, currentPacket);
} // while()
sgSocket.close();
} else { // if(mode == PILOT)
int sessionDuration = 1000;
_p.stamp();
if(mode == OBS) {
SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode OBS (max duration: 6 hours)" );
sessionDuration *= 2160; // 6 hours for OBS mode
lib_setVolume(0.0, 1.0);
lib_setCallerId("::OBS::");
num = computePhoneNumber(_frequency, _airport);
} else {
lib_setVolume(1.0, 1.0);
if(mode == TEST) {
sessionDuration *= 65; // 65 seconds for TEST mode
SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode TEST (max duration: 65 seconds)" );
_airport = "ZZZZ";
num = computePhoneNumber(_frequency, _airport);
} else if(mode == ATC) {
sessionDuration *= 45; // 45 seconds for ATC mode
SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode ATC (max duration: 45 seconds)" );
num = computePhoneNumber(_atis, _airport, true);
}
}
_connected = lib_directCall(_airport, _frequency, num);
while (_p.elapsedMSec() <= sessionDuration && _running){
SGTimeStamp::sleepForMSec(2000);
}
}
if(!lib_shutdown())
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
// function: getAirportsData
// action: parse positionsData.hxx then build multimap
std::multimap<int, Airport> getAirportsData()
{
std::vector<std::string> lines;
std::multimap<int, Airport> aptData;
SG_LOG(SG_GENERAL, SG_INFO, "Loading airports information...");
for(size_t i=0; i < sizeof(_positionsData)/sizeof(*_positionsData); i++) { // _positionsData is provided by positions.hxx
std::vector<std::string> entries = simgear::strutils::split(_positionsData[i], ",");
if(entries.size() == 6) {
// [0]=ICAO, [1]=Frequency, [2]=Latitude, [3]=Longitude, [4]=ID/Type, [5]=Name
std::string entryIcao = entries[0];
double entryFreq = atof(entries[1].c_str());
double entryLat = atof(entries[2].c_str());
double entryLon = atof(entries[3].c_str());
std::string entryType = entries[4];
std::string entryName = entries[5];
int aptFreqKhz = 10 * static_cast<int>(entryFreq * 100 + 0.25);
Airport apt;
apt.icao = entryIcao;
apt.frequency = entryFreq;
apt.latitude = entryLat;
apt.longitude = entryLon;
apt.type = entryType;
apt.name = entryName;
aptData.insert( std::pair<int, Airport>(aptFreqKhz, apt) );
}
}
return aptData;
}
// function: orderByDistanceNm
// action: sort airportsInRange vector by distanceNm ASC in getClosestAirportForFreq()
bool orderByDistanceNm(Airport a, Airport b)
{
return a.distanceNm < b.distanceNm;
}
// function: gestClosestAircraftForFreq
// action: return ICAO of closest airport with given frequency and define his position
std::string getClosestAirportForFreq(double freq, double acftLat, double acftLon, double acftAlt)
{
for(size_t i=0; i<sizeof(special_freq)/sizeof(special_freq[0]); i++) { // Check if it's a special freq
if( special_freq[i] == _currentFreqKhz )
return std::string("ZZZZ");
}
std::string icao = "";
double aptLon = 0;
double aptLat = 0;
int freqKhz = 10 * static_cast<int>(freq * 100 + 0.25);
SGGeod acftPos = SGGeod::fromDegFt(acftLon, acftLat, acftAlt);
std::vector<Airport> airportsInRange;
std::pair <std::multimap<int, Airport>::iterator, std::multimap<int, Airport>::iterator> ret;
ret = _airportsData.equal_range(freqKhz);
for (std::multimap<int, Airport>::iterator it=ret.first; it!=ret.second; ++it) {
SGGeod aptPos = SGGeod::fromDeg(it->second.longitude, it->second.latitude);
double distNm = SGGeodesy::distanceNm(aptPos, acftPos);
if(distNm <= _maxRange){
it->second.distanceNm = distNm;
airportsInRange.push_back(it->second);
}
}
if(!airportsInRange.size())
return icao;
std::sort(airportsInRange.begin(), airportsInRange.end(), orderByDistanceNm);
aptLon = airportsInRange[0].longitude;
aptLat = airportsInRange[0].latitude;
icao = airportsInRange[0].icao;
_airportPos = SGGeod::fromDeg(aptLon, aptLat);
SG_LOG(SG_GENERAL, SG_INFO, "Airport " << airportsInRange[0].icao << " " << airportsInRange[0].name << " - "
<< airportsInRange[0].type << " on " << airportsInRange[0].frequency
<< " - is in range " << airportsInRange[0].distanceNm << "nm ("
<< (SG_NM_TO_METER*airportsInRange[0].distanceNm)/1000 <<"km)");
return icao;
}
// function: isInRange
// action: return TRUE if airport/freq is in range, else return FALSE
bool isInRange(std::string icao, double acftLat, double acftLon, double acftAlt)
{
for(size_t i=0; i<sizeof(special_freq)/sizeof(special_freq[0]); i++) { // Check if it's a special freq
if( special_freq[i] == _currentFreqKhz )
return true;
}
if(icao.empty())
return false;
SGGeod acftPos = SGGeod::fromDegFt(acftLon, acftLat, acftAlt);
double distNm = SGGeodesy::distanceNm(_airportPos, acftPos);
double delta_elevation_ft = fabs(acftPos.getElevationFt() - _airportPos.getElevationFt());
double rangeNm = 1.23 * sqrt(delta_elevation_ft);
if (rangeNm > _maxRange) rangeNm = _maxRange;
if (rangeNm < _minRange) rangeNm = _minRange;
if( distNm > rangeNm ) return false;
return true;
}
// function: quit
// action: set _running flag to false
void quit(int state)
{
SG_LOG( SG_GENERAL, SG_INFO, "Exiting FGCom" );
_running = false;
#ifdef _WIN32
lib_shutdown();
SG_LOG(SG_GENERAL, SG_INFO, "You can close the terminal now");
#endif
}
// function: usage
// action: display FGCom usage then quit
int usage()
{
std::cout << "FGCom " << FGCOM_VERSION << " usage:" << std::endl;
std::cout << " --server=fgcom.flightgear.org - Server to connect" << std::endl;
std::cout << " --host=127.0.0.1 - Host to listen i.e where FG is running" << std::endl;
std::cout << " --port=16661 - Port to use" << std::endl;
std::cout << " --callsign=guest - Callsign during session e.g F-ELYD" << std::endl;
std::cout << " --frequency=xxx.xxx - Frequency e.g 120.500" << std::endl;
std::cout << " --airport=YYYY - ICAO of airport e.g KSFO" << std::endl;
std::cout << " --username=guest - Username for registration" << std::endl;
std::cout << " --password=guest - Password for registration" << std::endl;
std::cout << " --silence-threshold=-35 - Silence threshold in dB (-60 < range < 0 )" << std::endl;
std::cout << " --debug - Enable debug output" << std::endl;
std::cout << " --help - Show this message" << std::endl;
std::cout << " --version - Show version" << std::endl;
std::cout << "" << std::endl;
std::cout << " None of these options are required, you can simply start FGCom without option at all: it works" << std::endl;
std::cout << " For further information, please visit: http://wiki.flightgear.org/FGCom_3.0" << std::endl;
std::cout << "" << std::endl;
std::cout << " About silence-threshold:" << std::endl;
std::cout << " This is the limit, in dB, when FGCom consider no voice in your microphone." << std::endl;
std::cout << " --silence-threshold=-60 is similar to micro always ON" << std::endl;
std::cout << " --silence-threshold=0 is similar to micro always OFF" << std::endl;
std::cout << " Default value is -35.0 dB" << std::endl;
std::cout << "" << std::endl;
std::cout << " In order to make an echo-test, you have to start FGCom like:" << std::endl;
std::cout << " fgcom --frequency=910" << std::endl;
std::cout << "" << std::endl;
std::cout << " In order to listen a frequency, you have to start FGCom like:" << std::endl;
std::cout << " fgcom --frequency=xxx.xxx --airport=YYYY" << std::endl;
std::cout << " where xxx.xxx is the frequency of the ICAO airport YYYY that you want to listen to" << std::endl;
std::cout << "" << std::endl;
std::cout << " In order to record an ATIS message, you have to start FGCom like:" << std::endl;
std::cout << " fgcom --atis=xxx.xxx --airport=YYYY" << std::endl;
std::cout << " where xxx.xxx is the ATIS frequency of the ICAO airport YYYY" << std::endl;
std::cout << "" << std::endl;
return EXIT_SUCCESS;
}
// function: version
// action: display FGCom version then quit
int version()
{
SG_LOG(SG_GENERAL, SG_INFO, "FGCom " << FGCOM_VERSION << " compiled " << __DATE__
<< ", at " << __TIME__ );
std::cout << "" << std::endl;
return EXIT_SUCCESS;
}
// function: computePhoneNumber
// action: return phone number
std::string computePhoneNumber(double freq, std::string icao, bool atis)
{
if(icao.empty())
return std::string();
char phoneNumber[256];
char exten[32];
char tmp[5];
int prefix = atis ? 99 : 01;
sprintf( tmp, "%4s", icao.c_str() );
sprintf( exten,
"%02d%02d%02d%02d%02d%06d",
prefix,
tmp[0],
tmp[1],
tmp[2],
tmp[3],
(int) (freq * 1000 + 0.5) );
exten[16] = '\0';
snprintf( phoneNumber,
sizeof(phoneNumber),
"%s:%s@%s/%s",
_username.c_str(),
_password.c_str(),
_server.c_str(),
exten);
return phoneNumber;
}
// function: lib_setVolume
// action: set input/output volume
void lib_setVolume(double input, double output)
{
SG_CLAMP_RANGE<double>(input, 0.0, 1.0);
SG_CLAMP_RANGE<double>(output, 0.0, 1.0);
SG_LOG(SG_GENERAL, SG_DEBUG, "Set volume input=" << input << " , output=" << output);
iaxc_input_level_set(input);
iaxc_output_level_set(output);
}
// function: lib_setSilenceThreshold
// action: set silence threshold
void lib_setSilenceThreshold(double thd)
{
SG_CLAMP_RANGE<double>(thd, -60, 0);
SG_LOG(SG_GENERAL, SG_DEBUG, "Set silence threshold=" << thd);
iaxc_set_silence_threshold(thd);
}
// function: lib_setCallerId
// action: set caller id for the session
void lib_setCallerId(std::string callsign)
{
SG_LOG(SG_GENERAL, SG_DEBUG, "Set caller ID=" << callsign);
iaxc_set_callerid (callsign.c_str(), _app.c_str());
}
// function: lib_init
// action: init the library
bool lib_init()
{
SG_LOG(SG_GENERAL, SG_DEBUG, "Initializing IAX library");
#ifdef _MSC_VER
iaxc_set_networking( (iaxc_sendto_t)sendto, (iaxc_recvfrom_t)recvfrom );
#endif
if (iaxc_initialize(4)) {
SG_LOG( SG_GENERAL, SG_ALERT, "Error: cannot initialize IAXClient !\nHINT: Have you checked the mic and speakers ?" );
return false;
}
iaxc_set_callerid( _callsign.c_str(), _app.c_str() );
iaxc_set_formats(IAXC_FORMAT_SPEEX, IAXC_FORMAT_ULAW|IAXC_FORMAT_SPEEX);
iaxc_set_speex_settings(1, 5, 0, 1, 0, 3);
iaxc_set_filters(IAXC_FILTER_AGC | IAXC_FILTER_DENOISE);
iaxc_set_event_callback(iaxc_callback);
iaxc_start_processing_thread ();
lib_setSilenceThreshold(_silenceThd);
return true;
}
// function: lib_shutdown
// action: stop the library
bool lib_shutdown()
{
SG_LOG(SG_GENERAL, SG_DEBUG, "Shutdown IAX library");
lib_hangup();
if(_registrationId != -1)
iaxc_unregister(_registrationId);
return true;
}
// function: lib_call
// action: register a user on remote server then return the registration ID
int lib_registration()
{
SG_LOG(SG_GENERAL, SG_DEBUG, "Request registration");
SG_LOG(SG_GENERAL, SG_DEBUG, " username: " << _username);
SG_LOG(SG_GENERAL, SG_DEBUG, " password: xxxxxxxx");
SG_LOG(SG_GENERAL, SG_DEBUG, " server: " << _server);
int regId = iaxc_register( _username.c_str(), _password.c_str(), _server.c_str());
if(regId == -1) {
SG_LOG( SG_GENERAL, SG_ALERT, "Warning: cannot register '" << _username << "' at '" << _server );
}
return regId;
}
// function: lib_call
// action: kill current call then do a new call
bool lib_call(std::string icao, double freq)
{
SG_LOG(SG_GENERAL, SG_DEBUG, "Request new call");
SG_LOG(SG_GENERAL, SG_DEBUG, " icao: " << icao);
SG_LOG(SG_GENERAL, SG_DEBUG, " freq: " << freq);
lib_hangup();
iaxc_millisleep(300);
std::string num = computePhoneNumber(freq, icao);
if(num.empty())
return false;
_callId = iaxc_call(num.c_str());
if(_callId == -1) {
SG_LOG( SG_GENERAL, SG_ALERT, "Warning: cannot call: " << num );
return false;
}
return true;
}
bool lib_directCall(std::string icao, double freq, std::string num)
{
SG_LOG(SG_GENERAL, SG_DEBUG, "Request new call");
SG_LOG(SG_GENERAL, SG_DEBUG, " icao: " << icao);
SG_LOG(SG_GENERAL, SG_DEBUG, " freq: " << freq);
lib_hangup();
iaxc_millisleep(300);
if(num.empty())
return false;
_callId = iaxc_call(num.c_str());
if(_callId == -1) {
SG_LOG( SG_GENERAL, SG_ALERT, "Warning: cannot call: " << num );
return false;
}
return true;
}
// function: lib_hangup
// action: kill current call
bool lib_hangup()
{
if(!_connected)
return false;
SG_LOG(SG_GENERAL, SG_DEBUG, "Request hangup");
iaxc_dump_all_calls();
_callId = -1;
return false;
}
// function: iaxc_callback
// action: parse IAX event then call event handler
int iaxc_callback(iaxc_event e)
{
switch (e.type) {
case IAXC_EVENT_TEXT:
if(e.ev.text.type == IAXC_TEXT_TYPE_STATUS ||
e.ev.text.type == IAXC_TEXT_TYPE_IAX)
SG_LOG( SG_GENERAL, SG_INFO, "Message: " << e.ev.text.message );
break;
}
return 1;
}
// eof

View File

@@ -0,0 +1,123 @@
/*
* fgcom - VoIP-Client for the FlightGear-Radio-Infrastructure
*
* This program realizes the usage of the VoIP infractructure based
* on flight data which is send from FlightGear with an external
* protocol to this application.
*
* Clement de l'Hamaide - Jan 2014
* Re-writting of FGCom standalone
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#ifndef __FGCOM_H__
#define __FGCOM_H__
// avoid name clash with winerror.h
#define FGC_SUCCESS(__x__) (__x__ == 0)
#define FGC_FAILED(__x__) (__x__ < 0)
#ifdef _MSC_VER
#define snprintf _snprintf
#ifdef WIN64
typedef __int64 ssize_t;
#else
typedef int ssize_t;
#endif
#endif
#include <map>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#ifndef FGCOM_VERSION
#ifdef FLIGHTGEAR_VERSION
#define FGCOM_VERSION FLIGHTGEAR_VERSION
#else
#define FGCOM_VERSION "unknown"
#endif
#endif
#define MAXBUFLEN 1024
enum Modes {
ATC,
PILOT,
OBS,
TEST
};
enum ActiveComm {
COM1,
COM2
};
struct Data
{
int ptt;
float com1;
float com2;
double lon;
double lat;
double alt;
float outputVol;
float silenceThd;
std::string callsign;
};
struct Airport
{
double frequency;
double latitude;
double longitude;
double distanceNm;
std::string icao;
std::string type;
std::string name;
};
// Internal functions
int usage();
int version();
void quit(int state);
bool isInRange(std::string icao, double acftLat, double acftLon, double acftAlt);
std::string computePhoneNumber(double freq, std::string icao, bool atis = false);
std::string getClosestAirportForFreq(double freq, double acftLat, double acftLon, double acftAlt);
std::multimap<int, Airport> getAirportsData();
// Library functions
bool lib_init();
bool lib_hangup();
bool lib_shutdown();
bool lib_call(std::string icao, double freq);
bool lib_directCall(std::string icao, double freq, std::string num);
int lib_registration();
int iaxc_callback(iaxc_event e);
void lib_setSilenceThreshold(double thd);
void lib_setCallerId(std::string callsign);
void lib_setVolume(double input, double output);
#endif

47174
utils/fgcom/positions.hxx Executable file

File diff suppressed because it is too large Load Diff

50
utils/fgcom/utils/README Normal file
View File

@@ -0,0 +1,50 @@
# README for fgcom/utils folder content
#
# 26 sept, 2013 - Clément de l'Hamaide
============================================
==== fgcom.conf =====
============================================
This file is the dialplan as of 26/09/2013 generated by gen_phonebook.pl
If you are looking for setup an Asterisk server for FGCom you should
add this file in your /etc/asterisk folder
============================================
==== gen_phonebook.pl =====
============================================
This file is used to parse apt.dat.gz and nav.dat.gz
and create fgcom.conf and positions.txt as result.
To use this script you should put apt.dat.gz and nav.dat.gz
belong gen_phonebook.pl then run the script like:
./gen_phonebook.pl
For details use:
./gen_phonebook.pl --help
============================================
==== build_fgcom_server =====
============================================
This file is used to build an FGCom server FOR DEBIAN BASED OPERATING SYSTEM ONLY
You just need to run the script like:
./build_fgcom_server.sh
What does the script ?
- install Asterisk and related/dependencies
- download apt.dat.gz and nav.data.gz
- download gen_phonebook.pl script
- run gen_phonebook.pl
- configure Asterisk
- start Asterisk and show the CLI

View File

@@ -0,0 +1,122 @@
#!/bin/bash
PHONEBOOK_SCRIPT="http://clemaez.fr/flightgear/gen_phonebook.pl.txt"
APTNAV_DATA="http://dev.x-plane.com/update/data/AptNav201304XP1000.zip"
DAHDI_SRC="http://downloads.asterisk.org/pub/telephony/dahdi-linux-complete/dahdi-linux-complete-current.tar.gz"
LOGSEP="###########################################"
GETMETAR_SCRIPT="#!/bin/bash
#curl https://tgftp.nws.noaa.gov/data/observations/metar/stations/$1.TXT
echo \"Hello World !\""
ROOT=$PWD
echo $LOGSEP
echo "Install Asterisk & Festival & dependances"
echo ""
sudo apt-get install asterisk asterisk-dahdi festival gzip unzip perl libfile-slurp-perl libdatetime-perl
echo ""
echo ""
wget $DAHDI_SRC -O dahdi_src.tar.gz
tar -zxvf dahdi_src.tar.gz
mv dahdi*/ dahdi_src/
cd dahdi_src
make
sudo make install
sudo make config
echo ""
echo ""
echo "$GETMETAR_SCRIPT" > getmetar
chmod +x getmetar
sudo mv getmetar /usr/bin/getmetar
cd $ROOT
echo $LOGSEP
echo "Download APT & NAV data"
echo ""
wget $APTNAV_DATA -O aptnav_data.zip
mkdir aptnav && cd aptnav
unzip ../aptnav_data.zip
mv earth_nav.dat nav.dat
gzip apt.dat && mv apt.dat.gz ../apt.dat.gz
gzip nav.dat && mv nav.dat.gz ../nav.dat.gz
cd .. && rm -rf aptnav/
rm aptnav_data.zip
cd $ROOT
echo $LOGSEP
echo "Download PhoneBook script"
echo ""
wget $PHONEBOOK_SCRIPT -O gen_phonebook.pl
echo $LOGSEP
echo "Run PhoneBook script"
echo ""
chmod +x gen_phonebook.pl
./gen_phonebook.pl
echo $LOGSEP
echo "Configure Asterisk"
echo ""
echo "[general]
static=yes
writeprotect=yes
;
[default]
#include \"fgcom.conf\"
include => fgcom" > extensions.conf
cd /etc/asterisk
if [ -f iax.conf.bak ]
then
sudo rm iax.conf
sudo mv iax.conf.bak iax.conf
fi
sudo cp iax.conf iax.conf.bak
sudo sed -i '/callerid="Guest IAX User"/a requirecalltoken=no' iax.conf
sudo sed -i '/^;calltokenoptional=/c calltokenoptional=0.0.0.0/0.0.0.0' iax.conf
sudo sed -i '/^;authdebug=no/c authdebug=no' iax.conf
if [ ! -f extensions.conf.bak ]
then
sudo cp extensions.conf extensions.conf.bak
fi
sudo rm extensions.conf
sudo mv $ROOT/extensions.conf extensions.conf
sudo mv $ROOT/fgcom.conf fgcom.conf
sudo chown asterisk:asterisk extensions.conf
sudo chown asterisk:asterisk extensions.conf.bak
sudo chown asterisk:asterisk iax.conf
sudo chown asterisk:asterisk iax.conf.bak
sudo chown asterisk:asterisk fgcom.conf
sudo mkdir /var/fgcom-server
sudo chown asterisk:asterisk /var/fgcom-server
sudo mkdir /var/fgcom-server/atis
sudo chown asterisk:asterisk /var/fgcom-server/atis
cd $ROOT
echo $LOGSEP
echo "Restart Asterisk"
echo ""
sudo service asterisk restart
echo $LOGSEP
echo "Run Festival"
echo ""
nohup festival --server > /dev/null 2> /dev/null < /dev/null &
echo $LOGSEP
echo "Enter in Asterisk CLI"
echo ""
sudo asterisk -r -vvvvvvvvvv

138836
utils/fgcom/utils/fgcom.conf Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,608 @@
#!/usr/bin/perl -w
#
# gen_phonebook.pl
# Version 1.04 - 26 Sept 2013
# Version 1.03 - 04 Sept 2013
# Version 1.02 - 01 Sept 2013
#
# Modified by Clément de l'Hamaide - 20130926
# * Disable phonebook.txt generation
#
# Modified by Geoff R. McLane - 20130904
# * Added strict and warnings, fixing all variables to comply
# * Added command line interface and 'help', but kept same defaults
# * Removed numerous substr($txt,0,-1) which truncated some string
# * Change 'chop' to the safer 'chomp'
# * Added heliport(16) and seaport(17) under an option switch
# * Added output of some stats of what was collected, and skipped
# * Added $trim_to_20 option to align the phonebook if desired
# * Replaced all tabs with ' ' to better align the code for readability
# * Added a trim_all($txt) to removed some unwanted line endings
# * Is compatible with FG 810 AND XP 1000 apt.dat files
#
# Modified by Clement de l'Hamaide - 20130901
#
use IO::Zlib;
use IO::File;
use File::Slurp;
use Data::Dumper;
use DateTime;
use Encode;
use strict;
use warnings;
my $VERS = "1.04 - 26 Sept 2013";
# default locations
my $FG_AIRPORTS = "./apt.dat.gz";
my $FG_NAVAIDS = "./nav.dat.gz";
my $out_fil1 = "fgcom.conf";
my $out_pos = "positions.txt";
#my $out_phon = "phonebook.txt";
# options
my $trim_to_20 = 0;
my $add_heliports = 1;
my $add_seaports = 1;
my $phonebook_post="ZZZZ 910.000 0190909090910000 Echo-Box
ZZZZ 911.000 0190909090911000 Music-Box
ZZZZ 700.000 0190909090700000 Radio-Box
ZZZZ 123.450 0190909090123450 Air2Air
ZZZZ 122.750 0190909090122750 Air2Air
ZZZZ 121.500 0190909090121500 Air2Air
ZZZZ 123.500 0190909090123500 Air2Air
ZZZZ 121.000 0190909090121000 Emergency
ZZZZ 723.340 0190909090723340 French Air Patrol
";
my $extensions_pre="[globals]
ATIS_RECORDINGS=/var/fgcom-server/atis
RADIO_FILE=/var/fgcom-server/radio
;Morse tone, 1020Hz for VOR and ILS but 1350Hz for DME, in Hz
MORSETONE=1020
;Dit length for morse code, in ms
;MORSEDITLEN=300
[macro-com]
exten => s,1,Answer()
exten => s,n,NoCDR
exten => s,n,MeetMe(\${MACRO_EXTEN},qd)
exten => s,n,Hangup()
[macro-echo]
exten => s,1,Answer()
exten => s,n,NoCDR
exten => s,n,Echo()
exten => s,n,Hangup()
[macro-atis]
exten => s,1,Answer()
; Check if audio file exists
exten => s,n,TrySystem(ls \${ATIS_RECORDINGS}/99\${MACRO_EXTEN:2}*)
exten => s,n,Goto(\${SYSTEMSTATUS})
; If audio file exists, play it
exten => s,n(SUCCESS),While(\$[1])
exten => s,n,Playback(\${ATIS_RECORDINGS}/99\${MACRO_EXTEN:2})
exten => s,n,Wait(3)
exten => s,n,EndWhile
exten => s,n,Hangup()
; If audio doesn't exist or TrySystem failed (why?), go to festival macro
;exten => s,n,(APPERROR),Macro(festival, \${MACRO_EXTEN}) ; DISABLED FOR NOW
;exten => s,n,(FAILURE),Macro(festival, \${MACRO_EXTEN}) ; DISABLED FOR NOW
exten => s,n,(APPERROR),Hangup()
exten => s,n,(FAILURE),Hangup()
[macro-record-atis]
exten => s,1,Answer()
exten => s,n,SendText(Record begin in 3s)
exten => s,n,Wait(1)
exten => s,n,SendText(Record begin in 2s)
exten => s,n,Wait(1)
exten => s,n,SendText(Record begin in 1s)
exten => s,n,Wait(1)
exten => s,n,Record(\${ATIS_RECORDINGS}/\${MACRO_EXTEN}:gsm,,90,k)
exten => s,n,Wait(2)
exten => s,n,Playback(\${ATIS_RECORDINGS}/\${MACRO_EXTEN})
exten => s,n,Hangup()
[macro-festival]
exten => s,1,Set(metar=\"\${SHELL(getmetar \$[1]):0:-1}\");
exten => s,n,While(\$[1])
exten => s,n,Festival(\${metar:1:-1})
exten => s,n,Wait(3)
exten => s,n,EndWhile
[macro-vor]
exten => s,1,Answer()
exten => s,n,While(\$[1])
exten => s,n,Morsecode(\${ARG1})
exten => s,n,Wait(2)
exten => s,n,EndWhile
exten => s,n,Hangup()
[macro-radio]
exten => s,1,Answer()
exten => s,n,Playback(\${RADIO_FILE})
exten => s,n,Hangup()
[fgcom]
; 910.000 Echo-Box
exten => 0190909090910000,1,SendText(Echo Box - For testing FGCOM)
exten => 0190909090910000,n,Macro(echo)
; 911.000 Music-Box
exten => 0190909090911000,1,Answer
exten => 0190909090911000,n,SendText(Music On Hold Box - For testing FGCOM)
exten => 0190909090911000,n,MusicOnHold(default)
; Radio Station: 700.000 MHz
exten => 0190909090700000,1,SendText(Radio Station - For testing FGCOM)
exten => 0190909090700000,n,Macro(radio)
; 121.500 Air2Air
exten => 0190909090121500,1,SendText(121.500 Auto-information frequency)
exten => 0190909090121500,n,Macro(com)
; 123.450 Air2Air
exten => 0190909090123450,1,SendText(123.450 Auto-information frequency)
exten => 0190909090123450,n,Macro(com)
; 123.500 Air2Air
exten => 0190909090123500,1,SendText(123.500 Auto-information frequency)
exten => 0190909090123500,n,Macro(com)
; 122.750 Air2Air
exten => 0190909090122750,1,SendText(122.750 Auto-information frequency)
exten => 0190909090122750,n,Macro(com)
; 121.000 emergency
exten => 0190909090121000,1,SendText(121.000 Emergency frequency)
exten => 0190909090121000,n,Macro(com)
; 723.340 Franch Air Patrol
exten => 0190909090723340,1,SendText(723.340 French Air Patrol frequency)
exten => 0190909090723340,n,Macro(com)
";
my $dt = DateTime->now;
my $pgmname = $0;
if ($pgmname =~ /(\\|\/)/) {
my @tmpsp = split(/(\\|\/)/,$pgmname);
$pgmname = $tmpsp[-1];
}
my $numberOfFrequenciesParsed = 0;
my $numberOfFrequenciesComputed = 0;
my $numberOfFrequenciesWritten = 0;
my $numberOfFrequenciesSkipped = 0;
my %APT = ();
my %NAV = ();
my %frq = ();
my ($fh,$z,$icao,$lat,$lon,$latW,$lonW,$com,$code,$text);
my ($vor,$icao_number,$f,$positions,$extensions,$phonebook);
my ($i,$tmp,$airport,$nav,$freq,$ssf,$type);
my $land_apcnt = 0;
my $heli_apcnt = 0;
my $sea_apcnt = 0;
my $no_latloncnt = 0;
my $no_freqcnt = 0;
my $airportcount = 0;
my $navlinecount = 0;
my $vordmeadded = 0;
sub prt($) { print shift; }
sub icao2number($) {
my ($icao) = @_;
my ($number,$n);
$icao = " ".$icao while(length($icao) < 4);
$number = '';
for ($i = 0; $i< length($icao); $i++) {
$n = ord(substr($icao,$i,1));
$number .= sprintf("%02d",$n);
}
return($number);
}
sub trim_tailing($) {
my ($ln) = shift;
$ln = substr($ln,0, length($ln) - 1) while ($ln =~ /\s$/g); # remove all TRAILING space
return $ln;
}
sub trim_leading($) {
my ($ln) = shift;
$ln = substr($ln,1) while ($ln =~ /^\s/); # remove all LEADING space
return $ln;
}
sub trim_ends($) {
my ($ln) = shift;
$ln = trim_tailing($ln); # remove all TRAINING space
$ln = trim_leading($ln); # remove all LEADING space
return $ln;
}
sub trim_all {
my ($ln) = shift;
$ln =~ s/\n/ /gm; # replace CR (\n)
$ln =~ s/\r/ /gm; # replace LF (\r)
$ln =~ s/\t/ /g; # TAB(s) to a SPACE
$ln = trim_ends($ln);
$ln =~ s/\s{2}/ /g while ($ln =~ /\s{2}/); # all double space to SINGLE
return $ln;
}
##############################################################################
# Main program
##############################################################################
parse_args(@ARGV);
# read airport data in hash
$fh = new IO::Zlib;
if ($fh->open($FG_AIRPORTS, "r")) {
my $hadlines = 0;
printf("Parsing ".$FG_AIRPORTS." ...\n");
while ($z = <$fh>) {
chomp($z);
if ($z =~ /^\s*$/) {
if ($icao) {
if (scalar(keys(%frq)) > 0) {
if (!$lat && !$lon) {
if ($latW && $lonW) {
$lat = $latW;
$lon = $lonW;
} else {
print($icao." :: LAT/LON not found\n");
}
}
if ($lat && $lon) {
$APT{$icao}{'text'} = $text;
$APT{$icao}{'lat'} = $lat;
$APT{$icao}{'lon'} = $lon;
foreach $f (keys(%frq)) {
if (length($frq{$f}) <= 1) {
$frq{$f} = "TWR";
}
$APT{$icao}{'com'}{$f} = $frq{$f};
}
$airportcount++;
} else {
$no_latloncnt++;
}
} else {
$no_freqcnt++;
}
} elsif ($hadlines) {
$numberOfFrequenciesSkipped++;
}
undef($icao);
undef($text);
undef($lon);
undef($lat);
undef($com);
%frq=();
$hadlines = 0;
next;
}
elsif ($z=~/^1\s+-?\d+\s+[01]\s+[01]\s+([A-Z0-9]+)\s+(.+)\s*$/)
{
# Airport Header
$icao = $1;
$text = trim_all($2);
$land_apcnt++;
}
elsif ($z=~/^16\s+\d+\s+[01]\s+[01]\s+([A-Z0-9]+)\s+(.+)\s*$/)
{
$heli_apcnt++;
# Heliport Header
if ($add_seaports) {
$icao = $1;
$text = trim_all($2);
}
}
elsif ($z=~/^17\s+\d+\s+[01]\s+[01]\s+([A-Z0-9]+)\s+(.+)\s*$/)
{
# Seaport Header
$sea_apcnt++;
if ($add_seaports) {
$icao = $1;
$text = trim_all($2);
}
}
elsif ($z=~/^14\s+(-?\d+\.\d+)\s+(-?\d+\.\d+)/)
{
# TWR Position
$lat=$1;
$lon=$2;
}
elsif ($z=~/^19\s+(-?\d+\.\d+)\s+(-?\d+\.\d+)/)
{
# WS Position
$latW=$1;
$lonW=$2;
}
elsif ($z=~/^5[0-6]\s+(\d{5})\d*\s+(.+)\s*$/)
{
# COM data
$freq = $1;
$type = trim_all($2);
###prt("$freq $type\n");
$ssf = substr($freq, -1);
if ( $ssf == 2 || $ssf == 7) {
$com = sprintf("%3.2f5", $freq/100);
} else {
$com = sprintf("%3.3f", $freq/100);
}
$frq{$com} = $type;
}
$hadlines++;
}
}
else
{
die("Cannot open $FG_AIRPORTS :$!\n");
}
$fh->close;
prt("Got $airportcount airports, $land_apcnt land(1), $heli_apcnt heliports(16), and $sea_apcnt seaports(17), skipped $no_latloncnt no lat/lon, $no_freqcnt no freqs\n");
# read nav data in hash
$nav=new IO::Zlib;
if($nav->open($FG_NAVAIDS, "r"))
{
printf("Parsing ".$FG_NAVAIDS." ...\n");
while ($z=<$nav>) {
chomp($z);
last if ($z =~ /^99/);
$navlinecount++;
if ($z =~ /^3\s+(-?\d+\.\d+)\s+(-?\d+\.\d+)\s+\d+\s+(\d+)\s+\d+\s+-?\d+\.\d+\s+([A-Z]+)\s+(.*)\s*$/) {
# VOR/DME Nav
$lat=$1;
$lon=$2;
$freq=sprintf("%3.3f",$3/100);
$code=$4;
$text = trim_all($5);
$NAV{$code}{'lat'}=$lat;
$NAV{$code}{'lon'}=$lon;
$NAV{$code}{'frq'}=$freq;
$NAV{$code}{'text'}=$text;
$vordmeadded++;
}
}
}
else
{
die("Cannot open $FG_NAVAIDS :$!\n");
}
$nav->close;
prt("Done $navlinecount nav.dat lines, adding $vordmeadded VOR/DME(3) records\n");
# get output files open
# open positions file
$positions = new IO::File;
$positions->open($out_pos, "w") || die("Cannot open $out_pos for writing: $!\n");
# open phonebook file
#$phonebook = new IO::File;
#$phonebook->open($out_phon, "w") || die("Cannot open $out_phon for writing: $!\n");
# open fgcom.conf
$extensions = new IO::File;
$extensions->open($out_fil1, "w") || die("Cannot open $out_fil1 for writing: $!\n");
#print $phonebook "File generated by $pgmname - ".join ' ', $dt->ymd, $dt->hms." UTC\n";
#print $phonebook keys(%APT)." airports and ".keys(%NAV)." navaids are present in this file (".$numberOfFrequenciesSkipped." freq skipped) \n\n";
#print $phonebook "ICAO Decription FRQ Phone no. Name\n";
#print $phonebook "-" x 79,"\n";
# read pre data for fgcom.conf;
print $extensions $extensions_pre;
printf("Writing airports data ...\n");
# Print all known airports
foreach $airport (sort(keys(%APT))) {
foreach $f (keys(%{$APT{$airport}{'com'}})) {
$ssf = substr($f, -2);
$type = $APT{$airport}{'com'}{$f};
if ($trim_to_20 && (length($type) > 20)) {
$type = substr($type,0,20);
}
if ($ssf == 30 || $ssf == 80) {
next;
printf("Found a 8.33KHz freq !!! $airport\n");
}
$icao_number = icao2number($airport);
# write positions APT
print $positions Encode::encode( "utf8", $airport.",".$f.",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
# write phonebook
#$tmp = sprintf("%4s %-20s %3.3f %-.16s %-20s\n",$airport,$type,$f,"01".$icao_number.$f*1000,$APT{$airport}{'text'});
#print $phonebook Encode::encode( "utf8", $tmp);
# write extensions.conf
$tmp = "; $airport $type $f - ".$APT{$airport}{'text'}."\n";
###prt($tmp);
print $extensions Encode::encode( "utf8", $tmp);
if ( $APT{$airport}{'com'}{$f} =~ /ATIS$/ ) {
# ATIS playback extension
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",1,SendText($airport ".$APT{$airport}{'text'}." $f ".$APT{$airport}{'com'}{$f}.")\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",n,SendURL(http://www.the-airport-guide.com/search.php?by=icao&search=$airport)\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",n,Macro(atis)\n");
if ($ssf == 00) {
print $positions Encode::encode( "utf8", $airport.",".($f+0.005).",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
} elsif ($ssf == 25) {
print $positions Encode::encode( "utf8", $airport.",".($f+0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
print $positions Encode::encode( "utf8", $airport.",".($f-0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000-5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
} elsif ($ssf == 50) {
print $positions Encode::encode( "utf8", $airport.",".($f+0.005).",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
} elsif($ssf == 75) {
print $positions Encode::encode( "utf8", $airport.",".($f+0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
print $positions Encode::encode( "utf8", $airport.",".($f-0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000-5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
}
# ATIS record extension
print $extensions Encode::encode( "utf8", "exten => 99$icao_number".($f*1000).",1,SendText($airport ".$APT{$airport}{'text'}." $f Record-".$APT{$airport}{'com'}{$f}.")\n");
print $extensions Encode::encode( "utf8", "exten => 99$icao_number".($f*1000).",n,Macro(record-atis)\n");
}
else
{
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",1,SendText($airport ".$APT{$airport}{'text'}." $f ".$APT{$airport}{'com'}{$f}.")\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",n,SendURL(http://www.the-airport-guide.com/search.php?by=icao&search=$airport)\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",n,Macro(com)\n");
if ($ssf == 00) {
print $positions Encode::encode( "utf8", $airport.",".($f+0.005).",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
} elsif ($ssf == 25) {
print $positions Encode::encode( "utf8", $airport.",".($f+0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
print $positions Encode::encode( "utf8", $airport.",".($f-0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000-5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
} elsif ($ssf == 50) {
print $positions Encode::encode( "utf8", $airport.",".($f+0.005).",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
} elsif ($ssf == 75) {
print $positions Encode::encode( "utf8", $airport.",".($f+0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
print $positions Encode::encode( "utf8", $airport.",".($f-0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000-5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
}
}
print $extensions ";\n";
}
}
printf("Writing navaids data ...\n");
# write VORs to files
foreach $vor (sort(keys(%NAV))) {
$icao_number = icao2number($vor);
# write positions NAV
print $positions Encode::encode( "utf8", $vor.",".$NAV{$vor}{'frq'}.",".$NAV{$vor}{'lat'}.",".$NAV{$vor}{'lon'}.",VOR,".$NAV{$vor}{'text'}."\n");
# write phonebook
#$tmp = sprintf("%4s %-20s %3.3f %-.16s %-20s\n",$vor,"",$NAV{$vor}{'frq'},"01".$icao_number.$NAV{$vor}{'frq'}*1000,$NAV{$vor}{'text'});
#print $phonebook Encode::encode( "utf8", $tmp);
# write extensions.conf
print $extensions Encode::encode( "utf8", "; VOR $vor $NAV{$vor}{'frq'} - $NAV{$vor}{'text'}\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($NAV{$vor}{'frq'}*1000).",1,SendText($vor ".$NAV{$vor}{'text'}." ".$NAV{$vor}{'frq'}.")\n");
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($NAV{$vor}{'frq'}*1000).",n,Macro(vor,$vor)\n");
}
# close positions
$positions->close;
# close extensions
$extensions->close;
# close phonebook
#print $phonebook $phonebook_post;
#$phonebook->close;
prt("Done file outputs... $out_fil1 and $out_pos...\n");
exit(0);
###############################################
sub give_help() {
prt("\n");
prt("$pgmname, version $VERS\n");
prt("Usage:\n");
prt(" --help (-h, -?) = This help and exit(2)\n");
prt("\n");
prt("Input files:\n");
prt(" --air <file> (-a) = Name of airports file. (def=$FG_AIRPORTS)\n");
prt(" --nav <file> (-n) = Name of navaids file. (def=$FG_NAVAIDS)\n");
prt("\n");
prt("Output files:\n");
prt(" --conf <file> (-c) = Name of conf file. (def=$out_fil1)\n");
prt(" --pos <file> (-p) = Name of position file. (def=$out_pos)\n");
#prt(" --book <file> (-b) = Name of phonebook file. (def=$out_phon)\n");
prt("\n");
prt("Purpose:\n");
prt("Read the input files, and output the extensions (conf),\n");
prt("used to configure the asterisk voip server, and output the positions file,\n");
prt("used by standalone fgcom to establish the location of the caller.\n");
prt("\n");
exit(2);
}
sub need_arg {
my ($arg,@av) = @_;
die("ERROR: [$arg] must have a following argument!\n") if (!@av);
}
sub parse_args {
my @av = @_;
my ($arg,$sarg);
my $bad = 0;
while (@av) {
$arg = $av[0];
### prt("ARG [$arg]\n");
if ($arg =~ /^-/) {
$sarg = substr($arg,1);
$sarg = substr($sarg,1) while ($sarg =~ /^-/);
if ($sarg =~ /^a/) {
need_arg(@av);
shift @av;
$sarg = $av[0];
$FG_AIRPORTS = $sarg;
} elsif ($sarg =~ /^n/) {
need_arg(@av);
shift @av;
$sarg = $av[0];
$FG_NAVAIDS = $sarg;
} elsif ($sarg =~ /^c/) {
need_arg(@av);
shift @av;
$sarg = $av[0];
$out_fil1 = $sarg;
} elsif ($sarg =~ /^p/) {
need_arg(@av);
shift @av;
$sarg = $av[0];
$out_pos = $sarg;
# } elsif ($sarg =~ /^b/) {
# need_arg(@av);
# shift @av;
# $sarg = $av[0];
# $out_phon = $sarg;
} elsif (($sarg =~ /^h/)||($sarg eq '?')) {
give_help();
} else {
die("ERROR: Unknown argument [$arg]\n");
}
} else {
die("ERROR: Unknown argument [$arg]\n");
}
shift @av;
}
$arg = '';
if (! -f $FG_AIRPORTS ) {
$arg .= "Error: Unable to locate [$FG_AIRPORTS]!\n";
$bad++;
}
if (! -f $FG_NAVAIDS ) {
$arg .= "Error: Unable to locate [$FG_NAVAIDS]!\n";
$bad++;
}
die("Invalid input file!\n$arg") if ($bad);
}
# eof

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
add_executable(fgelev fgelev.cxx)
target_link_libraries(fgelev SimGearScene SimGearCore)
install(TARGETS fgelev RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

250
utils/fgelev/fgelev.cxx Normal file
View File

@@ -0,0 +1,250 @@
// fgelev.cxx -- compute scenery elevation
//
// Copyright (C) 2009 - 2012 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <iostream>
#include <cstdlib>
#include <iomanip>
#include <osg/ArgumentParser>
#include <osg/Image>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/ResourceManager.hxx>
#include <simgear/bvh/BVHNode.hxx>
#include <simgear/bvh/BVHLineSegmentVisitor.hxx>
#include <simgear/bvh/BVHPager.hxx>
#include <simgear/bvh/BVHPageNode.hxx>
#include <simgear/bvh/BVHMaterial.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/model/BVHPageNodeOSG.hxx>
#include <simgear/scene/model/ModelRegistry.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/OptionsReadFileCallback.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/tgdb/userdata.hxx>
namespace sg = simgear;
class Visitor : public sg::BVHLineSegmentVisitor {
public:
Visitor(const SGLineSegmentd& lineSegment, sg::BVHPager& pager) :
BVHLineSegmentVisitor(lineSegment, 0),
_pager(pager)
{ }
virtual ~Visitor()
{ }
virtual void apply(sg::BVHPageNode& node)
{
// we have a non threaded pager so load just right here.
_pager.use(node);
BVHLineSegmentVisitor::apply(node);
}
private:
sg::BVHPager& _pager;
};
// Short circuit reading image files.
class ReadFileCallback : public sg::OptionsReadFileCallback {
public:
virtual ~ReadFileCallback()
{ }
virtual osgDB::ReaderWriter::ReadResult readImage(const std::string& name, const osgDB::Options*)
{ return new osg::Image; }
};
static bool
intersect(sg::BVHNode& node, sg::BVHPager& pager,
const SGVec3d& start, SGVec3d& end, double offset, const simgear::BVHMaterial** material)
{
SGVec3d perp = offset*perpendicular(start - end);
Visitor visitor(SGLineSegmentd(start + perp, end + perp), pager);
node.accept(visitor);
if (visitor.empty())
return false;
end = visitor.getLineSegment().getEnd();
* material = visitor.getMaterial();
return true;
}
int
main(int argc, char** argv)
{
/// Read arguments and environment variables.
// use an ArgumentParser object to manage the program arguments.
osg::ArgumentParser arguments(&argc, argv);
unsigned expire;
if (arguments.read("--expire", expire)) {
} else expire = 10;
bool printSolidness = arguments.read("--print-solidness");
std::string fg_root;
if (arguments.read("--fg-root", fg_root)) {
} else if (const char *fg_root_env = std::getenv("FG_ROOT")) {
fg_root = fg_root_env;
} else {
fg_root = PKGLIBDIR;
}
SGPath fg_scenery;
std::string s;
if (arguments.read("--fg-scenery", s)) {
fg_scenery = SGPath::fromLocal8Bit(s.c_str());
} else if (std::getenv("FG_SCENERY")) {
fg_scenery = SGPath::fromEnv("FG_SCENERY");
} else {
SGPath path(fg_root);
path.append("Scenery");
fg_scenery = path;
}
SGSharedPtr<SGPropertyNode> props = new SGPropertyNode;
try {
SGPath preferencesFile = fg_root;
preferencesFile.append("defaults.xml");
readProperties(preferencesFile, props);
} catch (...) {
// In case of an error, at least make summer :)
props->getNode("sim/startup/season", true)->setStringValue("summer");
SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading FlightGear preferences.\n"
<< "Probably FG_ROOT is not properly set.");
}
bool use_vpb = arguments.read("--use-vpb");
props->setBoolValue("/scenery/use-vpb", use_vpb);
SGSceneFeatures::instance()->setVPBActive(use_vpb);
/// now set up the simgears required model stuff
simgear::ResourceManager::instance()->addBasePath(fg_root, simgear::ResourceManager::PRIORITY_DEFAULT);
// Just reference simgears reader writer stuff so that the globals get
// pulled in by the linker ...
simgear::ModelRegistry::instance();
sgUserDataInit(props.get());
SGMaterialLibPtr ml = new SGMaterialLib;
SGPath mpath(fg_root);
mpath.append("Materials/default/materials.xml");
try {
ml->load(fg_root, mpath.local8BitStr(), props);
} catch (...) {
SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading FlightGear materials.\n"
<< "Probably FG_ROOT is not properly set.");
}
simgear::SGModelLib::init(fg_root, props);
// Set up the reader/writer options
osg::ref_ptr<simgear::SGReaderWriterOptions> options;
if (osgDB::Options* ropt = osgDB::Registry::instance()->getOptions())
options = new simgear::SGReaderWriterOptions(*ropt);
else
options = new simgear::SGReaderWriterOptions;
osgDB::convertStringPathIntoFilePathList(fg_scenery.local8BitStr(),
options->getDatabasePathList());
options->setMaterialLib(ml);
options->setPropertyNode(props);
options->setReadFileCallback(new ReadFileCallback);
options->setPluginStringData("SimGear::FG_ROOT", fg_root);
// we do not need the builtin boundingvolumes
options->setPluginStringData("SimGear::BOUNDINGVOLUMES", "OFF");
// And we only want terrain, no objects on top.
options->setPluginStringData("SimGear::FG_ONLY_TERRAIN", "ON");
string_list scenerySuffixes = {"Terrain"}; // Just Terrain
options->setSceneryPathSuffixes(scenerySuffixes);
props->getNode("sim/rendering/random-objects", true)->setBoolValue(false);
props->getNode("sim/rendering/random-vegetation", true)->setBoolValue(false);
std::string bvhFile = "w180s90-360x180.spt";
if (arguments.read("--tile-file", s))
bvhFile = s;
// Here, all arguments are processed
arguments.reportRemainingOptionsAsUnrecognized();
arguments.writeErrorMessages(std::cerr);
// Get the whole world bvh tree
SGSharedPtr<sg::BVHNode> node;
node = sg::BVHPageNodeOSG::load(bvhFile, options);
// if no model has been successfully loaded report failure.
if (!node.valid()) {
SG_LOG(SG_GENERAL, SG_ALERT, arguments.getApplicationName()
<< ": No data loaded");
return EXIT_FAILURE;
}
// We assume that the above is a paged database.
sg::BVHPager pager;
while (std::cin.good()) {
// Increment the paging relevant number
pager.setUseStamp(1 + pager.getUseStamp());
// and expire everything not accessed for the past 30 requests
pager.update(expire);
std::string id;
std::cin >> id;
double lon, lat;
std::cin >> lon >> lat;
if (std::cin.fail())
return EXIT_FAILURE;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
SGVec3d start = SGVec3d::fromGeod(SGGeod::fromDegM(lon, lat, 10000));
SGVec3d end = SGVec3d::fromGeod(SGGeod::fromDegM(lon, lat, -1000));
const simgear::BVHMaterial* material = NULL;
// Try to find an intersection
bool found = intersect(*node, pager, start, end, 0, &material);
double scale = 1e-5;
while (!found && scale <= 1) {
found = intersect(*node, pager, start, end, scale, &material);
scale *= 2;
}
if (1e-5 < scale)
std::cerr << "Found hole of minimum diameter "
<< scale << "m at lon = " << lon
<< "deg lat = " << lat << "deg" << std::endl;
std::cout << id << ": ";
string solid = material && material->get_solid() ? "solid" : "-";
if (!found) {
std::cout << "-1000" << std::endl;
} else {
SGGeod geod = SGGeod::fromCart(end);
std::cout << std::fixed << std::setprecision(3) << geod.getElevationM();
if( printSolidness )
std::cout << " " << (material && material->get_solid() ? "solid" : "-");
std::cout << std::endl;
}
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,75 @@
//
// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef _WIN32
# include <direct.h> // for getcwd()
#else // !_WIN32
# include <unistd.h>
#endif
#include "ApplicationProperties.hxx"
double
ApplicationProperties::getDouble (const char *name, const double def) {
const SGPropertyNode_ptr n (ApplicationProperties::Properties->getNode (name, false));
if (n == NULL) {
return def;
}
return n->getDoubleValue ();
}
SGPath
ApplicationProperties::GetCwd () {
SGPath path (".");
char buf[512];
char *cwd (getcwd (buf, 511));
buf[511] = '\0';
if (cwd) {
path = SGPath::fromLocal8Bit (cwd);
}
return path;
}
SGPath
ApplicationProperties::GetRootPath (const char *sub) {
if (sub != NULL) {
const SGPath subpath (sub);
// relative path to current working dir?
if (subpath.isRelative ()) {
SGPath path (GetCwd ());
path.append (sub);
if (path.exists ()) {
return path;
}
} else if (subpath.exists ()) {
// absolute path
return subpath;
}
}
// default: relative path to FGROOT
SGPath path (ApplicationProperties::root);
if (sub != NULL) {
path.append (sub);
}
return path;
}
string ApplicationProperties::root = ".";
SGPropertyNode_ptr ApplicationProperties::Properties = new SGPropertyNode;

View File

@@ -0,0 +1,32 @@
//
// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#pragma once
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
class ApplicationProperties {
public:
static double getDouble (const char *name, const double def = 0.0);
static SGPath GetRootPath (const char *subDir = NULL);
static SGPath GetCwd ();
static SGPropertyNode_ptr Properties;
static std::string root;
};

View File

@@ -0,0 +1,143 @@
find_package(PNG QUIET)
find_package(OpenGL QUIET)
find_package(Freetype QUIET)
find_package(GLUT QUIET)
find_package(GLEW QUIET)
if(NOT ${GLUT_FOUND})
message(WARNING "GLUT NOT found, can't build FGPanel")
set(WITH_FGPANEL 0)
return()
endif()
if(NOT ${GLEW_FOUND})
message(WARNING "GLEW NOT found, can't build FGPanel")
set(WITH_FGPANEL 0)
return()
endif()
if((NOT PNG_FOUND) OR (NOT OPENGL_FOUND) OR (NOT FREETYPE_FOUND))
message(WARNING "FGPanel enabled, but some dependencies are missing")
message(STATUS "libPNG: ${PNG_FOUND}")
message(STATUS "OpenGL: ${OPENGL_FOUND}")
message(STATUS "Freetype: ${FREETYPE_FOUND}")
return()
endif()
find_path(BCMHOST_INCLUDE_DIR
NAMES bcm_host.h
PATHS /opt/vc/include
NO_DEFAULT_PATH
)
set(TARGET_SOURCES
main.cxx
ApplicationProperties.hxx
ApplicationProperties.cxx
FGCroppedTexture.hxx
FGCroppedTexture.cxx
FGDummyTextureLoader.hxx
FGDummyTextureLoader.cxx
FGFontCache.cxx
FGFontCache.hxx
FGGLApplication.cxx
FGGLApplication.hxx
FGGroupLayer.cxx
FGGroupLayer.hxx
FGInstrumentLayer.cxx
FGInstrumentLayer.hxx
FGLayeredInstrument.cxx
FGLayeredInstrument.hxx
FGPanel.cxx
FGPanel.hxx
FGPanelApplication.cxx
FGPanelApplication.hxx
FGPanelInstrument.cxx
FGPanelInstrument.hxx
FGPanelProtocol.cxx
FGPanelProtocol.hxx
FGPanelTransformation.cxx
FGPanelTransformation.hxx
FGPNGTextureLoader.cxx
FGPNGTextureLoader.hxx
FGRGBTextureLoader.cxx
FGRGBTextureLoader.hxx
FGSwitchLayer.cxx
FGSwitchLayer.hxx
FGTextLayer.cxx
FGTextLayer.hxx
FGTexturedLayer.cxx
FGTexturedLayer.hxx
panel_io.cxx
panel_io.hxx
GL_utils.cxx
GL_utils.hxx
)
add_executable(fgpanel ${TARGET_SOURCES})
target_link_libraries(fgpanel
SimGearCore
${PNG_LIBRARIES}
${FREETYPE_LIBRARIES}
OpenGL::GL
${GLUT_LIBRARIES}
${GLEW_LIBRARIES}
)
target_include_directories(fgpanel PUBLIC
${FREETYPE_INCLUDE_DIRS}
${PNG_INCLUDE_DIR}
${GLEW_INCLUDE_DIRS}
)
if(MSVC)
target_compile_definitions(fgpanel PUBLIC
-DFREEGLUT_LIB_PRAGMAS=0
)
endif(MSVC)
if(BCMHOST_INCLUDE_DIR)
message(STATUS "found Raspberry Pi")
add_executable(fgpanel-egl ${TARGET_SOURCES}
GLES_utils.cxx
GLES_utils.hxx
)
target_include_directories(fgpanel-egl PUBLIC
${FREETYPE_INCLUDE_DIRS}
${PNG_INCLUDE_DIR}
${BCMHOST_INCLUDE_DIR}
${BCMHOST_INCLUDE_DIR}/interface/vcos/pthreads
${BCMHOST_INCLUDE_DIR}/interface/vmcs_host/linux
)
target_link_libraries(fgpanel-egl
SimGearCore
${PNG_LIBRARIES}
${FREETYPE_LIBRARIES}
-lbrcmGLESv2 -lbrcmEGL -lm -lbcm_host -L/opt/vc/lib
)
target_compile_definitions(fgpanel-egl PUBLIC
-D_GLES2 -D_RPI
)
target_compile_definitions(fgpanel PUBLIC
-D_RPI
)
message(STATUS "FGPanel (Raspberry Pi) : ENABLED")
install(TARGETS fgpanel-egl RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
else(BCMHOST_INCLUDE_DIR)
message(STATUS "FGPanel (Raspberry Pi) : DISABLED")
endif(BCMHOST_INCLUDE_DIR)
if(WITH_FGPANEL)
message(STATUS "FGPanel : ENABLED")
install(TARGETS fgpanel RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
else(WITH_FGPANEL)
message(WARNING "FGPanel : DISABLED")
endif(WITH_FGPANEL)

View File

@@ -0,0 +1,127 @@
//
// Written by David Megginson, started January 2000.
// Adopted for standalone fgpanel application by Torsten Dreyer, August 2009
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
#include "ApplicationProperties.hxx"
#include "FGCroppedTexture.hxx"
GLuint FGCroppedTexture::s_current_bound_texture = 0;
map <string, GLuint> FGCroppedTexture::s_cache;
map <string, FGTextureLoaderInterface*> FGCroppedTexture::s_TextureLoader;
FGDummyTextureLoader FGCroppedTexture::s_DummyTextureLoader;
FGCroppedTexture::FGCroppedTexture (const string &path,
const float minX, const float minY,
const float maxX, const float maxY) :
m_path (path),
m_minX (minX), m_minY (minY),
m_maxX (maxX), m_maxY (maxY),
m_texture(0) {
}
FGCroppedTexture::~FGCroppedTexture () {
}
void
FGCroppedTexture::setPath (const string &path) {
m_path = path;
}
const string &
FGCroppedTexture::getPath () const {
return m_path;
}
void
FGCroppedTexture::setCrop (const float minX, const float minY, const float maxX, const float maxY) {
m_minX = minX; m_minY = minY; m_maxX = maxX; m_maxY = maxY;
}
void
FGCroppedTexture::registerTextureLoader (const string &extension,
FGTextureLoaderInterface * const loader) {
if (s_TextureLoader.count (extension) == 0) {
s_TextureLoader[extension] = loader;
}
}
float
FGCroppedTexture::getMinX () const {
return m_minX;
}
float
FGCroppedTexture::getMinY () const {
return m_minY;
}
float
FGCroppedTexture::getMaxX () const {
return m_maxX;
}
float
FGCroppedTexture::getMaxY () const {
return m_maxY;
}
GLuint
FGCroppedTexture::getTexture () const {
return m_texture;
}
void
FGCroppedTexture::bind (const GLint Textured_Layer_Sampler_Loc) {
if (m_texture == 0) {
SG_LOG (SG_COCKPIT,
SG_DEBUG,
"First bind of texture " << m_path);
if (s_cache.count (m_path) > 0 ) {
m_texture = s_cache[m_path];
SG_LOG (SG_COCKPIT,
SG_DEBUG,
"Using texture " << m_path << " from cache (#" << m_texture << ")");
} else {
const SGPath path (ApplicationProperties::GetRootPath (m_path.c_str ()));
const string extension (path.extension ());
FGTextureLoaderInterface *loader (&s_DummyTextureLoader);
if (s_TextureLoader.count (extension) == 0) {
SG_LOG (SG_COCKPIT,
SG_ALERT,
"Can't handle textures of type " << extension);
} else {
loader = s_TextureLoader[extension];
}
m_texture = loader->loadTexture (path.local8BitStr ());
SG_LOG (SG_COCKPIT,
SG_DEBUG,
"Texture " << path << " loaded from file as #" << m_texture);
s_cache[m_path] = m_texture;
}
}
if (s_current_bound_texture != m_texture) {
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, m_texture);
glUniform1i (Textured_Layer_Sampler_Loc, 0);
s_current_bound_texture = m_texture;
}
}

View File

@@ -0,0 +1,69 @@
//
// Written by David Megginson, started January 2000.
// Adopted for standalone fgpanel application by Torsten Dreyer, August 2009
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef FGCROPPEDTEXTURE_HXX
#define FGCROPPEDTEXTURE_HXX
#include <map>
#include <simgear/structure/SGSharedPtr.hxx>
#include "FGDummyTextureLoader.hxx"
/**
* Cropped texture (should migrate out into FGFS).
*
* This structure wraps an SSG texture with cropping information.
*/
class FGCroppedTexture : public SGReferenced {
public:
FGCroppedTexture (const string &path,
const float minX = 0.0, const float minY = 0.0,
const float maxX = 1.0, const float maxY = 1.0);
virtual ~FGCroppedTexture ();
virtual void setPath (const string &path);
virtual const string &getPath () const;
virtual void setCrop (const float minX, const float minY, const float maxX, const float maxY);
static void registerTextureLoader (const string &extension,
FGTextureLoaderInterface * const loader);
virtual float getMinX () const;
virtual float getMinY () const;
virtual float getMaxX () const;
virtual float getMaxY () const;
GLuint getTexture () const;
virtual void bind (const GLint Textured_Layer_Sampler_Loc);
private:
string m_path;
float m_minX, m_minY, m_maxX, m_maxY;
GLuint m_texture;
static GLuint s_current_bound_texture;
static map <string, GLuint> s_cache;
static map <string, FGTextureLoaderInterface*> s_TextureLoader;
static FGDummyTextureLoader s_DummyTextureLoader;
};
typedef SGSharedPtr <FGCroppedTexture> FGCroppedTexture_ptr;
#endif

View File

@@ -0,0 +1,41 @@
//
// Written by David Megginson, started January 2000.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "FGDummyTextureLoader.hxx"
GLuint
FGDummyTextureLoader::loadTexture (const string& filename) {
GLuint texture;
glGenTextures (1, &texture);
glBindTexture (GL_TEXTURE_2D, texture);
GLubyte image[ 2 * 2 * 3 ];
/* Red and white chequerboard */
image [ 0] = 255; image [ 1] = 0; image [ 2] = 0;
image [ 3] = 255; image [ 4] = 255; image [ 5] = 255;
image [ 6] = 255; image [ 7] = 255; image [ 8] = 255;
image [ 9] = 255; image [10] = 0; image [11] = 0;
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0,
GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*) image);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
return texture;
}

View File

@@ -0,0 +1,29 @@
//
// Written by David Megginson, started January 2000.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#pragma once
#include <string.h>
#include "FGTextureLoaderInterface.hxx"
class FGDummyTextureLoader : public FGTextureLoaderInterface {
public:
virtual GLuint loadTexture (const std::string& filename);
};

View File

@@ -0,0 +1,186 @@
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <math.h>
#include "FGGLApplication.hxx"
#include "ApplicationProperties.hxx"
#include "FGFontCache.hxx"
////////////////////////////////////////////////////////////////////////
// FGFontCache class.
////////////////////////////////////////////////////////////////////////
const unsigned short FGFontCache::First_Printable_Char = 32;
const unsigned short FGFontCache::Last_Printable_Char = 127;
FGFontCache::FGFontCache () :
m_Face_Map (),
m_Current_Face_Ptr (NULL),
m_Pos_Map (),
m_Current_Pos (0) {
if (FT_Init_FreeType (&m_Ft)) {
SG_LOG (SG_COCKPIT, SG_ALERT, "Could not init freetype library");
}
for (unsigned int Index = 0; Index < Texture_Size * Texture_Size; ++Index) {
m_Texture[Index] = char (0);
}
glGenTextures (1, &m_Glyph_Texture);
}
FGFontCache::~FGFontCache () {
for (Face_Map_Type::iterator It = m_Face_Map.begin (); It != m_Face_Map.end (); ++It) {
delete (It->second);
}
m_Face_Map.clear ();
glDeleteTextures (1, &m_Glyph_Texture);
}
bool
FGFontCache::Set_Font (const string& Font_Name,
const float Size,
GLuint &Glyph_Texture) {
if (m_Face_Map.find (Font_Name) != m_Face_Map.end ()) {
m_Current_Face_Ptr = m_Face_Map[Font_Name];
} else {
FT_Face * const Face_Ptr (new FT_Face);
if (FT_New_Face (m_Ft, Font_Name.c_str (), 0, Face_Ptr)) {
SG_LOG (SG_COCKPIT, SG_ALERT, "Could not open font : " + Font_Name);
return false;
}
m_Face_Map.insert (pair <string, FT_Face *> (Font_Name, Face_Ptr));
m_Current_Face_Ptr = Face_Ptr;
}
if (m_Current_Face_Ptr != NULL) {
FT_Set_Pixel_Sizes (*m_Current_Face_Ptr, 0, Size);
} else {
return false;
}
const string Key_Str (Font_Name + "_" + Get_Size (Size));
if (m_Pos_Map.find (Key_Str) != m_Pos_Map.end ()) {
m_Current_Pos = m_Pos_Map[Key_Str];
} else {
m_Current_Pos = m_Pos_Map.size ();
for (unsigned short ASCII_Code = First_Printable_Char;
ASCII_Code < Last_Printable_Char;
++ASCII_Code) {
if (FT_Load_Char (*m_Current_Face_Ptr, char (ASCII_Code), FT_LOAD_RENDER)) {
SG_LOG (SG_COCKPIT, SG_ALERT, "Could not load character : " << char (ASCII_Code));
} else {
unsigned int Line;
unsigned int Column;
const FT_GlyphSlot Glyph ((*m_Current_Face_Ptr)->glyph);
for (unsigned int Row = 0; Row < Glyph->bitmap.rows; ++Row) {
for (unsigned int Width = 0; Width < Glyph->bitmap.width; ++Width) {
Get_Pos (ASCII_Code, Row, Width, Line, Column);
m_Texture[Line * Texture_Size + Column] =
Glyph->bitmap.buffer[Row * Glyph->bitmap.width + Width];
}
}
}
}
glBindTexture (GL_TEXTURE_2D, m_Glyph_Texture);
/* We require 1 byte alignment when uploading texture data */
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
/* Clamping to edges is important to prevent artifacts when scaling */
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
/* Linear filtering usually looks best for text */
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D (GL_TEXTURE_2D,
0,
GL_ALPHA,
Texture_Size,
Texture_Size,
0,
GL_ALPHA,
GL_UNSIGNED_BYTE,
m_Texture);
m_Pos_Map.insert (pair <string, unsigned int> (Key_Str, m_Current_Pos));
}
Glyph_Texture = m_Glyph_Texture;
return true;
}
bool
FGFontCache::Get_Char (const char Char,
int &X, int &Y,
int &Left, int &Bottom,
int &W, int &H,
double &X1, double &Y1,
double &X2, double &Y2) const {
if (m_Current_Face_Ptr != NULL) {
if (FT_Load_Char (*m_Current_Face_Ptr, Char, FT_LOAD_RENDER)) {
SG_LOG (SG_COCKPIT, SG_ALERT, "Could not load character : " + Char);
return false;
} else {
const FT_GlyphSlot Glyph ((*m_Current_Face_Ptr)->glyph);
Left = Glyph->bitmap_left;
W = Glyph->bitmap.width;
H = Glyph->bitmap.rows;
Bottom = Glyph->bitmap_top - H;
X += (Glyph->advance.x >> 6);
Y += (Glyph->advance.y >> 6);
Get_Relative_Pos (int (Char), 0, 0, X1, Y1);
Get_Relative_Pos (int (Char), Glyph->bitmap.rows, Glyph->bitmap.width, X2, Y2);
return true;
}
} else {
return false;
}
}
void
FGFontCache::Get_Pos (const unsigned short ASCII_Code,
const unsigned short Row,
const unsigned short Width,
unsigned int &Line,
unsigned int &Column) const {
const unsigned short Font_Size (32);
Line = (((((ASCII_Code - First_Printable_Char) * Font_Size) / Texture_Size) + 3 * m_Current_Pos) * Font_Size) + Row;
Column = (((ASCII_Code - First_Printable_Char) * Font_Size) % Texture_Size) + Width;
}
void
FGFontCache::Get_Relative_Pos (const unsigned short ASCII_Code,
const unsigned short Row,
const unsigned short Width,
double &X,
double &Y) const {
unsigned int Line;
unsigned int Column;
Get_Pos (ASCII_Code, Row, Width, Line, Column);
X = double (Column) / double (Texture_Size);
Y = double (Line) / double (Texture_Size);
}
string
FGFontCache::Get_Size (const float Size) {
const int Half_Size (int (round (2.0 * Size)));
const int Int_Part (Half_Size / 2);
const int Dec_Part ((Half_Size % 2) ? 5 : 0);
stringstream Result_SS;
Result_SS << Int_Part << "." << Dec_Part;
return Result_SS.str ();
}

View File

@@ -0,0 +1,77 @@
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#pragma once
#include <string>
#include <map>
#include <ft2build.h>
#include FT_FREETYPE_H
#if defined (SG_MAC)
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#elif defined (_GLES2)
#include <GLES2/gl2.h>
#else
#include <GL/glew.h> // Must be included before <GL/gl.h>
#include <GL/gl.h>
#include <GL/glut.h>
#endif
/**
* A class to keep all fonts available for future use.
* This also assures a font isn't resident more than once.
*/
class FGFontCache {
public:
FGFontCache ();
~FGFontCache ();
bool Set_Font (const std::string& Font_Name,
const float Size,
GLuint &Glyph_Texture);
bool Get_Char (const char Char,
int &X, int &Y,
int &Left, int &Bottom,
int &W, int &H,
double &X1, double &Y1, // Top (Y1) left (X1)
double &X2, double &Y2) const; // Bottom (Y2) right (X2)
private:
void Get_Pos (const unsigned short ASCII_Code,
const unsigned short Row,
const unsigned short Width,
unsigned int &Line,
unsigned int &Column) const;
void Get_Relative_Pos (const unsigned short ASCII_Code,
const unsigned short Row,
const unsigned short Width,
double &X,
double &Y) const;
static std::string Get_Size (const float Size);
static const unsigned short First_Printable_Char;
static const unsigned short Last_Printable_Char;
static const unsigned int Texture_Size = 1024;
FT_Library m_Ft;
typedef std::map <std::string, FT_Face *> Face_Map_Type;
Face_Map_Type m_Face_Map;
FT_Face *m_Current_Face_Ptr;
char m_Texture[Texture_Size * Texture_Size];
typedef std::map <std::string, unsigned int> Pos_Map_Type;
Pos_Map_Type m_Pos_Map;
unsigned int m_Current_Pos;
GLuint m_Glyph_Texture;
};

View File

@@ -0,0 +1,156 @@
//
// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <iostream>
#include <exception>
#include <stdio.h>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#if defined (SG_MAC)
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#elif defined (_GLES2)
#include <GLES2/gl2.h>
#include "GLES_utils.hxx"
#else
#include <GL/glew.h> // Must be included before <GL/gl.h>
#include <GL/gl.h>
#include <GL/freeglut.h>
#endif
#include <simgear/compiler.h>
#include "FGGLApplication.hxx"
using namespace std;
FGGLApplication *FGGLApplication::application = NULL;
FGGLApplication::FGGLApplication (const char * a_name, int argc, char **argv) :
gameMode (false),
name (a_name) {
if (application != NULL ) {
cerr << "Only one instance of FGGLApplication allowed!" << endl;
throw exception ();
}
application = this;
#ifdef _GLES2
GLES_utils::instance ().init ("FG Panel");
#else
glutInit (&argc, argv);
glutInitContextVersion (2, 1);
glutInitContextFlags (GLUT_CORE_PROFILE);
#endif
}
FGGLApplication::~FGGLApplication () {
#ifndef _GLES2
if (gameMode) {
glutLeaveGameMode ();
}
#endif
}
void
FGGLApplication::DisplayCallback () {
if (application) {
application->Display ();
}
}
void
FGGLApplication::IdleCallback () {
if (application) {
application->Idle ();
}
}
void
FGGLApplication::KeyCallback (const unsigned char key, const int x, const int y) {
if (application) {
application->Key (key, x, y);
}
}
void
FGGLApplication::ReshapeCallback (const int width, const int height) {
if (application) {
application->Reshape (width, height);
}
}
void
FGGLApplication::Run (const int glutMode,
const bool gameMode,
int width,
int height,
const int bpp) {
#ifndef _GLES2
glutInitDisplayMode (glutMode);
if (gameMode) {
width = glutGet (GLUT_SCREEN_WIDTH);
height = glutGet (GLUT_SCREEN_HEIGHT);
char game_mode_str[20];
snprintf (game_mode_str, 20, "%dx%d:%d", width, height, bpp);
glutGameModeString (game_mode_str);
glutEnterGameMode ();
this->gameMode = gameMode;
} else {
if (width == -1) {
width = glutGet (GLUT_SCREEN_WIDTH);
}
if (height == -1) {
height = glutGet (GLUT_SCREEN_HEIGHT);
}
glutInitDisplayMode (glutMode);
glutInitWindowSize(width, height);
windowId = glutCreateWindow (name);
}
const GLenum GLEW_err (glewInit ());
if (GLEW_OK != GLEW_err) {
cerr << "Unable to initialize GLEW " << glewGetErrorString (GLEW_err) << endl;
exit (1);
}
if (!GLEW_VERSION_2_1) { // check that the machine supports the 2.1 API.
cerr << "GLEW version 2.1 not supported : " << glewGetString (GLEW_VERSION) << endl;
exit (1);
}
cout << "OpenGL version = " << glGetString (GL_VERSION) << endl;
#endif
Init ();
#ifdef _GLES2
GLES_utils::instance ().register_keyboard_func (FGGLApplication::KeyCallback);
GLES_utils::instance ().register_idle_func (FGGLApplication::IdleCallback);
GLES_utils::instance ().register_display_func (FGGLApplication::DisplayCallback);
GLES_utils::instance ().register_reshape_func (FGGLApplication::ReshapeCallback);
GLES_utils::instance ().main_loop ();
#else
glutKeyboardFunc (FGGLApplication::KeyCallback);
glutIdleFunc (FGGLApplication::IdleCallback);
glutDisplayFunc (FGGLApplication::DisplayCallback);
glutReshapeFunc (FGGLApplication::ReshapeCallback);
glutMainLoop ();
#endif
}

View File

@@ -0,0 +1,52 @@
//
// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef __FGGLAPPLICATION_HXX
#define __FGGLAPPLICATION_HXX
class FGGLApplication {
public:
FGGLApplication (const char *a_name, int argc, char **argv);
virtual ~FGGLApplication ();
void Run (const int glutMode,
const bool gameMode,
int widht = -1,
int height = -1,
const int bpp = 32);
protected:
virtual void Key (const unsigned char key, const int x, const int y) {}
virtual void Idle () {}
virtual void Display () {}
virtual void Reshape (const int width, const int height) {}
virtual void Init () {}
int windowId;
bool gameMode;
const char *name;
static FGGLApplication *application;
private:
static void KeyCallback (const unsigned char key, const int x, const int y);
static void IdleCallback ();
static void DisplayCallback ();
static void ReshapeCallback (const int width, const int height);
};
#endif

View File

@@ -0,0 +1,43 @@
//
// Written by David Megginson, started January 2000.
// Adopted for standalone fgpanel application by Torsten Dreyer, August 2009
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "FGGroupLayer.hxx"
FGGroupLayer::FGGroupLayer () {
}
FGGroupLayer::~FGGroupLayer () {
for (unsigned int i = 0; i < m_layers.size (); ++i)
delete m_layers[i];
}
void
FGGroupLayer::draw () {
if (test ()) {
transform ();
for (unsigned int i = 0; i < m_layers.size (); ++i) {
m_layers[i]->draw ();
}
}
}
void
FGGroupLayer::addLayer (FGInstrumentLayer * const layer) {
m_layers.push_back (layer);
}

View File

@@ -0,0 +1,43 @@
//
// Written by David Megginson, started January 2000.
// Adopted for standalone fgpanel application by Torsten Dreyer, August 2009
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef FGGROUPLAYER_HXX
#define FGGROUPLAYER_HXX
#include "FGInstrumentLayer.hxx"
/**
* An instrument layer containing a group of sublayers.
*
* This class is useful for gathering together a group of related
* layers, either to hold in an external file or to work under
* the same condition.
*/
class FGGroupLayer : public FGInstrumentLayer {
public:
FGGroupLayer ();
virtual ~FGGroupLayer ();
virtual void draw ();
// transfer pointer ownership
virtual void addLayer (FGInstrumentLayer * const layer);
protected:
vector <FGInstrumentLayer *> m_layers;
};
#endif

View File

@@ -0,0 +1,100 @@
//
// Written by David Megginson, started January 2000.
// Adopted for standalone fgpanel application by Torsten Dreyer, August 2009
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <math.h>
#include <simgear/props/props.hxx>
#include "GL_utils.hxx"
#include "FGInstrumentLayer.hxx"
FGInstrumentLayer::FGInstrumentLayer (const int w, const int h) :
m_w (w), m_h (h) {
}
FGInstrumentLayer::~FGInstrumentLayer () {
for (transformation_list::iterator it = m_transformations.begin ();
it != m_transformations.end ();
++it) {
delete *it;
*it = 0;
}
}
void
FGInstrumentLayer::transform () const {
for (transformation_list::const_iterator it = m_transformations.begin ();
it != m_transformations.end ();
++it) {
FGPanelTransformation *t = *it;
if (t->test ()) {
float val (t->node == 0 ? 0.0 : t->node->getFloatValue ());
if (t->has_mod) {
val = fmod (val, t->mod);
}
if (val < t->min) {
val = t->min;
} else if (val > t->max) {
val = t->max;
}
if (t->table == 0) {
val = val * t->factor + t->offset;
} else {
val = t->table->interpolate (val) * t->factor + t->offset;
}
switch (t->type) {
case FGPanelTransformation::XSHIFT:
GL_utils::instance ().glTranslatef (val, 0.0, 0.0);
break;
case FGPanelTransformation::YSHIFT:
GL_utils::instance ().glTranslatef (0.0, val, 0.0);
break;
case FGPanelTransformation::ROTATION:
GL_utils::instance ().glRotatef (-val, 0.0, 0.0, 1.0);
break;
}
}
}
}
int
FGInstrumentLayer::getWidth () const {
return m_w;
}
int
FGInstrumentLayer::getHeight () const {
return m_h;
}
void
FGInstrumentLayer::setWidth (const int w) {
m_w = w;
}
void
FGInstrumentLayer::setHeight (const int h) {
m_h = h;
}
void
FGInstrumentLayer::addTransformation (FGPanelTransformation * const transformation) {
m_transformations.push_back (transformation);
}

View File

@@ -0,0 +1,57 @@
//
// Written by David Megginson, started January 2000.
// Adopted for standalone fgpanel application by Torsten Dreyer, August 2009
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#pragma once
#include <vector>
#include <simgear/props/condition.hxx>
#include "FGPanelTransformation.hxx"
/**
* A single layer of a multi-layered instrument.
*
* Each layer can be subject to a series of transformations based
* on current FGFS instrument readings: for example, a texture
* representing a needle can rotate to show the airspeed.
*/
class FGInstrumentLayer : public SGConditional {
public:
FGInstrumentLayer (const int w = -1, const int h = -1);
virtual ~FGInstrumentLayer ();
virtual void draw () = 0;
virtual void transform () const;
virtual int getWidth () const;
virtual int getHeight () const;
virtual void setWidth (const int w);
virtual void setHeight (const int h);
// Transfer pointer ownership!!
// DEPRECATED
virtual void addTransformation (FGPanelTransformation * const transformation);
protected:
int m_w, m_h;
typedef std::vector <FGPanelTransformation *> transformation_list;
transformation_list m_transformations;
};

View File

@@ -0,0 +1,70 @@
//
// Written by David Megginson, started January 2000.
// Adopted for standalone fgpanel application by Torsten Dreyer, August 2009
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "GL_utils.hxx"
#include "FGLayeredInstrument.hxx"
#include "FGTexturedLayer.hxx"
FGLayeredInstrument::FGLayeredInstrument (const int x, const int y, const int w, const int h) :
FGPanelInstrument (x, y, w, h) {
}
FGLayeredInstrument::~FGLayeredInstrument () {
for (layer_list::iterator it = m_layers.begin();
it != m_layers.end();
++it) {
delete *it;
*it = 0;
}
}
void
FGLayeredInstrument::draw () {
if (test ()) {
for (unsigned int i = 0; i < m_layers.size (); ++i) {
GL_utils::instance ().glPushMatrix ();
m_layers[i]->draw ();
GL_utils::instance ().glPopMatrix ();
}
}
}
int
FGLayeredInstrument::addLayer (FGInstrumentLayer * const layer) {
const int n (m_layers.size ());
if (layer->getWidth () == -1) {
layer->setWidth (getWidth ());
}
if (layer->getHeight () == -1) {
layer->setHeight (getHeight ());
}
m_layers.push_back (layer);
return n;
}
int
FGLayeredInstrument::addLayer (const FGCroppedTexture_ptr texture, const int w, const int h) {
return addLayer (new FGTexturedLayer (texture, w, h));
}
void
FGLayeredInstrument::addTransformation (FGPanelTransformation * const transformation) {
const int layer (m_layers.size () - 1);
m_layers[layer]->addTransformation (transformation);
}

Some files were not shown because too many files have changed in this diff Show More