first commit
This commit is contained in:
50
utils/CMakeLists.txt
Normal file
50
utils/CMakeLists.txt
Normal 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()
|
||||
41
utils/GPSsmooth/CMakeLists.txt
Normal file
41
utils/GPSsmooth/CMakeLists.txt
Normal 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})
|
||||
166
utils/GPSsmooth/GPSsmooth.cxx
Normal file
166
utils/GPSsmooth/GPSsmooth.cxx
Normal 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;
|
||||
}
|
||||
102
utils/GPSsmooth/GPSsmooth.hxx
Normal file
102
utils/GPSsmooth/GPSsmooth.hxx
Normal 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
586
utils/GPSsmooth/MIDG-II.cxx
Normal 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
157
utils/GPSsmooth/MIDG-II.hxx
Normal 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 );
|
||||
628
utils/GPSsmooth/MIDG_main.cxx
Normal file
628
utils/GPSsmooth/MIDG_main.cxx
Normal 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;
|
||||
}
|
||||
1004
utils/GPSsmooth/ShortDemo.txt
Normal file
1004
utils/GPSsmooth/ShortDemo.txt
Normal file
File diff suppressed because it is too large
Load Diff
609
utils/GPSsmooth/UGear.cxx
Normal file
609
utils/GPSsmooth/UGear.cxx
Normal 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
173
utils/GPSsmooth/UGear.hxx
Normal 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 );
|
||||
106
utils/GPSsmooth/UGear_command.cxx
Normal file
106
utils/GPSsmooth/UGear_command.cxx
Normal 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;
|
||||
|
||||
42
utils/GPSsmooth/UGear_command.hxx
Normal file
42
utils/GPSsmooth/UGear_command.hxx
Normal 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;
|
||||
1136
utils/GPSsmooth/UGear_main.cxx
Normal file
1136
utils/GPSsmooth/UGear_main.cxx
Normal file
File diff suppressed because it is too large
Load Diff
141
utils/GPSsmooth/UGear_opengc.hxx
Normal file
141
utils/GPSsmooth/UGear_opengc.hxx
Normal 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
|
||||
235
utils/GPSsmooth/UGear_telnet.cxx
Normal file
235
utils/GPSsmooth/UGear_telnet.cxx
Normal 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);
|
||||
}
|
||||
85
utils/GPSsmooth/UGear_telnet.hxx
Normal file
85
utils/GPSsmooth/UGear_telnet.hxx
Normal 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();
|
||||
|
||||
};
|
||||
491
utils/GPSsmooth/gps_main.cxx
Normal file
491
utils/GPSsmooth/gps_main.cxx
Normal 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
10613
utils/GPSsmooth/nmea_data.txt
Normal file
File diff suppressed because it is too large
Load Diff
40
utils/Modeller/3dconvert.cxx
Normal file
40
utils/Modeller/3dconvert.cxx
Normal 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
193
utils/Modeller/ac3d-despeckle
Executable 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;
|
||||
|
||||
|
||||
|
||||
79
utils/Modeller/animassist.c
Normal file
79
utils/Modeller/animassist.c
Normal 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
68
utils/Modeller/animcheck.pl
Executable 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;
|
||||
|
||||
585
utils/Modeller/fgfs_animation.py
Normal file
585
utils/Modeller/fgfs_animation.py
Normal 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()
|
||||
|
||||
|
||||
134
utils/Modeller/photomodel.cxx
Normal file
134
utils/Modeller/photomodel.cxx
Normal 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;
|
||||
}
|
||||
174
utils/Modeller/uv_export_svg.py
Normal file
174
utils/Modeller/uv_export_svg.py
Normal 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
308
utils/Modeller/uv_import_svg.py
Executable 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
172
utils/Modeller/uv_pack.py
Normal 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)
|
||||
|
||||
|
||||
827
utils/Modeller/yasim_import.py
Normal file
827
utils/Modeller/yasim_import.py
Normal 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()
|
||||
|
||||
6
utils/TerraSync/CMakeLists.txt
Normal file
6
utils/TerraSync/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
#add_executable(terrasync terrasync.cxx)
|
||||
|
||||
#target_link_libraries(terrasync SimGearCore)
|
||||
|
||||
#install(TARGETS terrasync RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
65
utils/TerraSync/README.txt
Normal file
65
utils/TerraSync/README.txt
Normal 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/
|
||||
515
utils/TerraSync/terrasync.cxx
Normal file
515
utils/TerraSync/terrasync.cxx
Normal 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
21
utils/climate/README
Normal 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
77
utils/climate/asc2a.cpp
Normal 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
289
utils/climate/colours.h
Normal 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
970
utils/climate/texture.cxx
Normal 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
153
utils/climate/texture.hxx
Normal 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
|
||||
|
||||
5
utils/demconvert/CMakeLists.txt
Normal file
5
utils/demconvert/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
add_executable(demconvert demconvert.cxx )
|
||||
|
||||
target_link_libraries(demconvert SimGearScene SimGearCore)
|
||||
|
||||
install(TARGETS demconvert RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
219
utils/demconvert/demconvert.cxx
Normal file
219
utils/demconvert/demconvert.cxx
Normal 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
153
utils/fgai/AIBVHPager.cxx
Normal 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
49
utils/fgai/AIBVHPager.hxx
Normal 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
|
||||
37
utils/fgai/AIEnvironment.cxx
Normal file
37
utils/fgai/AIEnvironment.cxx
Normal 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
|
||||
48
utils/fgai/AIEnvironment.hxx
Normal file
48
utils/fgai/AIEnvironment.hxx
Normal 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
228
utils/fgai/AIManager.cxx
Normal 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
77
utils/fgai/AIManager.hxx
Normal 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
103
utils/fgai/AIObject.cxx
Normal 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
92
utils/fgai/AIObject.hxx
Normal 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
104
utils/fgai/AIPhysics.cxx
Normal 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
108
utils/fgai/AIPhysics.hxx
Normal 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
|
||||
60
utils/fgai/AISubsystem.hxx
Normal file
60
utils/fgai/AISubsystem.hxx
Normal 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
27
utils/fgai/CMakeLists.txt
Normal 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})
|
||||
49
utils/fgai/HLAAirVehicle.cxx
Normal file
49
utils/fgai/HLAAirVehicle.cxx
Normal 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
|
||||
54
utils/fgai/HLAAirVehicle.hxx
Normal file
54
utils/fgai/HLAAirVehicle.hxx
Normal 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
|
||||
54
utils/fgai/HLAAirVehicleClass.cxx
Normal file
54
utils/fgai/HLAAirVehicleClass.cxx
Normal 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
|
||||
52
utils/fgai/HLAAirVehicleClass.hxx
Normal file
52
utils/fgai/HLAAirVehicleClass.hxx
Normal 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
|
||||
50
utils/fgai/HLAAircraft.cxx
Normal file
50
utils/fgai/HLAAircraft.cxx
Normal 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
|
||||
46
utils/fgai/HLAAircraft.hxx
Normal file
46
utils/fgai/HLAAircraft.hxx
Normal 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
|
||||
52
utils/fgai/HLAAircraftClass.cxx
Normal file
52
utils/fgai/HLAAircraftClass.cxx
Normal 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
|
||||
46
utils/fgai/HLAAircraftClass.hxx
Normal file
46
utils/fgai/HLAAircraftClass.hxx
Normal 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
50
utils/fgai/HLABaloon.cxx
Normal 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
41
utils/fgai/HLABaloon.hxx
Normal 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
|
||||
52
utils/fgai/HLABaloonClass.cxx
Normal file
52
utils/fgai/HLABaloonClass.cxx
Normal 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
|
||||
46
utils/fgai/HLABaloonClass.hxx
Normal file
46
utils/fgai/HLABaloonClass.hxx
Normal 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
|
||||
50
utils/fgai/HLAMPAircraft.cxx
Normal file
50
utils/fgai/HLAMPAircraft.cxx
Normal 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
|
||||
62
utils/fgai/HLAMPAircraft.hxx
Normal file
62
utils/fgai/HLAMPAircraft.hxx
Normal 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
|
||||
56
utils/fgai/HLAMPAircraftClass.cxx
Normal file
56
utils/fgai/HLAMPAircraftClass.cxx
Normal 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
|
||||
59
utils/fgai/HLAMPAircraftClass.hxx
Normal file
59
utils/fgai/HLAMPAircraftClass.hxx
Normal 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
|
||||
67
utils/fgai/HLASceneObject.cxx
Normal file
67
utils/fgai/HLASceneObject.cxx
Normal 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
|
||||
45
utils/fgai/HLASceneObject.hxx
Normal file
45
utils/fgai/HLASceneObject.hxx
Normal 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
|
||||
58
utils/fgai/HLASceneObjectClass.cxx
Normal file
58
utils/fgai/HLASceneObjectClass.cxx
Normal 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
|
||||
67
utils/fgai/HLASceneObjectClass.hxx
Normal file
67
utils/fgai/HLASceneObjectClass.hxx
Normal 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
608
utils/fgai/fgai.cxx
Normal 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();
|
||||
}
|
||||
32
utils/fgcom/CMakeLists.txt
Normal file
32
utils/fgcom/CMakeLists.txt
Normal 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
BIN
utils/fgcom/fgcom.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
1
utils/fgcom/fgcom.rc
Normal file
1
utils/fgcom/fgcom.rc
Normal file
@@ -0,0 +1 @@
|
||||
FGCOM ICON "fgcom.ico"
|
||||
644
utils/fgcom/fgcom_external.cxx
Normal file
644
utils/fgcom/fgcom_external.cxx
Normal 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
|
||||
123
utils/fgcom/fgcom_external.hxx
Normal file
123
utils/fgcom/fgcom_external.hxx
Normal 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
47174
utils/fgcom/positions.hxx
Executable file
File diff suppressed because it is too large
Load Diff
50
utils/fgcom/utils/README
Normal file
50
utils/fgcom/utils/README
Normal 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
|
||||
|
||||
|
||||
|
||||
122
utils/fgcom/utils/build_fgcom_server.sh
Normal file
122
utils/fgcom/utils/build_fgcom_server.sh
Normal 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
138836
utils/fgcom/utils/fgcom.conf
Normal file
File diff suppressed because it is too large
Load Diff
608
utils/fgcom/utils/gen_phonebook.pl
Executable file
608
utils/fgcom/utils/gen_phonebook.pl
Executable 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
|
||||
47171
utils/fgcom/utils/positions.txt
Normal file
47171
utils/fgcom/utils/positions.txt
Normal file
File diff suppressed because it is too large
Load Diff
5
utils/fgelev/CMakeLists.txt
Normal file
5
utils/fgelev/CMakeLists.txt
Normal 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
250
utils/fgelev/fgelev.cxx
Normal 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;
|
||||
}
|
||||
75
utils/fgpanel/ApplicationProperties.cxx
Normal file
75
utils/fgpanel/ApplicationProperties.cxx
Normal 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;
|
||||
32
utils/fgpanel/ApplicationProperties.hxx
Normal file
32
utils/fgpanel/ApplicationProperties.hxx
Normal 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;
|
||||
};
|
||||
143
utils/fgpanel/CMakeLists.txt
Normal file
143
utils/fgpanel/CMakeLists.txt
Normal 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)
|
||||
127
utils/fgpanel/FGCroppedTexture.cxx
Normal file
127
utils/fgpanel/FGCroppedTexture.cxx
Normal 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;
|
||||
}
|
||||
}
|
||||
69
utils/fgpanel/FGCroppedTexture.hxx
Normal file
69
utils/fgpanel/FGCroppedTexture.hxx
Normal 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
|
||||
41
utils/fgpanel/FGDummyTextureLoader.cxx
Normal file
41
utils/fgpanel/FGDummyTextureLoader.cxx
Normal 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;
|
||||
}
|
||||
29
utils/fgpanel/FGDummyTextureLoader.hxx
Normal file
29
utils/fgpanel/FGDummyTextureLoader.hxx
Normal 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);
|
||||
};
|
||||
186
utils/fgpanel/FGFontCache.cxx
Normal file
186
utils/fgpanel/FGFontCache.cxx
Normal 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 ();
|
||||
}
|
||||
77
utils/fgpanel/FGFontCache.hxx
Normal file
77
utils/fgpanel/FGFontCache.hxx
Normal 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;
|
||||
};
|
||||
156
utils/fgpanel/FGGLApplication.cxx
Normal file
156
utils/fgpanel/FGGLApplication.cxx
Normal 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
|
||||
}
|
||||
52
utils/fgpanel/FGGLApplication.hxx
Normal file
52
utils/fgpanel/FGGLApplication.hxx
Normal 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
|
||||
43
utils/fgpanel/FGGroupLayer.cxx
Normal file
43
utils/fgpanel/FGGroupLayer.cxx
Normal 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);
|
||||
}
|
||||
43
utils/fgpanel/FGGroupLayer.hxx
Normal file
43
utils/fgpanel/FGGroupLayer.hxx
Normal 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
|
||||
100
utils/fgpanel/FGInstrumentLayer.cxx
Normal file
100
utils/fgpanel/FGInstrumentLayer.cxx
Normal 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);
|
||||
}
|
||||
57
utils/fgpanel/FGInstrumentLayer.hxx
Normal file
57
utils/fgpanel/FGInstrumentLayer.hxx
Normal 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;
|
||||
};
|
||||
70
utils/fgpanel/FGLayeredInstrument.cxx
Normal file
70
utils/fgpanel/FGLayeredInstrument.cxx
Normal 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
Reference in New Issue
Block a user