Merge branch 'jd/atis' into next
This commit is contained in:
commit
7a14ec7e32
26 changed files with 1516 additions and 1458 deletions
|
@ -546,6 +546,7 @@ void FGAILocalTraffic::Update(double dt) {
|
||||||
// And to avoid compiler warnings...
|
// And to avoid compiler warnings...
|
||||||
case APPROACH: break;
|
case APPROACH: break;
|
||||||
case ATIS: break;
|
case ATIS: break;
|
||||||
|
case AWOS: break;
|
||||||
case ENROUTE: break;
|
case ENROUTE: break;
|
||||||
case DEPARTURE: break;
|
case DEPARTURE: break;
|
||||||
case INVALID: break;
|
case INVALID: break;
|
||||||
|
|
|
@ -79,10 +79,6 @@ void FGAIMgr::init() {
|
||||||
lon_node = fgGetNode("/position/longitude-deg", true);
|
lon_node = fgGetNode("/position/longitude-deg", true);
|
||||||
lat_node = fgGetNode("/position/latitude-deg", true);
|
lat_node = fgGetNode("/position/latitude-deg", true);
|
||||||
elev_node = fgGetNode("/position/altitude-ft", true);
|
elev_node = fgGetNode("/position/altitude-ft", true);
|
||||||
|
|
||||||
lon = lon_node->getDoubleValue();
|
|
||||||
lat = lat_node->getDoubleValue();
|
|
||||||
elev = elev_node->getDoubleValue();
|
|
||||||
|
|
||||||
// Load up models at the start to avoid pausing later
|
// Load up models at the start to avoid pausing later
|
||||||
// Hack alert - Hardwired paths!!
|
// Hack alert - Hardwired paths!!
|
||||||
|
@ -167,7 +163,7 @@ void FGAIMgr::update(double dt) {
|
||||||
|
|
||||||
//cout << activated.size() << '\n';
|
//cout << activated.size() << '\n';
|
||||||
|
|
||||||
SGGeod userPos = SGGeod::fromDegM(lon_node->getDoubleValue(), lat_node->getDoubleValue(), elev_node->getDoubleValue());
|
SGGeod userPos = SGGeod::fromDegM(lon_node->getDoubleValue(), lat_node->getDoubleValue(), elev_node->getDoubleValue());
|
||||||
|
|
||||||
// TODO - make these class variables!!
|
// TODO - make these class variables!!
|
||||||
static int i = 0;
|
static int i = 0;
|
||||||
|
@ -475,10 +471,9 @@ void FGAIMgr::SearchByPos(double range) {
|
||||||
//cout << "In SearchByPos(...)" << endl;
|
//cout << "In SearchByPos(...)" << endl;
|
||||||
|
|
||||||
// get bucket number for plane position
|
// get bucket number for plane position
|
||||||
lon = lon_node->getDoubleValue();
|
_userAircraftPos = SGGeod::fromDegFt(lon_node->getDoubleValue(),
|
||||||
lat = lat_node->getDoubleValue();
|
lat_node->getDoubleValue(), elev_node->getDoubleValue());
|
||||||
elev = elev_node->getDoubleValue() * SG_FEET_TO_METER;
|
SGBucket buck(_userAircraftPos);
|
||||||
SGBucket buck(lon, lat);
|
|
||||||
|
|
||||||
// get neigboring buckets
|
// get neigboring buckets
|
||||||
int bx = (int)( range*SG_NM_TO_METER / buck.get_width_m() / 2);
|
int bx = (int)( range*SG_NM_TO_METER / buck.get_width_m() / 2);
|
||||||
|
@ -492,7 +487,7 @@ void FGAIMgr::SearchByPos(double range) {
|
||||||
//cout << "i loop\n";
|
//cout << "i loop\n";
|
||||||
for ( int j=-by; j<=by; j++) {
|
for ( int j=-by; j<=by; j++) {
|
||||||
//cout << "j loop\n";
|
//cout << "j loop\n";
|
||||||
buck = sgBucketOffset(lon, lat, i, j);
|
buck = sgBucketOffset(_userAircraftPos.getLongitudeDeg(), _userAircraftPos.getLatitudeDeg(), i, j);
|
||||||
long int bucket = buck.gen_index();
|
long int bucket = buck.gen_index();
|
||||||
//cout << "bucket is " << bucket << endl;
|
//cout << "bucket is " << bucket << endl;
|
||||||
if(facilities.find(bucket) != facilities.end()) {
|
if(facilities.find(bucket) != facilities.end()) {
|
||||||
|
@ -530,14 +525,14 @@ void FGAIMgr::SearchByPos(double range) {
|
||||||
comm_list_type towered;
|
comm_list_type towered;
|
||||||
comm_list_iterator twd_itr;
|
comm_list_iterator twd_itr;
|
||||||
|
|
||||||
int num_twd = current_commlist->FindByPos(lon, lat, elev, range, &towered, TOWER);
|
int num_twd = current_commlist->FindByPos(_userAircraftPos, range, &towered, TOWER);
|
||||||
if (num_twd != 0) {
|
if (num_twd != 0) {
|
||||||
double closest = 1000000;
|
double closest = 1000000;
|
||||||
string s = "";
|
string s = "";
|
||||||
for(twd_itr = towered.begin(); twd_itr != towered.end(); twd_itr++) {
|
for(twd_itr = towered.begin(); twd_itr != towered.end(); twd_itr++) {
|
||||||
// Only activate the closest airport not already activated each time.
|
// Only activate the closest airport not already activated each time.
|
||||||
if(activated.find(twd_itr->ident) == activated.end()) {
|
if(activated.find(twd_itr->ident) == activated.end()) {
|
||||||
double sep = dclGetHorizontalSeparation(SGGeod::fromDegM(lon, lat, elev), fgGetAirportPos(twd_itr->ident));
|
double sep = dclGetHorizontalSeparation(_userAircraftPos, fgGetAirportPos(twd_itr->ident));
|
||||||
if(sep < closest) {
|
if(sep < closest) {
|
||||||
closest = sep;
|
closest = sep;
|
||||||
s = twd_itr->ident;
|
s = twd_itr->ident;
|
||||||
|
@ -601,7 +596,7 @@ string FGAIMgr::GenerateShortForm(const string& callsign, const string& plane_st
|
||||||
//cout << c << '\n';
|
//cout << c << '\n';
|
||||||
string tmp = "";
|
string tmp = "";
|
||||||
tmp += c;
|
tmp += c;
|
||||||
if(isalpha(c)) s += GetPhoneticIdent(c);
|
if(isalpha(c)) s += GetPhoneticLetter(c);
|
||||||
else s += ConvertNumToSpokenDigits(tmp);
|
else s += ConvertNumToSpokenDigits(tmp);
|
||||||
if(i > 1) s += '-';
|
if(i > 1) s += '-';
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,15 +81,12 @@ private:
|
||||||
typedef ai_callsigns_map_type::iterator ai_callsigns_map_iterator;
|
typedef ai_callsigns_map_type::iterator ai_callsigns_map_iterator;
|
||||||
ai_callsigns_map_type ai_callsigns_used;
|
ai_callsigns_map_type ai_callsigns_used;
|
||||||
|
|
||||||
// Position of the Users Aircraft
|
SGGeod _userAircraftPos;
|
||||||
double lon;
|
|
||||||
double lat;
|
|
||||||
double elev;
|
|
||||||
// Pointers to current users position
|
|
||||||
SGPropertyNode_ptr lon_node;
|
|
||||||
SGPropertyNode_ptr lat_node;
|
|
||||||
SGPropertyNode_ptr elev_node;
|
|
||||||
|
|
||||||
|
// Pointers to current users position
|
||||||
|
SGPropertyNode_ptr lon_node;
|
||||||
|
SGPropertyNode_ptr lat_node;
|
||||||
|
SGPropertyNode_ptr elev_node;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FGAIMgr();
|
FGAIMgr();
|
||||||
|
|
|
@ -105,7 +105,9 @@ void FGAIPlane::Update(double dt) {
|
||||||
if(1) {
|
if(1) {
|
||||||
// For now assume in range !!!
|
// For now assume in range !!!
|
||||||
// TODO - implement range checking
|
// TODO - implement range checking
|
||||||
Render(plane.callsign, false);
|
// TODO - at the moment the volume is always set off comm1
|
||||||
|
double volume = fgGetDouble("/instrumentation/comm[0]/volume");
|
||||||
|
Render(plane.callsign, volume, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Run the callback regardless of whether on same freq as user or not.
|
// Run the callback regardless of whether on same freq as user or not.
|
||||||
|
@ -165,7 +167,9 @@ void FGAIPlane::ConditionalTransmit(double timeout, int callback_code) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGAIPlane::ImmediateTransmit(int callback_code) {
|
void FGAIPlane::ImmediateTransmit(int callback_code) {
|
||||||
Render(plane.callsign, false);
|
// TODO - at the moment the volume is always set off comm1
|
||||||
|
double volume = fgGetDouble("/instrumentation/comm[0]/volume");
|
||||||
|
Render(plane.callsign, volume, false);
|
||||||
if(callback_code) {
|
if(callback_code) {
|
||||||
ProcessCallback(callback_code);
|
ProcessCallback(callback_code);
|
||||||
}
|
}
|
||||||
|
@ -179,26 +183,25 @@ void FGAIPlane::ProcessCallback(int code) {
|
||||||
// Outputs the transmission either on screen or as audio depending on user preference
|
// Outputs the transmission either on screen or as audio depending on user preference
|
||||||
// The refname is a string to identify this sample to the sound manager
|
// The refname is a string to identify this sample to the sound manager
|
||||||
// The repeating flag indicates whether the message should be repeated continuously or played once.
|
// The repeating flag indicates whether the message should be repeated continuously or played once.
|
||||||
void FGAIPlane::Render(const string& refname, bool repeating) {
|
void FGAIPlane::Render(const string& refname, const double volume, bool repeating) {
|
||||||
fgSetString("/sim/messages/ai-plane", pending_transmission.c_str());
|
fgSetString("/sim/messages/ai-plane", pending_transmission.c_str());
|
||||||
#ifdef ENABLE_AUDIO_SUPPORT
|
#ifdef ENABLE_AUDIO_SUPPORT
|
||||||
voice = (voiceOK && fgGetBool("/sim/sound/voice"));
|
voice = (voiceOK && fgGetBool("/sim/sound/voice"));
|
||||||
if(voice) {
|
if(voice) {
|
||||||
int len;
|
string buf = vPtr->WriteMessage((char*)pending_transmission.c_str(), voice);
|
||||||
unsigned char* buf = vPtr->WriteMessage((char*)pending_transmission.c_str(), len, voice);
|
if(voice) {
|
||||||
if(voice) {
|
SGSoundSample* simple =
|
||||||
SGSoundSample* simple = new SGSoundSample(buf, len, 8000);
|
new SGSoundSample((unsigned char*)buf.c_str(), buf.length(), 8000, AL_FORMAT_MONO8 );
|
||||||
// TODO - at the moment the volume is always set off comm1
|
// TODO - at the moment the volume can't be changed
|
||||||
// and can't be changed after the transmission has started.
|
// after the transmission has started.
|
||||||
simple->set_volume(5.0 * fgGetDouble("/instrumentation/comm[0]/volume"));
|
simple->set_volume(volume);
|
||||||
globals->get_soundmgr()->add(simple, refname);
|
globals->get_soundmgr()->add(simple, refname);
|
||||||
if(repeating) {
|
if(repeating) {
|
||||||
globals->get_soundmgr()->play_looped(refname);
|
globals->get_soundmgr()->play_looped(refname);
|
||||||
} else {
|
} else {
|
||||||
globals->get_soundmgr()->play_once(refname);
|
globals->get_soundmgr()->play_once(refname);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
delete[] buf;
|
}
|
||||||
}
|
}
|
||||||
#endif // ENABLE_AUDIO_SUPPORT
|
#endif // ENABLE_AUDIO_SUPPORT
|
||||||
if(!voice) {
|
if(!voice) {
|
||||||
|
|
|
@ -140,7 +140,7 @@ private:
|
||||||
// Outputs the transmission either on screen or as audio depending on user preference
|
// Outputs the transmission either on screen or as audio depending on user preference
|
||||||
// The refname is a string to identify this sample to the sound manager
|
// The refname is a string to identify this sample to the sound manager
|
||||||
// The repeating flag indicates whether the message should be repeated continuously or played once.
|
// The repeating flag indicates whether the message should be repeated continuously or played once.
|
||||||
void Render(const string& refname, bool repeating);
|
void Render(const string& refname, const double volume, bool repeating);
|
||||||
|
|
||||||
// Cease rendering a transmission.
|
// Cease rendering a transmission.
|
||||||
// Requires the sound manager refname if audio, else "".
|
// Requires the sound manager refname if audio, else "".
|
||||||
|
|
|
@ -22,43 +22,46 @@
|
||||||
# include <config.h>
|
# include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "ATC.hxx"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <simgear/sound/soundmgr_openal.hxx>
|
#include <simgear/sound/soundmgr_openal.hxx>
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
|
|
||||||
#include <Main/globals.hxx>
|
#include <Main/globals.hxx>
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
|
|
||||||
#include "ATC.hxx"
|
|
||||||
|
|
||||||
FGATC::FGATC() {
|
|
||||||
freqClear = true;
|
FGATC::FGATC() :
|
||||||
receiving = false;
|
_voiceOK(false),
|
||||||
respond = false;
|
freqClear(true),
|
||||||
runResponseCounter = false;
|
receiving(false),
|
||||||
_runReleaseCounter = false;
|
respond(false),
|
||||||
responseID = "";
|
responseID(""),
|
||||||
responseReqd = false;
|
runResponseCounter(false),
|
||||||
_type = INVALID;
|
_runReleaseCounter(false),
|
||||||
_display = false;
|
responseReqd(false),
|
||||||
_displaying = false;
|
_type(INVALID),
|
||||||
|
_display(false),
|
||||||
// Transmission timing stuff
|
// Transmission timing stuff
|
||||||
pending_transmission = "";
|
pending_transmission(""),
|
||||||
_timeout = 0;
|
_timeout(0),
|
||||||
_pending = false;
|
_pending(false),
|
||||||
_callback_code = 0;
|
_callback_code(0),
|
||||||
_transmit = false;
|
_transmit(false),
|
||||||
_transmitting = false;
|
_transmitting(false),
|
||||||
_counter = 0.0;
|
_counter(0.0),
|
||||||
_max_count = 5.0;
|
_max_count(5.0)
|
||||||
|
{
|
||||||
_voiceOK = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FGATC::~FGATC() {
|
FGATC::~FGATC() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derived classes wishing to use the response counter should call this from their own Update(...).
|
// Derived classes wishing to use the response counter should
|
||||||
|
// call this from their own Update(...).
|
||||||
void FGATC::Update(double dt) {
|
void FGATC::Update(double dt) {
|
||||||
if(runResponseCounter) {
|
if(runResponseCounter) {
|
||||||
//cout << responseCounter << '\t' << responseTime << '\n';
|
//cout << responseCounter << '\t' << responseTime << '\n';
|
||||||
|
@ -200,12 +203,9 @@ int FGATC::RemovePlane() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGATC::SetData(ATCData* d) {
|
void FGATC::SetData(ATCData* d) {
|
||||||
lon = d->lon;
|
_type = d->type;
|
||||||
lat = d->lat;
|
_geod = d->geod;
|
||||||
elev = d->elev;
|
_cart = d->cart;
|
||||||
x = d->x;
|
|
||||||
y = d->y;
|
|
||||||
z = d->z;
|
|
||||||
range = d->range;
|
range = d->range;
|
||||||
ident = d->ident;
|
ident = d->ident;
|
||||||
name = d->name;
|
name = d->name;
|
||||||
|
@ -216,7 +216,8 @@ void FGATC::SetData(ATCData* d) {
|
||||||
// Outputs the transmission either on screen or as audio depending on user preference
|
// Outputs the transmission either on screen or as audio depending on user preference
|
||||||
// The refname is a string to identify this sample to the sound manager
|
// The refname is a string to identify this sample to the sound manager
|
||||||
// The repeating flag indicates whether the message should be repeated continuously or played once.
|
// The repeating flag indicates whether the message should be repeated continuously or played once.
|
||||||
void FGATC::Render(string& msg, const string& refname, bool repeating) {
|
void FGATC::Render(string& msg, const double volume,
|
||||||
|
const string& refname, const bool repeating) {
|
||||||
if (repeating)
|
if (repeating)
|
||||||
fgSetString("/sim/messages/atis", msg.c_str());
|
fgSetString("/sim/messages/atis", msg.c_str());
|
||||||
else
|
else
|
||||||
|
@ -225,26 +226,29 @@ void FGATC::Render(string& msg, const string& refname, bool repeating) {
|
||||||
#ifdef ENABLE_AUDIO_SUPPORT
|
#ifdef ENABLE_AUDIO_SUPPORT
|
||||||
_voice = (_voiceOK && fgGetBool("/sim/sound/voice"));
|
_voice = (_voiceOK && fgGetBool("/sim/sound/voice"));
|
||||||
if(_voice) {
|
if(_voice) {
|
||||||
int len;
|
string buf = _vPtr->WriteMessage((char*)msg.c_str(), _voice);
|
||||||
unsigned char* buf = _vPtr->WriteMessage((char*)msg.c_str(), len, _voice);
|
if(_voice) {
|
||||||
if(_voice) {
|
NoRender(refname);
|
||||||
try {
|
try {
|
||||||
SGSoundSample *simple
|
// >>> Beware: must pass a (new) object to the (add) method,
|
||||||
= new SGSoundSample(buf, len, 8000);
|
// >>> because the (remove) method is going to do a (delete)
|
||||||
// TODO - at the moment the volume is always set off comm1
|
// >>> whether that's what you want or not.
|
||||||
// and can't be changed after the transmission has started.
|
SGSoundSample *simple =
|
||||||
simple->set_volume(5.0 * fgGetDouble("/instrumentation/comm[0]/volume"));
|
new SGSoundSample((unsigned char*) buf.c_str(),
|
||||||
globals->get_soundmgr()->add(simple, refname);
|
buf.length(), 8000, AL_FORMAT_MONO8);
|
||||||
if(repeating) {
|
// TODO - at the moment the volume can't be changed
|
||||||
globals->get_soundmgr()->play_looped(refname);
|
// after the transmission has started.
|
||||||
} else {
|
simple->set_volume(volume);
|
||||||
globals->get_soundmgr()->play_once(refname);
|
globals->get_soundmgr()->add(simple, refname);
|
||||||
}
|
if(repeating) {
|
||||||
} catch ( sg_io_exception &e ) {
|
globals->get_soundmgr()->play_looped(refname);
|
||||||
SG_LOG(SG_GENERAL, SG_ALERT, e.getFormattedMessage());
|
} else {
|
||||||
}
|
globals->get_soundmgr()->play_once(refname);
|
||||||
|
}
|
||||||
|
} catch ( sg_io_exception &e ) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_ALERT, e.getFormattedMessage());
|
||||||
}
|
}
|
||||||
delete[] buf;
|
}
|
||||||
}
|
}
|
||||||
#endif // ENABLE_AUDIO_SUPPORT
|
#endif // ENABLE_AUDIO_SUPPORT
|
||||||
if(!_voice) {
|
if(!_voice) {
|
||||||
|
@ -279,13 +283,73 @@ string FGATC::GenText(const string& m, int c) {
|
||||||
|
|
||||||
ostream& operator << (ostream& os, atc_type atc) {
|
ostream& operator << (ostream& os, atc_type atc) {
|
||||||
switch(atc) {
|
switch(atc) {
|
||||||
case(INVALID): return(os << "INVALID");
|
case(AWOS): return(os << "AWOS");
|
||||||
case(ATIS): return(os << "ATIS");
|
case(ATIS): return(os << "ATIS");
|
||||||
case(GROUND): return(os << "GROUND");
|
case(GROUND): return(os << "GROUND");
|
||||||
case(TOWER): return(os << "TOWER");
|
case(TOWER): return(os << "TOWER");
|
||||||
case(APPROACH): return(os << "APPROACH");
|
case(APPROACH): return(os << "APPROACH");
|
||||||
case(DEPARTURE): return(os << "DEPARTURE");
|
case(DEPARTURE): return(os << "DEPARTURE");
|
||||||
case(ENROUTE): return(os << "ENROUTE");
|
case(ENROUTE): return(os << "ENROUTE");
|
||||||
|
case(INVALID): return(os << "INVALID");
|
||||||
}
|
}
|
||||||
return(os << "ERROR - Unknown switch in atc_type operator << ");
|
return(os << "ERROR - Unknown switch in atc_type operator << ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::istream& operator >> ( std::istream& fin, ATCData& a )
|
||||||
|
{
|
||||||
|
double f;
|
||||||
|
char ch;
|
||||||
|
char tp;
|
||||||
|
|
||||||
|
fin >> tp;
|
||||||
|
|
||||||
|
switch(tp) {
|
||||||
|
case 'I':
|
||||||
|
a.type = ATIS;
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
a.type = TOWER;
|
||||||
|
break;
|
||||||
|
case 'G':
|
||||||
|
a.type = GROUND;
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
a.type = APPROACH;
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
a.type = INVALID;
|
||||||
|
return fin >> skipeol;
|
||||||
|
default:
|
||||||
|
SG_LOG(SG_GENERAL, SG_ALERT, "Warning - unknown type \'" << tp << "\' found whilst reading ATC frequency data!\n");
|
||||||
|
a.type = INVALID;
|
||||||
|
return fin >> skipeol;
|
||||||
|
}
|
||||||
|
|
||||||
|
double lat, lon, elev;
|
||||||
|
|
||||||
|
fin >> lat >> lon >> elev >> f >> a.range >> a.ident;
|
||||||
|
a.geod = SGGeod::fromDegM(lon, lat, elev);
|
||||||
|
a.name = "";
|
||||||
|
fin >> ch;
|
||||||
|
if(ch != '"') a.name += ch;
|
||||||
|
while(1) {
|
||||||
|
//in >> noskipws
|
||||||
|
fin.unsetf(std::ios::skipws);
|
||||||
|
fin >> ch;
|
||||||
|
if((ch == '"') || (ch == 0x0A)) {
|
||||||
|
break;
|
||||||
|
} // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the "
|
||||||
|
a.name += ch;
|
||||||
|
}
|
||||||
|
fin.setf(std::ios::skipws);
|
||||||
|
//cout << "Comm name = " << a.name << '\n';
|
||||||
|
|
||||||
|
a.freq = (int)(f*100.0 + 0.5);
|
||||||
|
|
||||||
|
// cout << a.ident << endl;
|
||||||
|
|
||||||
|
// generate cartesian coordinates
|
||||||
|
a.cart = SGVec3d::fromGeod(a.geod);
|
||||||
|
return fin >> skipeol;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,17 +28,25 @@
|
||||||
#include <simgear/math/sg_geodesy.hxx>
|
#include <simgear/math/sg_geodesy.hxx>
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
|
||||||
#include <istream>
|
#include <iosfwd>
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ATCVoice.hxx"
|
#include "ATCVoice.hxx"
|
||||||
|
|
||||||
using std::ostream;
|
// Convert a frequency in MHz to tens of kHz
|
||||||
using std::string;
|
// so we can use it e.g. as an index into commlist_freq
|
||||||
using std::ios;
|
//
|
||||||
|
// If freq > 1000 assume it's already in tens of KHz;
|
||||||
|
// otherwise assume MHz.
|
||||||
|
//
|
||||||
|
// Note: 122.375 must be rounded DOWN to 12237
|
||||||
|
// in order to be consistent with apt.dat et cetera.
|
||||||
|
inline int kHz10(double freq)
|
||||||
|
{
|
||||||
|
if (freq > 1000.) return int(freq);
|
||||||
|
return int(freq*100.0 + 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
enum plane_type {
|
enum plane_type {
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
GA_SINGLE,
|
GA_SINGLE,
|
||||||
|
@ -61,28 +69,24 @@ struct PlaneRec {
|
||||||
// Possible types of ATC type that the radios may be tuned to.
|
// Possible types of ATC type that the radios may be tuned to.
|
||||||
// INVALID implies not tuned in to anything.
|
// INVALID implies not tuned in to anything.
|
||||||
enum atc_type {
|
enum atc_type {
|
||||||
INVALID,
|
AWOS,
|
||||||
ATIS,
|
ATIS,
|
||||||
GROUND,
|
GROUND,
|
||||||
TOWER,
|
TOWER,
|
||||||
APPROACH,
|
APPROACH,
|
||||||
DEPARTURE,
|
DEPARTURE,
|
||||||
ENROUTE
|
ENROUTE,
|
||||||
|
INVALID /* must be last element; see ATC_NUM_TYPES */
|
||||||
};
|
};
|
||||||
|
|
||||||
const int ATC_NUM_TYPES = 7;
|
const int ATC_NUM_TYPES = 1 + INVALID;
|
||||||
|
|
||||||
// DCL - new experimental ATC data store
|
// DCL - new experimental ATC data store
|
||||||
struct ATCData {
|
struct ATCData {
|
||||||
atc_type type;
|
atc_type type;
|
||||||
// I've deliberately used float instead of double here to keep the size down - we'll be storing thousands of these in memory.
|
SGGeod geod;
|
||||||
// In fact, we could probably ditch x, y and z and generate on the fly as needed.
|
SGVec3d cart;
|
||||||
// On the other hand, we'll probably end up reading this data directly from the DAFIF eventually anyway!!
|
|
||||||
float lon, lat, elev;
|
|
||||||
float x, y, z;
|
|
||||||
//int freq;
|
|
||||||
unsigned short int freq;
|
unsigned short int freq;
|
||||||
//int range;
|
|
||||||
unsigned short int range;
|
unsigned short int range;
|
||||||
std::string ident;
|
std::string ident;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
@ -104,12 +108,14 @@ struct RunwayDetails {
|
||||||
std::ostream& operator << (std::ostream& os, atc_type atc);
|
std::ostream& operator << (std::ostream& os, atc_type atc);
|
||||||
|
|
||||||
class FGATC {
|
class FGATC {
|
||||||
|
friend class FGATCMgr;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FGATC();
|
FGATC();
|
||||||
virtual ~FGATC();
|
virtual ~FGATC();
|
||||||
|
|
||||||
|
virtual void Init()=0;
|
||||||
|
|
||||||
// Run the internal calculations
|
// Run the internal calculations
|
||||||
// Derived classes should call this method from their own Update methods if they
|
// Derived classes should call this method from their own Update methods if they
|
||||||
// wish to use the response timer functionality.
|
// wish to use the response timer functionality.
|
||||||
|
@ -154,19 +160,7 @@ public:
|
||||||
|
|
||||||
// Set the core ATC data
|
// Set the core ATC data
|
||||||
void SetData(ATCData* d);
|
void SetData(ATCData* d);
|
||||||
|
|
||||||
inline double get_lon() const { return lon; }
|
|
||||||
inline void set_lon(const double ln) {lon = ln;}
|
|
||||||
inline double get_lat() const { return lat; }
|
|
||||||
inline void set_lat(const double lt) {lat = lt;}
|
|
||||||
inline double get_elev() const { return elev; }
|
|
||||||
inline void set_elev(const double ev) {elev = ev;}
|
|
||||||
inline double get_x() const { return x; }
|
|
||||||
inline void set_x(const double ecs) {x = ecs;}
|
|
||||||
inline double get_y() const { return y; }
|
|
||||||
inline void set_y(const double why) {y = why;}
|
|
||||||
inline double get_z() const { return z; }
|
|
||||||
inline void set_z(const double zed) {z = zed;}
|
|
||||||
inline int get_freq() const { return freq; }
|
inline int get_freq() const { return freq; }
|
||||||
inline void set_freq(const int fq) {freq = fq;}
|
inline void set_freq(const int fq) {freq = fq;}
|
||||||
inline int get_range() const { return range; }
|
inline int get_range() const { return range; }
|
||||||
|
@ -182,7 +176,8 @@ protected:
|
||||||
// Outputs the transmission either on screen or as audio depending on user preference
|
// Outputs the transmission either on screen or as audio depending on user preference
|
||||||
// The refname is a string to identify this sample to the sound manager
|
// The refname is a string to identify this sample to the sound manager
|
||||||
// The repeating flag indicates whether the message should be repeated continuously or played once.
|
// The repeating flag indicates whether the message should be repeated continuously or played once.
|
||||||
void Render(std::string& msg, const std::string& refname = "", bool repeating = false);
|
void Render(std::string& msg, const double volume = 1.0,
|
||||||
|
const std::string& refname = "", bool repeating = false);
|
||||||
|
|
||||||
// Cease rendering all transmission from this station.
|
// Cease rendering all transmission from this station.
|
||||||
// Requires the sound manager refname if audio, else "".
|
// Requires the sound manager refname if audio, else "".
|
||||||
|
@ -199,13 +194,15 @@ protected:
|
||||||
|
|
||||||
virtual void ProcessCallback(int code);
|
virtual void ProcessCallback(int code);
|
||||||
|
|
||||||
double lon, lat, elev;
|
SGGeod _geod;
|
||||||
double x, y, z;
|
SGVec3d _cart;
|
||||||
int freq;
|
int freq;
|
||||||
|
std::map<std::string,int> active_on;
|
||||||
|
|
||||||
int range;
|
int range;
|
||||||
std::string ident; // Code of the airport its at.
|
std::string ident; // Code of the airport its at.
|
||||||
std::string name; // Name transmitted in the broadcast.
|
std::string name; // Name transmitted in the broadcast.
|
||||||
atc_type _type;
|
|
||||||
|
|
||||||
// Rendering related stuff
|
// Rendering related stuff
|
||||||
bool _voice; // Flag - true if we are using voice
|
bool _voice; // Flag - true if we are using voice
|
||||||
|
@ -213,28 +210,32 @@ protected:
|
||||||
bool _voiceOK; // Flag - true if at least one voice has loaded OK
|
bool _voiceOK; // Flag - true if at least one voice has loaded OK
|
||||||
FGATCVoice* _vPtr;
|
FGATCVoice* _vPtr;
|
||||||
|
|
||||||
std::string pending_transmission; // derived classes set this string before calling Transmit(...)
|
|
||||||
bool freqClear; // Flag to indicate if the frequency is clear of ongoing dialog
|
bool freqClear; // Flag to indicate if the frequency is clear of ongoing dialog
|
||||||
bool receiving; // Flag to indicate we are receiving a transmission
|
bool receiving; // Flag to indicate we are receiving a transmission
|
||||||
bool responseReqd; // Flag to indicate we should be responding to a request/report
|
|
||||||
bool runResponseCounter; // Flag to indicate the response counter should be run
|
|
||||||
double responseTime; // Time to take from end of request transmission to beginning of response
|
double responseTime; // Time to take from end of request transmission to beginning of response
|
||||||
// The idea is that this will be slightly random.
|
// The idea is that this will be slightly random.
|
||||||
double responseCounter; // counter to implement the above
|
|
||||||
|
bool respond; // Flag to indicate now is the time to respond - ie set following the count down of the response timer.
|
||||||
std::string responseID; // ID of the plane to respond to
|
std::string responseID; // ID of the plane to respond to
|
||||||
bool respond; // Flag to indicate now is the time to respond - ie set following the count down of the response timer.
|
bool runResponseCounter; // Flag to indicate the response counter should be run
|
||||||
|
double responseCounter; // counter to implement the above
|
||||||
// Derived classes only need monitor this flag, and use the response ID, as long as they call FGATC::Update(...)
|
// Derived classes only need monitor this flag, and use the response ID, as long as they call FGATC::Update(...)
|
||||||
bool _runReleaseCounter; // A timer for releasing the frequency after giving the message enough time to display
|
bool _runReleaseCounter; // A timer for releasing the frequency after giving the message enough time to display
|
||||||
|
bool responseReqd; // Flag to indicate we should be responding to a request/report
|
||||||
double _releaseTime;
|
double _releaseTime;
|
||||||
double _releaseCounter;
|
double _releaseCounter;
|
||||||
|
atc_type _type;
|
||||||
bool _display; // Flag to indicate whether we should be outputting to the ATC display.
|
bool _display; // Flag to indicate whether we should be outputting to the ATC display.
|
||||||
bool _displaying; // Flag to indicate whether we are outputting to the ATC display.
|
std::string pending_transmission; // derived classes set this string before calling Transmit(...)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Transmission timing stuff.
|
// Transmission timing stuff.
|
||||||
bool _pending;
|
|
||||||
double _timeout;
|
double _timeout;
|
||||||
|
bool _pending;
|
||||||
|
|
||||||
int _callback_code; // A callback code to be notified and processed by the derived classes
|
int _callback_code; // A callback code to be notified and processed by the derived classes
|
||||||
// A value of zero indicates no callback required
|
// A value of zero indicates no callback required
|
||||||
bool _transmit; // we are to transmit
|
bool _transmit; // we are to transmit
|
||||||
|
@ -243,67 +244,6 @@ private:
|
||||||
double _max_count;
|
double _max_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::istream&
|
std::istream& operator>> ( std::istream& fin, ATCData& a );
|
||||||
operator >> ( std::istream& fin, ATCData& a )
|
|
||||||
{
|
|
||||||
double f;
|
|
||||||
char ch;
|
|
||||||
char tp;
|
|
||||||
|
|
||||||
fin >> tp;
|
|
||||||
|
|
||||||
switch(tp) {
|
|
||||||
case 'I':
|
|
||||||
a.type = ATIS;
|
|
||||||
break;
|
|
||||||
case 'T':
|
|
||||||
a.type = TOWER;
|
|
||||||
break;
|
|
||||||
case 'G':
|
|
||||||
a.type = GROUND;
|
|
||||||
break;
|
|
||||||
case 'A':
|
|
||||||
a.type = APPROACH;
|
|
||||||
break;
|
|
||||||
case '[':
|
|
||||||
a.type = INVALID;
|
|
||||||
return fin >> skipeol;
|
|
||||||
default:
|
|
||||||
SG_LOG(SG_GENERAL, SG_ALERT, "Warning - unknown type \'" << tp << "\' found whilst reading ATC frequency data!\n");
|
|
||||||
a.type = INVALID;
|
|
||||||
return fin >> skipeol;
|
|
||||||
}
|
|
||||||
|
|
||||||
fin >> a.lat >> a.lon >> a.elev >> f >> a.range
|
|
||||||
>> a.ident;
|
|
||||||
|
|
||||||
a.name = "";
|
|
||||||
fin >> ch;
|
|
||||||
if(ch != '"') a.name += ch;
|
|
||||||
while(1) {
|
|
||||||
//in >> noskipws
|
|
||||||
fin.unsetf(std::ios::skipws);
|
|
||||||
fin >> ch;
|
|
||||||
if((ch == '"') || (ch == 0x0A)) {
|
|
||||||
break;
|
|
||||||
} // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the "
|
|
||||||
a.name += ch;
|
|
||||||
}
|
|
||||||
fin.setf(std::ios::skipws);
|
|
||||||
//cout << "Comm name = " << a.name << '\n';
|
|
||||||
|
|
||||||
a.freq = (int)(f*100.0 + 0.5);
|
|
||||||
|
|
||||||
// cout << a.ident << endl;
|
|
||||||
|
|
||||||
// generate cartesian coordinates
|
|
||||||
SGVec3d cart = SGVec3d::fromGeod(SGGeod::fromDegM(a.lon, a.lat, a.elev));
|
|
||||||
a.x = cart.x();
|
|
||||||
a.y = cart.y();
|
|
||||||
a.z = cart.z();
|
|
||||||
|
|
||||||
return fin >> skipeol;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif // _FG_ATC_HXX
|
#endif // _FG_ATC_HXX
|
||||||
|
|
|
@ -66,7 +66,7 @@ ATCMenuEntry::ATCMenuEntry() {
|
||||||
ATCMenuEntry::~ATCMenuEntry() {
|
ATCMenuEntry::~ATCMenuEntry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void atcUppercase(string &s) {
|
void atcUppercase(string &s) {
|
||||||
for(unsigned int i=0; i<s.size(); ++i) {
|
for(unsigned int i=0; i<s.size(); ++i) {
|
||||||
s[i] = toupper(s[i]);
|
s[i] = toupper(s[i]);
|
||||||
}
|
}
|
||||||
|
@ -318,21 +318,19 @@ void FGATCDialog::FreqDialog() {
|
||||||
// Find the ATC stations within a reasonable range
|
// Find the ATC stations within a reasonable range
|
||||||
comm_list_type atc_stations;
|
comm_list_type atc_stations;
|
||||||
comm_list_iterator atc_stat_itr;
|
comm_list_iterator atc_stat_itr;
|
||||||
|
|
||||||
double lon = fgGetDouble("/position/longitude-deg");
|
SGGeod geod(SGGeod::fromDegFt(fgGetDouble("/position/longitude-deg"),
|
||||||
double lat = fgGetDouble("/position/latitude-deg");
|
fgGetDouble("/position/latitude-deg"), fgGetDouble("/position/altitude-ft")));
|
||||||
double elev = fgGetDouble("/position/altitude-ft");
|
SGVec3d aircraft = SGVec3d::fromGeod(geod);
|
||||||
SGVec3d aircraft = SGVec3d::fromGeod(SGGeod::fromDegM(lon, lat, elev));
|
|
||||||
|
|
||||||
// search stations in range
|
// search stations in range
|
||||||
int num_stat = current_commlist->FindByPos(lon, lat, elev, 50.0, &atc_stations);
|
int num_stat = current_commlist->FindByPos(geod, 50.0, &atc_stations);
|
||||||
if (num_stat != 0) {
|
if (num_stat != 0) {
|
||||||
map<atcdata, bool> uniq;
|
map<atcdata, bool> uniq;
|
||||||
// fill map (sorts by distance and removes duplicates)
|
// fill map (sorts by distance and removes duplicates)
|
||||||
comm_list_iterator itr = atc_stations.begin();
|
comm_list_iterator itr = atc_stations.begin();
|
||||||
for (; itr != atc_stations.end(); ++itr) {
|
for (; itr != atc_stations.end(); ++itr) {
|
||||||
SGVec3d station(itr->x, itr->y, itr->z);
|
double distance = distSqr(aircraft, itr->cart);
|
||||||
double distance = distSqr(aircraft, station);
|
|
||||||
uniq[atcdata(itr->ident, itr->name, distance)] = true;
|
uniq[atcdata(itr->ident, itr->name, distance)] = true;
|
||||||
}
|
}
|
||||||
// create button per map entry (modified copy of <button-template>)
|
// create button per map entry (modified copy of <button-template>)
|
||||||
|
@ -382,7 +380,7 @@ void FGATCDialog::FreqDisplay(string& ident) {
|
||||||
int n = 0; // Number of ATC frequencies at this airport
|
int n = 0; // Number of ATC frequencies at this airport
|
||||||
|
|
||||||
comm_list_type stations;
|
comm_list_type stations;
|
||||||
int found = current_commlist->FindByPos(a->getLongitude(), a->getLatitude(), a->getElevation(), 20.0, &stations);
|
int found = current_commlist->FindByPos(a->geod(), 20.0, &stations);
|
||||||
if(found) {
|
if(found) {
|
||||||
ostringstream ostr;
|
ostringstream ostr;
|
||||||
comm_list_iterator itr = stations.begin();
|
comm_list_iterator itr = stations.begin();
|
||||||
|
|
|
@ -55,8 +55,9 @@ typedef atcmentry_vec_type::iterator atcmentry_vec_iterator;
|
||||||
typedef map < string, atcmentry_vec_type > atcmentry_map_type;
|
typedef map < string, atcmentry_vec_type > atcmentry_map_type;
|
||||||
typedef atcmentry_map_type::iterator atcmentry_map_iterator;
|
typedef atcmentry_map_type::iterator atcmentry_map_iterator;
|
||||||
|
|
||||||
//void ATCDialogInit();
|
void atcUppercase(string &s);
|
||||||
|
|
||||||
|
//void ATCDialogInit();
|
||||||
//void ATCDoDialog(atc_type type);
|
//void ATCDoDialog(atc_type type);
|
||||||
|
|
||||||
class FGATCDialog {
|
class FGATCDialog {
|
||||||
|
|
|
@ -23,16 +23,21 @@
|
||||||
# include <config.h>
|
# include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "ATCVoice.hxx"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
#include <simgear/misc/sgstream.hxx>
|
#include <simgear/misc/sgstream.hxx>
|
||||||
#include <simgear/math/sg_random.h>
|
#include <simgear/math/sg_random.h>
|
||||||
|
#include <simgear/sound/sample_openal.hxx>
|
||||||
|
|
||||||
#include <Main/globals.hxx>
|
#include <Main/globals.hxx>
|
||||||
|
|
||||||
#include "ATCVoice.hxx"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
FGATCVoice::FGATCVoice() {
|
FGATCVoice::FGATCVoice() {
|
||||||
SoundData = 0;
|
SoundData = 0;
|
||||||
rawSoundData = 0;
|
rawSoundData = 0;
|
||||||
|
@ -47,20 +52,24 @@ FGATCVoice::~FGATCVoice() {
|
||||||
// Load the two voice files - one containing the raw sound data (.wav) and one containing the word positions (.vce).
|
// Load the two voice files - one containing the raw sound data (.wav) and one containing the word positions (.vce).
|
||||||
// Return true if successful.
|
// Return true if successful.
|
||||||
bool FGATCVoice::LoadVoice(const string& voice) {
|
bool FGATCVoice::LoadVoice(const string& voice) {
|
||||||
// FIXME CLO: disabled to try to see if this is causign problemcs
|
// FIXME CLO: disabled to try to see if this is causing problemcs
|
||||||
// return false;
|
// return false;
|
||||||
|
|
||||||
ifstream fin;
|
std::ifstream fin;
|
||||||
|
|
||||||
SGPath path = globals->get_fg_root();
|
SGPath path = globals->get_fg_root();
|
||||||
path.append( "ATC" );
|
path.append( "ATC" );
|
||||||
|
|
||||||
string file = voice + ".wav";
|
string file = voice + ".wav";
|
||||||
|
|
||||||
SoundData = new SGSoundSample();
|
SGSoundSample SoundData;
|
||||||
rawSoundData = (char *)SoundData->load_file(path.c_str(), file.c_str());
|
rawSoundData = (char *)SoundData.load_file(path.c_str(), file.c_str());
|
||||||
rawDataSize = SoundData->get_size();
|
rawDataSize = SoundData.get_size();
|
||||||
|
#ifdef VOICE_TEST
|
||||||
|
ALenum fmt = SoundData.get_format();
|
||||||
|
cout << "ATCVoice: format: " << fmt
|
||||||
|
<< " size: " << rawDataSize << endl;
|
||||||
|
#endif
|
||||||
path = globals->get_fg_root();
|
path = globals->get_fg_root();
|
||||||
string wordPath = "ATC/" + voice + ".vce";
|
string wordPath = "ATC/" + voice + ".vce";
|
||||||
path.append(wordPath);
|
path.append(wordPath);
|
||||||
|
@ -93,6 +102,13 @@ bool FGATCVoice::LoadVoice(const string& voice) {
|
||||||
wd.offset = wrdOffset;
|
wd.offset = wrdOffset;
|
||||||
wd.length = wrdLength;
|
wd.length = wrdLength;
|
||||||
wordMap[wrdstr] = wd;
|
wordMap[wrdstr] = wd;
|
||||||
|
string ws2 = wrdstr;
|
||||||
|
for(string::iterator p = ws2.begin(); p != ws2.end(); p++){
|
||||||
|
*p = tolower(*p);
|
||||||
|
if (*p == '-') *p = '_';
|
||||||
|
}
|
||||||
|
if (wrdstr != ws2) wordMap[ws2] = wd;
|
||||||
|
|
||||||
//cout << wrd << "\t\t" << wrdOffset << "\t\t" << wrdLength << '\n';
|
//cout << wrd << "\t\t" << wrdOffset << "\t\t" << wrdLength << '\n';
|
||||||
//cout << i << '\n';
|
//cout << i << '\n';
|
||||||
}
|
}
|
||||||
|
@ -105,8 +121,9 @@ bool FGATCVoice::LoadVoice(const string& voice) {
|
||||||
typedef list < string > tokenList_type;
|
typedef list < string > tokenList_type;
|
||||||
typedef tokenList_type::iterator tokenList_iterator;
|
typedef tokenList_type::iterator tokenList_iterator;
|
||||||
|
|
||||||
// Given a desired message, return a pointer to the data buffer and write the buffer length into len.
|
// Given a desired message, return a string containing the
|
||||||
unsigned char* FGATCVoice::WriteMessage(char* message, int& len, bool& dataOK) {
|
// sound-sample data
|
||||||
|
string FGATCVoice::WriteMessage(const char* message, bool& dataOK) {
|
||||||
|
|
||||||
// What should we do here?
|
// What should we do here?
|
||||||
// First - parse the message into a list of tokens.
|
// First - parse the message into a list of tokens.
|
||||||
|
@ -117,28 +134,36 @@ unsigned char* FGATCVoice::WriteMessage(char* message, int& len, bool& dataOK) {
|
||||||
|
|
||||||
// TODO - at the moment we're effectively taking 3 passes through the data.
|
// TODO - at the moment we're effectively taking 3 passes through the data.
|
||||||
// There is no need for this - 2 should be sufficient - we can probably ditch the tokenList.
|
// There is no need for this - 2 should be sufficient - we can probably ditch the tokenList.
|
||||||
|
size_t n1 = 1+strlen(message);
|
||||||
|
char msg[n1];
|
||||||
|
strncpy(msg, message, n1); // strtok requires a non-const char*
|
||||||
char* token;
|
char* token;
|
||||||
char mes[1000];
|
|
||||||
int numWords = 0;
|
int numWords = 0;
|
||||||
strcpy(mes, message);
|
const char delimiters[] = " \t.,;:\"\n";
|
||||||
const char delimiters[] = " \t.,;:\"";
|
char* context;
|
||||||
token = strtok(mes, delimiters);
|
token = strtok_r(msg, delimiters, &context);
|
||||||
while(token != NULL) {
|
while(token != NULL) {
|
||||||
|
for (char *t = token; *t; t++) {
|
||||||
|
*t = tolower(*t); // canonicalize the case, to
|
||||||
|
if (*t == '-') *t = '_'; // match what's in the index
|
||||||
|
}
|
||||||
tokenList.push_back(token);
|
tokenList.push_back(token);
|
||||||
++numWords;
|
++numWords;
|
||||||
//cout << "token = " << token << '\n';
|
SG_LOG(SG_ATC, SG_DEBUG, "voice synth: token: '"
|
||||||
token = strtok(NULL, delimiters);
|
<< token << "'");
|
||||||
|
token = strtok_r(NULL, delimiters, &context);
|
||||||
}
|
}
|
||||||
|
|
||||||
WordData* wdptr = new WordData[numWords];
|
WordData wdptr[numWords];
|
||||||
int word = 0;
|
int word = 0;
|
||||||
unsigned int cumLength = 0;
|
unsigned int cumLength = 0;
|
||||||
|
|
||||||
tokenListItr = tokenList.begin();
|
tokenListItr = tokenList.begin();
|
||||||
while(tokenListItr != tokenList.end()) {
|
while(tokenListItr != tokenList.end()) {
|
||||||
if(wordMap.find(*tokenListItr) == wordMap.end()) {
|
if(wordMap.find(*tokenListItr) == wordMap.end()) {
|
||||||
// Oh dear - the token isn't in the sound file
|
// Oh dear - the token isn't in the sound file
|
||||||
//cout << "word " << *tokenListItr << " not found :-(\n";
|
SG_LOG(SG_ATC, SG_ALERT, "voice synth: word '"
|
||||||
|
<< *tokenListItr << "' not found");
|
||||||
} else {
|
} else {
|
||||||
wdptr[word] = wordMap[*tokenListItr];
|
wdptr[word] = wordMap[*tokenListItr];
|
||||||
cumLength += wdptr[word].length;
|
cumLength += wdptr[word].length;
|
||||||
|
@ -151,13 +176,10 @@ unsigned char* FGATCVoice::WriteMessage(char* message, int& len, bool& dataOK) {
|
||||||
// Check for no tokens found else slScheduler can be crashed
|
// Check for no tokens found else slScheduler can be crashed
|
||||||
if(!word) {
|
if(!word) {
|
||||||
dataOK = false;
|
dataOK = false;
|
||||||
delete[] wdptr;
|
return "";
|
||||||
return(NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char* tmpbuf = new unsigned char[cumLength];
|
char tmpbuf[cumLength];
|
||||||
unsigned char* outbuf = new unsigned char[cumLength];
|
|
||||||
len = cumLength;
|
|
||||||
unsigned int bufpos = 0;
|
unsigned int bufpos = 0;
|
||||||
for(int i=0; i<word; ++i) {
|
for(int i=0; i<word; ++i) {
|
||||||
/*
|
/*
|
||||||
|
@ -170,11 +192,9 @@ unsigned char* FGATCVoice::WriteMessage(char* message, int& len, bool& dataOK) {
|
||||||
SG_LOG(SG_ATC, SG_ALERT, "ERROR - mismatch between ATC .wav and .vce file in ATCVoice.cxx\n");
|
SG_LOG(SG_ATC, SG_ALERT, "ERROR - mismatch between ATC .wav and .vce file in ATCVoice.cxx\n");
|
||||||
SG_LOG(SG_ATC, SG_ALERT, "Offset + length: " << wdptr[i].offset + wdptr[i].length
|
SG_LOG(SG_ATC, SG_ALERT, "Offset + length: " << wdptr[i].offset + wdptr[i].length
|
||||||
<< " exceeds rawdata size: " << rawDataSize << endl);
|
<< " exceeds rawdata size: " << rawDataSize << endl);
|
||||||
delete[] wdptr;
|
|
||||||
delete[] tmpbuf;
|
|
||||||
delete[] outbuf;
|
|
||||||
dataOK = false;
|
dataOK = false;
|
||||||
return(NULL);
|
return "";
|
||||||
}
|
}
|
||||||
memcpy(tmpbuf + bufpos, rawSoundData + wdptr[i].offset, wdptr[i].length);
|
memcpy(tmpbuf + bufpos, rawSoundData + wdptr[i].offset, wdptr[i].length);
|
||||||
bufpos += wdptr[i].length;
|
bufpos += wdptr[i].length;
|
||||||
|
@ -183,12 +203,10 @@ unsigned char* FGATCVoice::WriteMessage(char* message, int& len, bool& dataOK) {
|
||||||
// tmpbuf now contains the message starting at the beginning - but we want it to start at a random position.
|
// tmpbuf now contains the message starting at the beginning - but we want it to start at a random position.
|
||||||
unsigned int offsetIn = (int)(cumLength * sg_random());
|
unsigned int offsetIn = (int)(cumLength * sg_random());
|
||||||
if(offsetIn > cumLength) offsetIn = cumLength;
|
if(offsetIn > cumLength) offsetIn = cumLength;
|
||||||
memcpy(outbuf, tmpbuf + offsetIn, (cumLength - offsetIn));
|
|
||||||
memcpy(outbuf + (cumLength - offsetIn), tmpbuf, offsetIn);
|
string front(tmpbuf, offsetIn);
|
||||||
|
string back(tmpbuf+offsetIn, cumLength - offsetIn);
|
||||||
delete[] tmpbuf;
|
|
||||||
delete[] wdptr;
|
|
||||||
|
|
||||||
dataOK = true;
|
dataOK = true;
|
||||||
return(outbuf);
|
return back + front;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,17 +23,10 @@
|
||||||
|
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
|
|
||||||
# include <fstream>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <list>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <simgear/sound/sample_openal.hxx>
|
class SGSoundSample;
|
||||||
|
|
||||||
using std::map;
|
|
||||||
using std::list;
|
|
||||||
using std::string;
|
|
||||||
|
|
||||||
|
|
||||||
struct WordData {
|
struct WordData {
|
||||||
unsigned int offset; // Offset of beginning of word sample into raw sound sample
|
unsigned int offset; // Offset of beginning of word sample into raw sound sample
|
||||||
|
@ -57,8 +50,7 @@ public:
|
||||||
|
|
||||||
// Given a desired message, return a pointer to the data buffer and write the buffer length into len.
|
// Given a desired message, return a pointer to the data buffer and write the buffer length into len.
|
||||||
// Sets dataOK = true if the returned buffer is valid.
|
// Sets dataOK = true if the returned buffer is valid.
|
||||||
unsigned char* WriteMessage(char* message, int& len, bool& dataOK);
|
std::string WriteMessage(const char* message, bool& dataOK);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -45,9 +45,7 @@ using std::map;
|
||||||
struct AirportATC {
|
struct AirportATC {
|
||||||
AirportATC();
|
AirportATC();
|
||||||
|
|
||||||
float lon;
|
SGGeod geod;
|
||||||
float lat;
|
|
||||||
float elev;
|
|
||||||
float atis_freq;
|
float atis_freq;
|
||||||
bool atis_active;
|
bool atis_active;
|
||||||
float tower_freq;
|
float tower_freq;
|
||||||
|
@ -65,7 +63,7 @@ struct AirportATC {
|
||||||
// Flags to ensure the stations don't get wrongly deactivated
|
// Flags to ensure the stations don't get wrongly deactivated
|
||||||
bool set_by_AI; // true when the AI manager has activated this station
|
bool set_by_AI; // true when the AI manager has activated this station
|
||||||
unsigned int numAI; // Ref count of the number of AI planes registered
|
unsigned int numAI; // Ref count of the number of AI planes registered
|
||||||
bool set_by_comm[2][ATC_NUM_TYPES]; // true when the relevant comm_freq has activated this station and type
|
//xx bool set_by_comm[2][ATC_NUM_TYPES]; // true when the relevant comm_freq has activated this station and type
|
||||||
};
|
};
|
||||||
|
|
||||||
class FGATCMgr : public SGSubsystem
|
class FGATCMgr : public SGSubsystem
|
||||||
|
@ -84,47 +82,26 @@ private:
|
||||||
airport_atc_map_iterator airport_atc_map_itr;
|
airport_atc_map_iterator airport_atc_map_itr;
|
||||||
|
|
||||||
// A list of pointers to all currently active ATC classes
|
// A list of pointers to all currently active ATC classes
|
||||||
typedef list <FGATC*> atc_list_type;
|
typedef map<string,FGATC*> atc_list_type;
|
||||||
typedef atc_list_type::iterator atc_list_iterator;
|
typedef atc_list_type::iterator atc_list_iterator;
|
||||||
typedef atc_list_type::const_iterator atc_list_const_iterator;
|
typedef atc_list_type::const_iterator atc_list_const_iterator;
|
||||||
|
|
||||||
// Everything put in this list should be created dynamically
|
// Everything put in this list should be created dynamically
|
||||||
// on the heap and ***DELETED WHEN REMOVED!!!!!***
|
// on the heap and ***DELETED WHEN REMOVED!!!!!***
|
||||||
atc_list_type atc_list;
|
atc_list_type* atc_list;
|
||||||
atc_list_iterator atc_list_itr;
|
atc_list_iterator atc_list_itr;
|
||||||
// Any member function of FGATCMgr is permitted to leave this iterator pointing
|
// Any member function of FGATCMgr is permitted to leave this iterator pointing
|
||||||
// at any point in or at the end of the list.
|
// at any point in or at the end of the list.
|
||||||
// Hence any new access must explicitly first check for atc_list.end() before dereferencing.
|
// Hence any new access must explicitly first check for atc_list.end() before dereferencing.
|
||||||
|
|
||||||
// Position of the Users Aircraft
|
// Position of the Users Aircraft
|
||||||
double lon;
|
SGGeod _aircraftPos;
|
||||||
double lat;
|
|
||||||
double elev;
|
|
||||||
|
|
||||||
// Type of ATC control that the user's radios are tuned to.
|
|
||||||
atc_type comm_type[2];
|
|
||||||
|
|
||||||
// Pointer to the ATC station that the user is currently tuned into.
|
|
||||||
FGATC* comm_atc_ptr[2];
|
|
||||||
|
|
||||||
double comm_freq[2];
|
|
||||||
|
|
||||||
// Pointers to users current communication frequencies.
|
|
||||||
SGPropertyNode_ptr comm_node[2];
|
|
||||||
|
|
||||||
// Pointers to current users position
|
// Pointers to current users position
|
||||||
SGPropertyNode_ptr lon_node;
|
SGPropertyNode_ptr lon_node;
|
||||||
SGPropertyNode_ptr lat_node;
|
SGPropertyNode_ptr lat_node;
|
||||||
SGPropertyNode_ptr elev_node;
|
SGPropertyNode_ptr elev_node;
|
||||||
|
|
||||||
// Position of the ATC that the comm radios are tuned to in order to decide
|
|
||||||
// whether transmission will be received.
|
|
||||||
double comm_x[2], comm_y[2], comm_z[2], comm_lon[2], comm_lat[2], comm_elev[2];
|
|
||||||
|
|
||||||
double comm_range[2], comm_effective_range[2];
|
|
||||||
bool comm_valid[2];
|
|
||||||
string comm_ident[2];
|
|
||||||
//string last_comm_ident[2];
|
|
||||||
//string approach_ident;
|
//string approach_ident;
|
||||||
bool last_in_range;
|
bool last_in_range;
|
||||||
|
|
||||||
|
@ -170,10 +147,10 @@ public:
|
||||||
// at different airports in quick succession if a large enough selection are available.
|
// at different airports in quick succession if a large enough selection are available.
|
||||||
FGATCVoice* GetVoicePointer(const atc_type& type);
|
FGATCVoice* GetVoicePointer(const atc_type& type);
|
||||||
|
|
||||||
atc_type GetComm1ATCType() { return(comm_type[0]); }
|
atc_type GetComm1ATCType() { return(INVALID/* kludge */); }
|
||||||
FGATC* GetComm1ATCPointer() { return(comm_atc_ptr[0]); }
|
FGATC* GetComm1ATCPointer() { return(0/* kludge */); }
|
||||||
atc_type GetComm2ATCType() { return(comm_type[1]); }
|
atc_type GetComm2ATCType() { return(INVALID); }
|
||||||
FGATC* GetComm2ATCPointer() { return(comm_atc_ptr[1]); }
|
FGATC* GetComm2ATCPointer() { return(0/* kludge */); }
|
||||||
|
|
||||||
// Get the frequency of a given service at a given airport
|
// Get the frequency of a given service at a given airport
|
||||||
// Returns zero if not found
|
// Returns zero if not found
|
||||||
|
@ -189,11 +166,7 @@ private:
|
||||||
|
|
||||||
// Remove a class from the atc_list and delete it from memory
|
// Remove a class from the atc_list and delete it from memory
|
||||||
// *if* no other comm channel or AI plane is using it.
|
// *if* no other comm channel or AI plane is using it.
|
||||||
void CommRemoveFromList(const string& id, const atc_type& tp, int chan);
|
void ZapOtherService(const string ncunit, const string svc_name);
|
||||||
|
|
||||||
// Remove a class from the atc_list and delete it from memory
|
|
||||||
// Should be called from the above - not directly!!
|
|
||||||
void RemoveFromList(const string& id, const atc_type& tp);
|
|
||||||
|
|
||||||
// Return a pointer to a class in the list given ICAO code and type
|
// Return a pointer to a class in the list given ICAO code and type
|
||||||
// (external interface to this is through GetATCPointer)
|
// (external interface to this is through GetATCPointer)
|
||||||
|
@ -201,11 +174,13 @@ private:
|
||||||
// - *** THE CALLING FUNCTION MUST CHECK FOR THIS ***
|
// - *** THE CALLING FUNCTION MUST CHECK FOR THIS ***
|
||||||
FGATC* FindInList(const string& id, const atc_type& tp);
|
FGATC* FindInList(const string& id, const atc_type& tp);
|
||||||
|
|
||||||
// Search the specified channel for stations on the same frequency and in range.
|
// Search the specified radio for stations on the same frequency and in range.
|
||||||
void FreqSearch(int channel);
|
void FreqSearch(const string navcomm, const int unit);
|
||||||
|
|
||||||
// Search ATC stations by area in order that we appear 'on the radar'
|
#ifdef AREA_SEARCH
|
||||||
void AreaSearch();
|
// Search ATC stations by area in order that we appear 'on the radar'
|
||||||
|
void AreaSearch();
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,16 @@
|
||||||
#include "ATCutils.hxx"
|
#include "ATCutils.hxx"
|
||||||
#include "ATCProjection.hxx"
|
#include "ATCProjection.hxx"
|
||||||
|
|
||||||
static const string nums[10] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "niner"};
|
static const string nums[10] = {"zero", "one", "two", "three", "four",
|
||||||
|
"five", "six", "seven", "eight", "niner"};
|
||||||
|
|
||||||
|
static const string letters[LTRS] = {
|
||||||
|
"alpha", "bravo", "charlie", "delta", "echo",
|
||||||
|
"foxtrot", "golf", "hotel", "india", "juliet",
|
||||||
|
"kilo", "lima", "mike", "november", "oscar",
|
||||||
|
"papa", "quebec", "romeo", "sierra", "tango",
|
||||||
|
"uniform", "victor", "whiskey", "xray", "yankee", "zulu"
|
||||||
|
};
|
||||||
|
|
||||||
// Convert any number to spoken digits
|
// Convert any number to spoken digits
|
||||||
string ConvertNumToSpokenDigits(const string &n) {
|
string ConvertNumToSpokenDigits(const string &n) {
|
||||||
|
@ -59,101 +68,50 @@ string ConvertNumToSpokenDigits(const string &n) {
|
||||||
return(str);
|
return(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert an integer to a decimal numeral string
|
||||||
// Convert an integer to spoken digits
|
string decimalNumeral(const int& n) {
|
||||||
string ConvertNumToSpokenDigits(int n) {
|
|
||||||
std::ostringstream buf;
|
std::ostringstream buf;
|
||||||
buf << n;
|
buf << n;
|
||||||
return(ConvertNumToSpokenDigits(buf.str()));
|
return buf.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert an integer to spoken digits
|
||||||
|
string ConvertNumToSpokenDigits(const int& n) {
|
||||||
|
return ConvertNumToSpokenDigits(decimalNumeral(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Convert a 2 digit rwy number to a spoken-style string
|
// Assumes we get a string of digits optionally appended with L, R or C
|
||||||
string ConvertRwyNumToSpokenString(int n) {
|
// eg 1 7L 29R 36
|
||||||
// Basic error/sanity checking
|
|
||||||
while(n < 0) {
|
|
||||||
n += 36;
|
|
||||||
}
|
|
||||||
while(n > 36) {
|
|
||||||
n -= 36;
|
|
||||||
}
|
|
||||||
if(n == 0) {
|
|
||||||
n = 36; // Is this right?
|
|
||||||
}
|
|
||||||
|
|
||||||
string str = "";
|
|
||||||
int index = n/10;
|
|
||||||
str += nums[index];
|
|
||||||
n -= (index * 10);
|
|
||||||
//str += "-";
|
|
||||||
str += " "; //Changed this for the benefit of the voice token parser - prefer the "-" in the visual output though.
|
|
||||||
str += nums[n];
|
|
||||||
return(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assumes we get a two-digit string optionally appended with L, R or C
|
|
||||||
// eg 01 07L 29R 36
|
|
||||||
// Anything else is not guaranteed to be handled correctly!
|
// Anything else is not guaranteed to be handled correctly!
|
||||||
string ConvertRwyNumToSpokenString(const string &s) {
|
string ConvertRwyNumToSpokenString(const string &rwy) {
|
||||||
if(s.size() < 3) {
|
string rslt;
|
||||||
return(ConvertRwyNumToSpokenString(atoi(s.c_str())));
|
for (int ii = 0; ii < rwy.length(); ii++){
|
||||||
} else {
|
if (rslt.length()) rslt += " ";
|
||||||
string r = ConvertRwyNumToSpokenString(atoi(s.substr(0,2).c_str()));
|
string ch = rwy.substr(ii,1);
|
||||||
if(s.substr(2,1) == "L") {
|
if (isdigit(ch[0])) rslt += ConvertNumToSpokenDigits(atoi(ch.c_str()));
|
||||||
r += " left";
|
else if (ch == "R") rslt += "right";
|
||||||
} else if(s.substr(2,1) == "R") {
|
else if (ch == "C") rslt += "center";
|
||||||
r += " right";
|
else if (ch == "L") rslt += "left";
|
||||||
} else if(s.substr(2,1) == "C") {
|
else {
|
||||||
r += " center";
|
rslt += GetPhoneticLetter(ch[0]);
|
||||||
} else {
|
SG_LOG(SG_ATC, SG_WARN, "WARNING: Unknown suffix '" << ch
|
||||||
SG_LOG(SG_ATC, SG_WARN, "WARNING: Unknown suffix " << s.substr(2,1) << " from runway ID " << s << " in ConvertRwyNumToSpokenString(...)");
|
<< "' in runway " << rwy << " in ConvertRwyNumToSpokenString(...)");
|
||||||
}
|
}
|
||||||
return(r);
|
}
|
||||||
}
|
return rslt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Return the phonetic letter of a letter represented as an integer 1->26
|
// Return the phonetic letter of a letter represented as an integer 1->26
|
||||||
string GetPhoneticIdent(int i) {
|
string GetPhoneticLetter(const int i) {
|
||||||
// TODO - Check i is between 1 and 26 and wrap if necessary
|
return(letters[i % LTRS]);
|
||||||
return(GetPhoneticIdent(char('a' + (i-1))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the phonetic letter of a character in the range a-z or A-Z.
|
// Return the phonetic letter of a character in the range a-z or A-Z.
|
||||||
// Currently always returns prefixed by lowercase.
|
// Currently always returns prefixed by lowercase.
|
||||||
string GetPhoneticIdent(char c) {
|
string GetPhoneticLetter(const char c) {
|
||||||
c = tolower(c);
|
return GetPhoneticLetter(int(tolower(c) - 'a'));
|
||||||
// TODO - Check c is between a and z and wrap if necessary
|
|
||||||
switch(c) {
|
|
||||||
case 'a' : return("alpha");
|
|
||||||
case 'b' : return("bravo");
|
|
||||||
case 'c' : return("charlie");
|
|
||||||
case 'd' : return("delta");
|
|
||||||
case 'e' : return("echo");
|
|
||||||
case 'f' : return("foxtrot");
|
|
||||||
case 'g' : return("golf");
|
|
||||||
case 'h' : return("hotel");
|
|
||||||
case 'i' : return("india");
|
|
||||||
case 'j' : return("juliet");
|
|
||||||
case 'k' : return("kilo");
|
|
||||||
case 'l' : return("lima");
|
|
||||||
case 'm' : return("mike");
|
|
||||||
case 'n' : return("november");
|
|
||||||
case 'o' : return("oscar");
|
|
||||||
case 'p' : return("papa");
|
|
||||||
case 'q' : return("quebec");
|
|
||||||
case 'r' : return("romeo");
|
|
||||||
case 's' : return("sierra");
|
|
||||||
case 't' : return("tango");
|
|
||||||
case 'u' : return("uniform");
|
|
||||||
case 'v' : return("victor");
|
|
||||||
case 'w' : return("whiskey");
|
|
||||||
case 'x' : return("x-ray");
|
|
||||||
case 'y' : return("yankee");
|
|
||||||
case 'z' : return("zulu");
|
|
||||||
}
|
|
||||||
// We shouldn't get here
|
|
||||||
return("Error");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the compass direction associated with a heading in degrees
|
// Get the compass direction associated with a heading in degrees
|
||||||
|
|
|
@ -41,24 +41,22 @@ using std::string;
|
||||||
string ConvertNumToSpokenDigits(const string &n);
|
string ConvertNumToSpokenDigits(const string &n);
|
||||||
|
|
||||||
// Convert an integer to spoken digits
|
// Convert an integer to spoken digits
|
||||||
string ConvertNumToSpokenDigits(int n);
|
string ConvertNumToSpokenDigits(const int& n);
|
||||||
|
string decimalNumeral(const int& n);
|
||||||
// Convert a 2 digit rwy number to a spoken-style string
|
|
||||||
string ConvertRwyNumToSpokenString(int n);
|
|
||||||
|
|
||||||
// Convert rwy number string to a spoken-style string
|
// Convert rwy number string to a spoken-style string
|
||||||
// eg "05L" to "zero five left"
|
// eg "15L" to "one five left"
|
||||||
// Assumes we get a two-digit string optionally appended with R, L, or C
|
// Assumes we get a string of digits optionally appended with R, L, or C
|
||||||
// eg 01 07L 29R 36
|
// eg 1 7L 29R 36
|
||||||
// Anything else is not guaranteed to be handled correctly!
|
|
||||||
string ConvertRwyNumToSpokenString(const string &s);
|
string ConvertRwyNumToSpokenString(const string &s);
|
||||||
|
|
||||||
// Return the phonetic letter of a letter represented as an integer 1->26
|
const int LTRS(26);
|
||||||
string GetPhoneticIdent(int i);
|
// Return the phonetic letter of a letter represented as an integer 0..25
|
||||||
|
string GetPhoneticLetter(const int i);
|
||||||
|
|
||||||
// Return the phonetic letter of a character in the range a-z or A-Z.
|
// Return the phonetic letter of a character in the range a-z or A-Z.
|
||||||
// Currently always returns prefixed by lowercase.
|
// Currently always returns prefixed by lowercase.
|
||||||
string GetPhoneticIdent(char c);
|
string GetPhoneticLetter(char c);
|
||||||
|
|
||||||
// Get the compass direction associated with a heading in degrees
|
// Get the compass direction associated with a heading in degrees
|
||||||
// Currently returns 8 direction resolution (N, NE, E etc...)
|
// Currently returns 8 direction resolution (N, NE, E etc...)
|
||||||
|
|
|
@ -408,18 +408,19 @@ double FGApproach::angle_diff_deg( const double &a1, const double &a2) {
|
||||||
void FGApproach::calc_wp( const int &i ) {
|
void FGApproach::calc_wp( const int &i ) {
|
||||||
|
|
||||||
int j;
|
int j;
|
||||||
double course, d, cd, a1;
|
double course, d, cd, a1, az2;
|
||||||
|
|
||||||
int wpn = planes[i].wpn;
|
int wpn = planes[i].wpn;
|
||||||
// waypoint 0: Threshold of active runway
|
// waypoint 0: Threshold of active runway
|
||||||
course = SGGeoc::courseRad(SGGeoc::fromDegM(lon, lat, 6e6), SGGeoc::fromDegM(active_rw_lon, active_rw_lat, 6e6));
|
SGGeod activeRunway(SGGeod::fromDeg(active_rw_lon, active_rw_lat));
|
||||||
d = SGGeoc::distanceM(SGGeoc::fromDegM(lon, lat, 6e6), SGGeoc::fromDegM(active_rw_lon, active_rw_lat, 6e6));
|
SGGeodesy::inverse(_geod, activeRunway, course, az2, d);
|
||||||
|
|
||||||
double d1 = active_rw_hdg+180.0;
|
double d1 = active_rw_hdg+180.0;
|
||||||
if ( d1 > 360.0 ) d1 -=360.0;
|
if ( d1 > 360.0 ) d1 -=360.0;
|
||||||
calc_cd_head_dist(360.0-course*SGD_RADIANS_TO_DEGREES, d/SG_NM_TO_METER,
|
calc_cd_head_dist(360.0-course, d/SG_NM_TO_METER,
|
||||||
d1, active_rw_len/SG_NM_TO_METER/2.0,
|
d1, active_rw_len/SG_NM_TO_METER/2.0,
|
||||||
&planes[i].wpts[wpn][0], &planes[i].wpts[wpn][1]);
|
&planes[i].wpts[wpn][0], &planes[i].wpts[wpn][1]);
|
||||||
planes[i].wpts[wpn][2] = elev;
|
planes[i].wpts[wpn][2] = _geod.getElevationM();
|
||||||
planes[i].wpts[wpn][4] = 0.0;
|
planes[i].wpts[wpn][4] = 0.0;
|
||||||
planes[i].wpts[wpn][5] = 0.0;
|
planes[i].wpts[wpn][5] = 0.0;
|
||||||
wpn += 1;
|
wpn += 1;
|
||||||
|
@ -506,7 +507,7 @@ void FGApproach::calc_wp( const int &i ) {
|
||||||
// ====================
|
// ====================
|
||||||
// vertical navigation
|
// vertical navigation
|
||||||
// ====================
|
// ====================
|
||||||
double alt = elev+3000.0;
|
double alt = _geod.getElevationM()+3000.0;
|
||||||
planes[i].wpts[1][2] = round_alt( true, alt );
|
planes[i].wpts[1][2] = round_alt( true, alt );
|
||||||
for ( j=2; j<wpn-1; ++j ) {
|
for ( j=2; j<wpn-1; ++j ) {
|
||||||
double dalt = planes[i].alt - planes[i].wpts[j-1][2];
|
double dalt = planes[i].alt - planes[i].wpts[j-1][2];
|
||||||
|
@ -585,11 +586,11 @@ void FGApproach::update_plane_dat() {
|
||||||
planes[i].hdg = hdg_node->getDoubleValue();
|
planes[i].hdg = hdg_node->getDoubleValue();
|
||||||
planes[i].spd = speed_node->getDoubleValue();
|
planes[i].spd = speed_node->getDoubleValue();
|
||||||
|
|
||||||
double course, distance;
|
double course, distance, az2;
|
||||||
course = SGGeoc::courseRad(SGGeoc::fromDegM(lon, lat, 6e6), SGGeoc::fromDegM(planes[i].lon, active_rw_lat, 6e6));
|
SGGeod plane(SGGeod::fromDeg(planes[1].lon, active_rw_lat));
|
||||||
distance = SGGeoc::distanceM(SGGeoc::fromDegM(lon, lat, 6e6), SGGeoc::fromDegM(planes[i].lon, active_rw_lat, 6e6));
|
SGGeodesy::inverse(_geod, plane, course, az2, distance);
|
||||||
planes[i].dist = distance/SG_NM_TO_METER;
|
planes[i].dist = distance * SG_METER_TO_NM;
|
||||||
planes[i].brg = 360.0-course*SGD_RADIANS_TO_DEGREES;
|
planes[i].brg = 360.0-course;
|
||||||
|
|
||||||
//cout << "Plane Id: " << planes[i].ident << " Distance to " << ident
|
//cout << "Plane Id: " << planes[i].ident << " Distance to " << ident
|
||||||
// << " is " << planes[i].dist << " miles " << "Bearing " << planes[i].brg << endl;
|
// << " is " << planes[i].dist << " miles " << "Bearing " << planes[i].brg << endl;
|
||||||
|
|
|
@ -19,209 +19,433 @@
|
||||||
// along with this program; if not, write to the Free Software
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
/////
|
||||||
|
///// TODO: _Cumulative_ sky coverage.
|
||||||
|
///// TODO: wind _gust_
|
||||||
|
///// TODO: more-sensible encoding of voice samples
|
||||||
|
///// u-law? outright synthesis?
|
||||||
|
/////
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
# include <config.h>
|
# include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "atis.hxx"
|
||||||
|
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
|
|
||||||
#include <stdlib.h> // atoi()
|
#include <stdlib.h> // atoi()
|
||||||
#include <stdio.h> // sprintf
|
#include <stdio.h> // sprintf
|
||||||
#include <string>
|
#include <string>
|
||||||
using std::string;
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
using std::cout;
|
|
||||||
|
|
||||||
|
#include <boost/tuple/tuple.hpp>
|
||||||
|
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
|
|
||||||
#include <Environment/environment_mgr.hxx>
|
#include <Environment/environment_mgr.hxx>
|
||||||
#include <Environment/environment.hxx>
|
#include <Environment/environment.hxx>
|
||||||
|
#include <Environment/atmosphere.hxx>
|
||||||
|
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
#include <Main/globals.hxx>
|
#include <Main/globals.hxx>
|
||||||
#include <Airports/runways.hxx>
|
#include <Airports/runways.hxx>
|
||||||
|
|
||||||
#include "atis.hxx"
|
|
||||||
#include "commlist.hxx"
|
#include "commlist.hxx"
|
||||||
#include "ATCutils.hxx"
|
#include "ATCutils.hxx"
|
||||||
#include "ATCmgr.hxx"
|
#include "ATCmgr.hxx"
|
||||||
|
|
||||||
|
using std::cout;
|
||||||
|
using std::cout;
|
||||||
|
using boost::ref;
|
||||||
|
using boost::make_tuple;
|
||||||
|
|
||||||
FGATIS::FGATIS() :
|
FGATIS::FGATIS() :
|
||||||
transmission(""),
|
transmission(""),
|
||||||
trans_ident(""),
|
trans_ident(""),
|
||||||
atis_failed(false),
|
old_volume(0),
|
||||||
refname("atis")
|
atis_failed(false),
|
||||||
//type(ATIS)
|
attention(0),
|
||||||
|
_prev_display(0),
|
||||||
|
refname("atis")
|
||||||
{
|
{
|
||||||
_vPtr = globals->get_ATC_mgr()->GetVoicePointer(ATIS);
|
_vPtr = globals->get_ATC_mgr()->GetVoicePointer(ATIS);
|
||||||
_voiceOK = (_vPtr == NULL ? false : true);
|
_voiceOK = (_vPtr == NULL ? false : true);
|
||||||
_type = ATIS;
|
if (!(_type != ATIS || _type == AWOS)) {
|
||||||
|
SG_LOG(SG_ATC, SG_ALERT, "ERROR - _type not ATIS or AWOS in atis.cxx");
|
||||||
|
}
|
||||||
|
fgTie("/environment/attention", this, (int_getter)0, &FGATIS::attend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hint:
|
||||||
|
// http://localhost:5400/environment/attention?value=1&submit=update
|
||||||
|
|
||||||
FGATIS::~FGATIS() {
|
FGATIS::~FGATIS() {
|
||||||
|
fgUntie("/environment/attention");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FGATIS::Init() {
|
||||||
|
// Nothing to see here. Move along.
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FGATIS::attend (int attn)
|
||||||
|
{
|
||||||
|
attention = attn;
|
||||||
|
#ifdef ATMO_TEST
|
||||||
|
int flag = fgGetInt("/sim/logging/atmo");
|
||||||
|
if (flag) {
|
||||||
|
FGAltimeter().check_model();
|
||||||
|
FGAltimeter().dump_stack();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Main update function - checks whether we are displaying or not the correct message.
|
// Main update function - checks whether we are displaying or not the correct message.
|
||||||
void FGATIS::Update(double dt) {
|
void FGATIS::Update(double dt) {
|
||||||
if(_display) {
|
cur_time = globals->get_time_params()->get_cur_time();
|
||||||
if(_displaying) {
|
msg_OK = (msg_time < cur_time);
|
||||||
// Check if we need to update the message
|
#if 0
|
||||||
// - basically every hour and if the weather changes significantly at the station
|
if (msg_OK || _display != _prev_display) {
|
||||||
} else {
|
cout << "ATIS Update: " << _display << " " << _prev_display
|
||||||
// We need to get and display the message
|
<< " len: " << transmission.length()
|
||||||
UpdateTransmission();
|
<< " oldvol: " << old_volume
|
||||||
//cout << "ATIS.CXX - calling ATCMgr to render transmission..." << endl;
|
<< " dt: " << dt << endl;
|
||||||
Render(transmission, refname, true);
|
msg_time = cur_time;
|
||||||
_displaying = true;
|
}
|
||||||
}
|
#endif
|
||||||
} else {
|
if(_display) {
|
||||||
// We shouldn't be displaying
|
double volume(0);
|
||||||
if(_displaying) {
|
for (map<string,int>::iterator act = active_on.begin();
|
||||||
//cout << "ATIS.CXX - calling NoRender()..." << endl;
|
act != active_on.end(); act++) {
|
||||||
NoRender(refname);
|
string prop = "/instrumentation/" + act->first + "/volume";
|
||||||
_displaying = false;
|
volume += globals->get_props()->getDoubleValue(prop.c_str());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Check if we need to update the message
|
||||||
|
// - basically every hour and if the weather changes significantly at the station
|
||||||
|
// If !_prev_display, the radio had been detuned for a while and our
|
||||||
|
// "transmission" variable was lost when we were de-instantiated.
|
||||||
|
int rslt = GenTransmission(!_prev_display, attention);
|
||||||
|
if (rslt || volume != old_volume) {
|
||||||
|
//cout << "ATIS calling ATC::render volume: " << volume << endl;
|
||||||
|
Render(transmission, volume, refname, true);
|
||||||
|
old_volume = volume;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We shouldn't be displaying
|
||||||
|
//cout << "ATIS.CXX - calling NoRender()..." << endl;
|
||||||
|
NoRender(refname);
|
||||||
|
}
|
||||||
|
_prev_display = _display;
|
||||||
|
attention = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the actual broadcast ATIS transmission.
|
string uppercase(const string &s) {
|
||||||
void FGATIS::UpdateTransmission() {
|
string rslt(s);
|
||||||
double visibility;
|
for(string::iterator p = rslt.begin(); p != rslt.end(); p++){
|
||||||
char buf[10];
|
*p = toupper(*p);
|
||||||
int phonetic_id;
|
}
|
||||||
string phonetic_id_string;
|
return rslt;
|
||||||
string time_str = fgGetString("sim/time/gmt-string");
|
}
|
||||||
int hours;
|
|
||||||
// int minutes;
|
// Replace all occurrences of a given word.
|
||||||
|
// Words in the original string must be separated by hyphens (not spaces).
|
||||||
FGEnvironment stationweather =
|
// We check for the word as given, and for the all-caps version thereof.
|
||||||
((FGEnvironmentMgr *)globals->get_subsystem("environment"))
|
string replace_word(const string _orig, const string _www, const string _nnn){
|
||||||
->getEnvironment(lat, lon, 0.0);
|
// The following are so we can match words at the beginning
|
||||||
|
// and end of the string.
|
||||||
transmission = "";
|
string orig = "-" + _orig + "-";
|
||||||
|
string www = "-" + _www + "-";
|
||||||
// UK CAA radiotelephony manual indicated ATIS transmissions start with "This is"
|
string nnn = "-" + _nnn + "-";
|
||||||
// Not sure about rest of the world though.
|
|
||||||
transmission += "This_is ";
|
size_t where(0);
|
||||||
// transmitted station name.
|
for ( ; (where = orig.find(www, where)) != string::npos ; ) {
|
||||||
transmission += name;
|
orig.replace(where, www.length(), nnn);
|
||||||
// Add "Information"
|
where += nnn.length();
|
||||||
transmission += " information";
|
}
|
||||||
|
|
||||||
//cout << "In atis.cxx, time_str = " << time_str << '\n';
|
www = uppercase(www);
|
||||||
// Add the recording identifier
|
for ( ; (where = orig.find(www, where)) != string::npos ; ) {
|
||||||
// For now we will assume we only transmit every hour
|
orig.replace(where, www.length(), nnn);
|
||||||
hours = atoi((time_str.substr(1,2)).c_str()); //Warning - this is fragile if the
|
where += nnn.length();
|
||||||
//time string format changes
|
}
|
||||||
//cout << "In atis.cxx, hours = " << hours << endl;
|
where = orig.length();
|
||||||
phonetic_id = current_commlist->GetCallSign(ident, hours, 0);
|
return orig.substr(1, where-2);
|
||||||
phonetic_id_string = GetPhoneticIdent(phonetic_id);
|
}
|
||||||
transmission += " ";
|
|
||||||
transmission += phonetic_id_string;
|
// Normally the interval is 1 hour,
|
||||||
|
// but you can shorten it for testing.
|
||||||
// Output the recording time. - we'll just output the last whole hour for now.
|
const int minute(60); // measured in seconds
|
||||||
// FIXME - this only gets GMT time but that appears to be all the clock outputs for now
|
#ifdef ATIS_TEST
|
||||||
//cout << "in atis.cxx, time = " << time_str << endl;
|
const int ATIS_interval(2*minute);
|
||||||
transmission = transmission + " / Weather " + ConvertNumToSpokenDigits((time_str.substr(0,3) + "00")) + " hours zulu";
|
#else
|
||||||
|
const int ATIS_interval(60*minute);
|
||||||
// Get the temperature
|
#endif
|
||||||
int temp;
|
|
||||||
temp = (int)stationweather.get_temperature_degc();
|
// Generate the actual broadcast ATIS transmission.
|
||||||
|
// Regen means regenerate the /current/ transmission.
|
||||||
// HACK ALERT - at the moment the new environment subsystem returns bogus temperatures
|
// Special means generate a new transmission, with a new sequence.
|
||||||
// FIXME - take out this hack when this gets fixed upstream
|
// Returns 1 if we actually generated something.
|
||||||
if((temp < -50) || (temp > 60)) {
|
int FGATIS::GenTransmission(const int regen, const int special) {
|
||||||
temp = 15;
|
using namespace atmodel;
|
||||||
}
|
|
||||||
|
string BRK = ".\n";
|
||||||
sprintf(buf, "%i", abs(temp));
|
|
||||||
transmission += " / Temperature ";
|
double tstamp = atof(fgGetString("sim/time/elapsed-sec"));
|
||||||
if(temp < 0) {
|
int interval = ATIS ? ATIS_interval : 2*minute; // AWOS updated frequently
|
||||||
transmission += "minus ";
|
int sequence = current_commlist->GetAtisSequence(ident,
|
||||||
}
|
tstamp, interval, special);
|
||||||
string tempstr1 = buf;
|
if (!regen && sequence > LTRS) {
|
||||||
string tempstr2;
|
//xx if (msg_OK) cout << "ATIS: no change: " << sequence << endl;
|
||||||
transmission += ConvertNumToSpokenDigits(tempstr1);
|
//xx msg_time = cur_time;
|
||||||
transmission += " degrees_Celsius";
|
return 0; // no change since last time
|
||||||
|
}
|
||||||
// Get the visibility
|
|
||||||
visibility = stationweather.get_visibility_m();
|
const int bs(100);
|
||||||
sprintf(buf, "%i", int(visibility/1600));
|
char buf[bs];
|
||||||
transmission += " / Visibility ";
|
string time_str = fgGetString("sim/time/gmt-string");
|
||||||
tempstr1 = buf;
|
string hours, mins;
|
||||||
transmission += ConvertNumToSpokenDigits(tempstr1);
|
string phonetic_seq_string;
|
||||||
transmission += " miles";
|
|
||||||
|
transmission = "";
|
||||||
// Get the cloudbase
|
|
||||||
// FIXME: kludge for now
|
// UK CAA radiotelephony manual indicated ATIS transmissions start
|
||||||
if (strcmp(fgGetString("/environment/clouds/layer[0]/type"), "clear")) {
|
// with "This is ..."
|
||||||
double cloudbase =
|
// In the US they just start with the airport name.
|
||||||
fgGetDouble("/environment/clouds/layer[0]/elevation-ft");
|
// transmission += "This_is ";
|
||||||
// For some reason the altitude returned doesn't seem to correspond to the actual cloud altitude.
|
|
||||||
char buf3[10];
|
// SG_LOG(SG_ATC, SG_ALERT, "ATIS: facility name: " << name);
|
||||||
char buf4[10];
|
|
||||||
// cout << "cloudbase = " << cloudbase << endl;
|
// Note that at this point, multi-word facility names
|
||||||
sprintf(buf3, "%i", int(cloudbase)/1000);
|
// will sometimes contain hyphens, not spaces.
|
||||||
sprintf(buf4, "%i", ((int)cloudbase % 1000)/100);
|
// Force the issue, just to be safe:
|
||||||
transmission += " / Cloudbase";
|
|
||||||
if(int(cloudbase)/1000) {
|
string name2 = name;
|
||||||
tempstr1 = buf3;
|
for(string::iterator p = name2.begin(); p != name2.end(); p++){
|
||||||
transmission = transmission + " " + ConvertNumToSpokenDigits(tempstr1) + " thousand";
|
if (*p == ' ') *p = '-';
|
||||||
}
|
}
|
||||||
if(((int)cloudbase % 1000)/100) {
|
|
||||||
tempstr1 = buf4;
|
///////////////
|
||||||
transmission = transmission + " " + ConvertNumToSpokenDigits(tempstr1) + " hundred";
|
// FIXME: This would be more flexible and more extensible
|
||||||
}
|
// if the mappings were taken from an XML file, not hard-coded.
|
||||||
transmission += " feet";
|
///////////////
|
||||||
}
|
|
||||||
|
// Remap some abbreviations that occur in apt.dat, to
|
||||||
// Get the pressure / altimeter
|
// make things nicer for the text-to-speech system:
|
||||||
double P = fgGetDouble("/environment/pressure-sea-level-inhg");
|
name2 = replace_word(name2, "Intl", "International");
|
||||||
if(ident.substr(0,2) == "EG" && fgGetBool("/sim/atc/use-millibars") == true) {
|
name2 = replace_word(name2, "Rgnl", "Regional");
|
||||||
// Convert to millibars for the UK!
|
name2 = replace_word(name2, "Co", "County");
|
||||||
P *= 33.864;
|
name2 = replace_word(name2, "Muni", "Municipal");
|
||||||
sprintf(buf, "%.0f", P);
|
name2 = replace_word(name2, "Mem", "Memorial");
|
||||||
} else {
|
name2 = replace_word(name2, "Fld", "Field");
|
||||||
sprintf(buf, "%.2f", P);
|
name2 = replace_word(name2, "AFB", "Air-Force-Base");
|
||||||
}
|
name2 = replace_word(name2, "AAF", "Army-Air-Field");
|
||||||
transmission += " / Altimeter ";
|
name2 = replace_word(name2, "MCAS", "Marine-Corps-Air-Station");
|
||||||
tempstr1 = buf;
|
transmission += name2 + " ";
|
||||||
transmission += ConvertNumToSpokenDigits(tempstr1);
|
if (_type == ATIS /* as opposed to AWOS */) {
|
||||||
|
transmission += "airport_information ";
|
||||||
// Based on the airport-id and wind get the active runway
|
phonetic_seq_string = GetPhoneticLetter(sequence); // Add the sequence letter
|
||||||
//FGRunway *r;
|
transmission += phonetic_seq_string + BRK;
|
||||||
double speed = stationweather.get_wind_speed_kt();
|
}
|
||||||
double hdg = stationweather.get_wind_from_heading_deg();
|
transmission += "Automated_weather_observation ";
|
||||||
if (speed == 0) {
|
// Warning - this is fragile if the time string format changes
|
||||||
hdg = 270; // This forces West-facing rwys to be used in no-wind situations
|
hours = time_str.substr(0,2).c_str();
|
||||||
// which is consistent with Flightgear's initial setup.
|
mins = time_str.substr(3,2).c_str();
|
||||||
transmission += " / Winds_light_and_variable";
|
// speak each digit separately:
|
||||||
} else {
|
transmission += ConvertNumToSpokenDigits(hours + mins);
|
||||||
// FIXME: get gust factor in somehow
|
transmission += " zulu weather" + BRK;
|
||||||
char buf5[10];
|
|
||||||
char buf6[10];
|
transmission += "Wind: ";
|
||||||
sprintf(buf5, "%i", int(speed));
|
|
||||||
sprintf(buf6, "%i", int(hdg));
|
double wind_speed = fgGetDouble("/environment/config/boundary/entry[0]/wind-speed-kt");
|
||||||
tempstr1 = buf5;
|
double wind_dir = fgGetDouble("/environment/config/boundary/entry[0]/wind-from-heading-deg");
|
||||||
tempstr2 = buf6;
|
while (wind_dir <= 0) wind_dir += 360;
|
||||||
transmission = transmission + " / Winds " + ConvertNumToSpokenDigits(tempstr1) + " knots from "
|
// The following isn't as bad a kludge as it might seem.
|
||||||
+ ConvertNumToSpokenDigits(tempstr2) + " degrees";
|
// It combines the magvar at the /aircraft/ location with
|
||||||
}
|
// the wind direction in the environment/config array.
|
||||||
|
// But if the aircraft is close enough to the station to
|
||||||
const FGAirport* apt = fgFindAirportID(ident);
|
// be receiving the ATIS signal, this should be a good-enough
|
||||||
assert(apt);
|
// approximation. For more-distant aircraft, the wind_dir
|
||||||
string rwy_no = apt->getActiveRunwayForUsage()->ident();
|
// shouldn't be corrected anyway.
|
||||||
if(rwy_no != "NN") {
|
// The less-kludgy approach would be to use the magvar associated
|
||||||
transmission += " / Landing_and_departing_runway ";
|
// with the station, but that is not tabulated in the stationweather
|
||||||
transmission += ConvertRwyNumToSpokenString(atoi(rwy_no.c_str()));
|
// structure as it stands, and computing it would be expensive.
|
||||||
//cout << "in atis.cxx, r.rwy_no = " << rwy_no << " r.id = " << r->id << " r.heading = " << r->heading << endl;
|
// Also note that as it stands, there is only one environment in
|
||||||
}
|
// the entire FG universe, so the aircraft environment is the same
|
||||||
|
// as the station environment anyway.
|
||||||
// Anything else?
|
wind_dir -= fgGetDouble("/environment/magnetic-variation-deg"); // wind_dir now magnetic
|
||||||
|
if (wind_speed == 0) {
|
||||||
transmission += " / Advise_controller_on_initial_contact_you_have ";
|
// Force west-facing rwys to be used in no-wind situations
|
||||||
transmission += phonetic_id_string;
|
// which is consistent with Flightgear's initial setup:
|
||||||
transmission += " /// ";
|
wind_dir = 270;
|
||||||
|
transmission += " light_and_variable";
|
||||||
|
} else {
|
||||||
|
// FIXME: get gust factor in somehow
|
||||||
|
snprintf(buf, bs, "%03.0f", 5*round(wind_dir/5));
|
||||||
|
transmission += ConvertNumToSpokenDigits(buf);
|
||||||
|
|
||||||
|
snprintf(buf, bs, "%1.0f", wind_speed);
|
||||||
|
transmission += " at " + ConvertNumToSpokenDigits(buf) + BRK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int did_some(0);
|
||||||
|
int did_ceiling(0);
|
||||||
|
|
||||||
|
for (int layer = 0; layer <= 4; layer++) {
|
||||||
|
snprintf(buf, bs, "/environment/clouds/layer[%i]/coverage", layer);
|
||||||
|
string coverage = fgGetString(buf);
|
||||||
|
if (coverage == "clear") continue;
|
||||||
|
snprintf(buf, bs, "/environment/clouds/layer[%i]/thickness-ft", layer);
|
||||||
|
if (fgGetDouble(buf) == 0) continue;
|
||||||
|
snprintf(buf, bs, "/environment/clouds/layer[%i]/elevation-ft", layer);
|
||||||
|
double ceiling = int(fgGetDouble(buf) - _geod.getElevationFt());
|
||||||
|
if (ceiling > 12000) continue;
|
||||||
|
if (coverage == "scattered") {
|
||||||
|
if (!did_some) transmission += " Sky_condition: ";
|
||||||
|
did_some++;
|
||||||
|
} else /* must be a ceiling */ if (!did_ceiling) {
|
||||||
|
transmission += " Ceiling: ";
|
||||||
|
did_ceiling++;
|
||||||
|
did_some++;
|
||||||
|
} else {
|
||||||
|
transmission += " ";
|
||||||
|
}
|
||||||
|
int cig00 = int(round(ceiling/100)); // hundreds of feet
|
||||||
|
if (cig00) {
|
||||||
|
int cig000 = cig00/10;
|
||||||
|
cig00 -= cig000*10; // just the hundreds digit
|
||||||
|
if (cig000) {
|
||||||
|
snprintf(buf, bs, "%i", cig000);
|
||||||
|
transmission += ConvertNumToSpokenDigits(buf);
|
||||||
|
transmission += " thousand ";
|
||||||
|
}
|
||||||
|
if (cig00) {
|
||||||
|
snprintf(buf, bs, "%i", cig00);
|
||||||
|
transmission += ConvertNumToSpokenDigits(buf);
|
||||||
|
transmission += " hundred ";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Should this be "sky obscured?"
|
||||||
|
transmission += " zero "; // not "zero hundred"
|
||||||
|
}
|
||||||
|
transmission += coverage + BRK;
|
||||||
|
}
|
||||||
|
|
||||||
|
transmission += "Temperature: ";
|
||||||
|
double Tsl = fgGetDouble("/environment/temperature-sea-level-degc");
|
||||||
|
int temp = int(round(FGAtmo().fake_T_vs_a_us(_geod.getElevationFt(), Tsl)));
|
||||||
|
if(temp < 0) {
|
||||||
|
transmission += "minus ";
|
||||||
|
}
|
||||||
|
snprintf(buf, bs, "%i", abs(temp));
|
||||||
|
transmission += ConvertNumToSpokenDigits(buf);
|
||||||
|
transmission += " dewpoint ";
|
||||||
|
double dpsl = fgGetDouble("/environment/dewpoint-sea-level-degc");
|
||||||
|
temp = int(round(FGAtmo().fake_dp_vs_a_us(dpsl, _geod.getElevationFt())));
|
||||||
|
if(temp < 0) {
|
||||||
|
transmission += "minus ";
|
||||||
|
}
|
||||||
|
snprintf(buf, bs, "%i", abs(temp));
|
||||||
|
transmission += ConvertNumToSpokenDigits(buf) + BRK;
|
||||||
|
|
||||||
|
|
||||||
|
transmission += "Visibility: ";
|
||||||
|
double visibility = fgGetDouble("/environment/config/boundary/entry[0]/visibility-m");
|
||||||
|
visibility /= atmodel::sm; // convert to statute miles
|
||||||
|
if (visibility < 0.25) {
|
||||||
|
transmission += "less than one quarter";
|
||||||
|
} else if (visibility < 0.5) {
|
||||||
|
transmission += "one quarter";
|
||||||
|
} else if (visibility < 0.75) {
|
||||||
|
transmission += "one half";
|
||||||
|
} else if (visibility < 1.0) {
|
||||||
|
transmission += "three quarters";
|
||||||
|
} else if (visibility >= 1.5 && visibility < 2.0) {
|
||||||
|
transmission += "one and one half";
|
||||||
|
} else {
|
||||||
|
// integer miles
|
||||||
|
if (visibility > 10) visibility = 10;
|
||||||
|
sprintf(buf, "%i", int(.5 + visibility));
|
||||||
|
transmission += ConvertNumToSpokenDigits(buf);
|
||||||
|
}
|
||||||
|
transmission += BRK;
|
||||||
|
|
||||||
|
transmission += "Altimeter: ";
|
||||||
|
double myQNH;
|
||||||
|
double Psl = fgGetDouble("/environment/pressure-sea-level-inhg");
|
||||||
|
{
|
||||||
|
double press, temp;
|
||||||
|
|
||||||
|
make_tuple(ref(press), ref(temp)) = PT_vs_hpt(_geod.getElevationM(), Psl*inHg, Tsl + freezing);
|
||||||
|
#if 0
|
||||||
|
SG_LOG(SG_ATC, SG_ALERT, "Field P: " << press << " T: " << temp);
|
||||||
|
SG_LOG(SG_ATC, SG_ALERT, "based on elev " << elev
|
||||||
|
<< " Psl: " << Psl
|
||||||
|
<< " Tsl: " << Tsl);
|
||||||
|
#endif
|
||||||
|
myQNH = FGAtmo().QNH(_geod.getElevationM(), press);
|
||||||
|
}
|
||||||
|
if(ident.substr(0,2) == "EG" && fgGetBool("/sim/atc/use-millibars")) {
|
||||||
|
// Convert to millibars for the UK!
|
||||||
|
myQNH /= mbar;
|
||||||
|
if (myQNH > 1000) myQNH -= 1000; // drop high digit
|
||||||
|
snprintf(buf, bs, "%03.0f", myQNH);
|
||||||
|
} else {
|
||||||
|
myQNH /= inHg;
|
||||||
|
myQNH *= 100.; // shift two decimal places
|
||||||
|
snprintf(buf, bs, "%04.0f", myQNH);
|
||||||
|
}
|
||||||
|
transmission += ConvertNumToSpokenDigits(buf) + BRK;
|
||||||
|
|
||||||
|
if (_type == ATIS /* as opposed to AWOS */) {
|
||||||
|
const FGAirport* apt = fgFindAirportID(ident);
|
||||||
|
assert(apt);
|
||||||
|
string rwy_no = apt->getActiveRunwayForUsage()->ident();
|
||||||
|
if(rwy_no != "NN") {
|
||||||
|
transmission += "Landing_and_departing_runway ";
|
||||||
|
transmission += ConvertRwyNumToSpokenString(rwy_no) + BRK;
|
||||||
|
if (msg_OK) {
|
||||||
|
msg_time = cur_time;
|
||||||
|
//cout << "In atis.cxx, r.rwy_no: " << rwy_no
|
||||||
|
// << " wind_dir: " << wind_dir << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transmission += "On_initial_contact_advise_you_have_information ";
|
||||||
|
transmission += phonetic_seq_string;
|
||||||
|
transmission += "... " + BRK;
|
||||||
|
}
|
||||||
|
#ifdef ATIS_TEST
|
||||||
|
cout << "**** ATIS active on:";
|
||||||
|
#endif
|
||||||
|
for (map<string,int>::iterator act = active_on.begin(); act != active_on.end(); act++){
|
||||||
|
string prop = "/instrumentation/" + act->first + "/atis";
|
||||||
|
globals->get_props()->setStringValue(prop.c_str(),
|
||||||
|
("<pre>\n" + transmission + "</pre>\n").c_str());
|
||||||
|
#ifdef ATIS_TEST
|
||||||
|
cout << " " << prop;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef ATIS_TEST
|
||||||
|
cout << " ****" << endl;
|
||||||
|
cout << transmission << endl;
|
||||||
|
// Note that even if we aren't outputting the transmission
|
||||||
|
// on stdout, you can still see it by pointing a web browser
|
||||||
|
// at the property tree. The second comm radio is:
|
||||||
|
// http://localhost:5400/instrumentation/comm[1]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Take the previous English-looking string and munge it to
|
||||||
|
// be relatively-more acceptable to the primitive tts system.
|
||||||
|
// Note that : ; and . are among the token-delimeters recognized
|
||||||
|
// by the tts system.
|
||||||
|
for (unsigned int where;;) {
|
||||||
|
where = transmission.find_first_of(":.");
|
||||||
|
if (where == string::npos) break;
|
||||||
|
transmission.replace(where, 1, " /_ ");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,22 +23,18 @@
|
||||||
#ifndef _FG_ATIS_HXX
|
#ifndef _FG_ATIS_HXX
|
||||||
#define _FG_ATIS_HXX
|
#define _FG_ATIS_HXX
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
#include <simgear/math/sg_geodesy.hxx>
|
|
||||||
#include <simgear/misc/sgstream.hxx>
|
|
||||||
#include <simgear/magvar/magvar.hxx>
|
|
||||||
#include <simgear/timing/sg_time.hxx>
|
#include <simgear/timing/sg_time.hxx>
|
||||||
|
|
||||||
# include <iosfwd>
|
|
||||||
|
|
||||||
#include "ATC.hxx"
|
#include "ATC.hxx"
|
||||||
|
|
||||||
//DCL - a complete guess for now.
|
//DCL - a complete guess for now.
|
||||||
#define FG_ATIS_DEFAULT_RANGE 30
|
#define FG_ATIS_DEFAULT_RANGE 30
|
||||||
|
|
||||||
|
|
||||||
class FGATIS : public FGATC {
|
class FGATIS : public FGATC {
|
||||||
|
|
||||||
//atc_type type;
|
//atc_type type;
|
||||||
|
@ -48,8 +44,15 @@ class FGATIS : public FGATC {
|
||||||
|
|
||||||
// for failure modeling
|
// for failure modeling
|
||||||
std::string trans_ident; // transmitted ident
|
std::string trans_ident; // transmitted ident
|
||||||
|
double old_volume;
|
||||||
bool atis_failed; // atis failed?
|
bool atis_failed; // atis failed?
|
||||||
|
time_t msg_time; // for moderating error messages
|
||||||
|
time_t cur_time;
|
||||||
|
int msg_OK;
|
||||||
|
int attention;
|
||||||
|
|
||||||
|
bool _prev_display; // Previous value of _display flag
|
||||||
|
|
||||||
// Aircraft position
|
// Aircraft position
|
||||||
// ATIS is actually a special case in that unlike other ATC eg.tower it doesn't actually know about
|
// ATIS is actually a special case in that unlike other ATC eg.tower it doesn't actually know about
|
||||||
// or the whereabouts of the aircraft it is transmitting to. However, to ensure consistancy of
|
// or the whereabouts of the aircraft it is transmitting to. However, to ensure consistancy of
|
||||||
|
@ -63,7 +66,9 @@ class FGATIS : public FGATC {
|
||||||
|
|
||||||
FGATIS(void);
|
FGATIS(void);
|
||||||
~FGATIS(void);
|
~FGATIS(void);
|
||||||
|
virtual void Init();
|
||||||
|
void attend (int);
|
||||||
|
|
||||||
//run the ATIS instance
|
//run the ATIS instance
|
||||||
void Update(double dt);
|
void Update(double dt);
|
||||||
|
|
||||||
|
@ -75,10 +80,12 @@ class FGATIS : public FGATC {
|
||||||
|
|
||||||
std::string refname; // Holds the refname of a transmission in progress
|
std::string refname; // Holds the refname of a transmission in progress
|
||||||
|
|
||||||
//Update the transmission string
|
int GenTransmission(const int regen,
|
||||||
void UpdateTransmission(void);
|
const int special); // Generate the transmission string
|
||||||
|
|
||||||
friend std::istream& operator>> ( std::istream&, FGATIS& );
|
friend std::istream& operator>> ( std::istream&, FGATIS& );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef int (FGATIS::*int_getter)() const;
|
||||||
|
|
||||||
#endif // _FG_ATIS_HXX
|
#endif // _FG_ATIS_HXX
|
||||||
|
|
|
@ -24,14 +24,16 @@
|
||||||
# include <config.h>
|
# include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "commlist.hxx"
|
||||||
|
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
#include <simgear/misc/sg_path.hxx>
|
||||||
#include <simgear/misc/sgstream.hxx>
|
#include <simgear/misc/sgstream.hxx>
|
||||||
#include <simgear/math/sg_geodesy.hxx>
|
#include <simgear/math/sg_geodesy.hxx>
|
||||||
#include <simgear/math/sg_random.h>
|
#include <simgear/math/sg_random.h>
|
||||||
#include <simgear/bucket/newbucket.hxx>
|
#include <simgear/bucket/newbucket.hxx>
|
||||||
#include <Airports/simple.hxx>
|
#include <Airports/simple.hxx>
|
||||||
|
|
||||||
#include "commlist.hxx"
|
|
||||||
#include "ATCutils.hxx"
|
#include "ATCutils.hxx"
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,6 +42,7 @@ FGCommList *current_commlist;
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
FGCommList::FGCommList( void ) {
|
FGCommList::FGCommList( void ) {
|
||||||
|
sg_srandom_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,23 +54,23 @@ FGCommList::~FGCommList( void ) {
|
||||||
// load the navaids and build the map
|
// load the navaids and build the map
|
||||||
bool FGCommList::init( const SGPath& path ) {
|
bool FGCommList::init( const SGPath& path ) {
|
||||||
|
|
||||||
SGPath temp = path;
|
SGPath temp = path;
|
||||||
commlist_freq.erase(commlist_freq.begin(), commlist_freq.end());
|
commlist_freq.erase(commlist_freq.begin(), commlist_freq.end());
|
||||||
commlist_bck.erase(commlist_bck.begin(), commlist_bck.end());
|
commlist_bck.erase(commlist_bck.begin(), commlist_bck.end());
|
||||||
temp.append( "ATC/default.atis" );
|
temp.append( "ATC/default.atis" );
|
||||||
LoadComms(temp);
|
LoadComms(temp);
|
||||||
temp = path;
|
temp = path;
|
||||||
temp.append( "ATC/default.tower" );
|
temp.append( "ATC/default.tower" );
|
||||||
LoadComms(temp);
|
LoadComms(temp);
|
||||||
temp = path;
|
temp = path;
|
||||||
temp.append( "ATC/default.ground" );
|
temp.append( "ATC/default.ground" );
|
||||||
LoadComms(temp);
|
LoadComms(temp);
|
||||||
temp = path;
|
temp = path;
|
||||||
temp.append( "ATC/default.approach" );
|
temp.append( "ATC/default.approach" );
|
||||||
LoadComms(temp);
|
LoadComms(temp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FGCommList::LoadComms(const SGPath& path) {
|
bool FGCommList::LoadComms(const SGPath& path) {
|
||||||
|
|
||||||
|
@ -76,34 +79,34 @@ bool FGCommList::LoadComms(const SGPath& path) {
|
||||||
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
|
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// read in each line of the file
|
// read in each line of the file
|
||||||
fin >> skipcomment;
|
fin >> skipcomment;
|
||||||
|
|
||||||
while ( !fin.eof() ) {
|
while ( !fin.eof() ) {
|
||||||
ATCData a;
|
ATCData a;
|
||||||
fin >> a;
|
fin >> a;
|
||||||
if(a.type == INVALID) {
|
if(a.type == INVALID) {
|
||||||
SG_LOG(SG_GENERAL, SG_DEBUG, "WARNING - INVALID type found in " << path.str() << '\n');
|
SG_LOG(SG_GENERAL, SG_DEBUG, "WARNING - INVALID type found in " << path.str() << '\n');
|
||||||
} else {
|
} else {
|
||||||
// Push all stations onto frequency map
|
// Push all stations onto frequency map
|
||||||
commlist_freq[a.freq].push_back(a);
|
commlist_freq[a.freq].push_back(a);
|
||||||
|
|
||||||
// Push non-atis stations onto bucket map as well
|
// Push non-atis stations onto bucket map as well
|
||||||
// In fact, push all stations onto bucket map for now so FGATCMgr::GetFrequency() works.
|
// In fact, push all stations onto bucket map for now so FGATCMgr::GetFrequency() works.
|
||||||
//if(a.type != ATIS) {
|
//if(a.type != ATIS and/or AWOS?) {
|
||||||
// get bucket number
|
// get bucket number
|
||||||
SGBucket bucket(a.lon, a.lat);
|
SGBucket bucket(a.geod);
|
||||||
int bucknum = bucket.gen_index();
|
int bucknum = bucket.gen_index();
|
||||||
commlist_bck[bucknum].push_back(a);
|
commlist_bck[bucknum].push_back(a);
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
fin >> skipcomment;
|
fin >> skipcomment;
|
||||||
}
|
}
|
||||||
|
|
||||||
fin.close();
|
fin.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,98 +114,89 @@ bool FGCommList::LoadComms(const SGPath& path) {
|
||||||
// degrees, elev is in meters
|
// degrees, elev is in meters
|
||||||
// If no atc_type is specified, it returns true if any non-invalid type is found
|
// If no atc_type is specified, it returns true if any non-invalid type is found
|
||||||
// If atc_type is specifed, returns true only if the specified type is found
|
// If atc_type is specifed, returns true only if the specified type is found
|
||||||
bool FGCommList::FindByFreq( double lon, double lat, double elev, double freq,
|
bool FGCommList::FindByFreq(const SGGeod& aPos, double freq,
|
||||||
ATCData* ad, atc_type tp )
|
ATCData* ad, atc_type tp )
|
||||||
{
|
{
|
||||||
// HACK - if freq > 1000 assume it's in KHz, otherwise assume MHz.
|
comm_list_type stations;
|
||||||
// A bit ugly but it works for now!!!!
|
stations = commlist_freq[kHz10(freq)];
|
||||||
comm_list_type stations;
|
comm_list_iterator current = stations.begin();
|
||||||
if(freq > 1000.0) {
|
comm_list_iterator last = stations.end();
|
||||||
stations = commlist_freq[(int)freq];
|
|
||||||
} else {
|
// double az1, az2, s;
|
||||||
stations = commlist_freq[(int)(freq*100.0 + 0.5)];
|
SGVec3d aircraft = SGVec3d::fromGeod(aPos);
|
||||||
}
|
const double orig_max_d = 1e100;
|
||||||
comm_list_iterator current = stations.begin();
|
double max_d = orig_max_d;
|
||||||
comm_list_iterator last = stations.end();
|
double d;
|
||||||
|
// TODO - at the moment this loop returns the first match found in range
|
||||||
// double az1, az2, s;
|
// We want to return the closest match in the event of a frequency conflict
|
||||||
SGVec3d aircraft = SGVec3d::fromGeod(SGGeod::fromDegM(lon, lat, elev));
|
for ( ; current != last ; ++current ) {
|
||||||
const double orig_max_d = 1e100;
|
d = distSqr(aircraft, current->cart);
|
||||||
double max_d = orig_max_d;
|
|
||||||
// TODO - at the moment this loop returns the first match found in range
|
//cout << " dist = " << sqrt(d)
|
||||||
// We want to return the closest match in the event of a frequency conflict
|
// << " range = " << current->range * SG_NM_TO_METER << endl;
|
||||||
for ( ; current != last ; ++current ) {
|
|
||||||
//cout << "testing " << current->get_ident() << endl;
|
// TODO - match up to twice the published range so we can model
|
||||||
SGVec3d station(current->x, current->y, current->z);
|
// reduced signal strength
|
||||||
//cout << "aircraft = " << aircraft << endl;
|
// NOTE The below is squared since we match to distance3Dsquared (above) to avoid a sqrt.
|
||||||
//cout << "station = " << station << endl;
|
if ( d < (current->range * SG_NM_TO_METER
|
||||||
|
* current->range * SG_NM_TO_METER ) ) {
|
||||||
double d = distSqr(aircraft, station);
|
//cout << "matched = " << current->ident << endl;
|
||||||
|
if((tp == INVALID) || (tp == (*current).type)) {
|
||||||
//cout << " dist = " << sqrt(d)
|
if(d < max_d) {
|
||||||
// << " range = " << current->range * SG_NM_TO_METER << endl;
|
max_d = d;
|
||||||
|
*ad = *current;
|
||||||
// TODO - match up to twice the published range so we can model
|
}
|
||||||
// reduced signal strength
|
}
|
||||||
// NOTE The below is squared since we match to distance3Dsquared (above) to avoid a sqrt.
|
}
|
||||||
if ( d < (current->range * SG_NM_TO_METER
|
}
|
||||||
* current->range * SG_NM_TO_METER ) ) {
|
|
||||||
//cout << "matched = " << current->ident << endl;
|
if(max_d < orig_max_d) {
|
||||||
if((tp == INVALID) || (tp == (*current).type)) {
|
return true;
|
||||||
if(d < max_d) {
|
} else {
|
||||||
max_d = d;
|
return false;
|
||||||
*ad = *current;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(max_d < orig_max_d) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FGCommList::FindByPos(double lon, double lat, double elev, double range, comm_list_type* stations, atc_type tp)
|
int FGCommList::FindByPos(const SGGeod& aPos, double range, comm_list_type* stations, atc_type tp)
|
||||||
{
|
{
|
||||||
// number of relevant stations found within range
|
// number of relevant stations found within range
|
||||||
int found = 0;
|
int found = 0;
|
||||||
stations->erase(stations->begin(), stations->end());
|
stations->erase(stations->begin(), stations->end());
|
||||||
|
|
||||||
// get bucket number for plane position
|
// get bucket number for plane position
|
||||||
SGBucket buck(lon, lat);
|
SGBucket buck(aPos);
|
||||||
|
|
||||||
// get neigboring buckets
|
// get neigboring buckets
|
||||||
int bx = (int)( range*SG_NM_TO_METER / buck.get_width_m() / 2);
|
int bx = (int)( range*SG_NM_TO_METER / buck.get_width_m() / 2);
|
||||||
int by = (int)( range*SG_NM_TO_METER / buck.get_height_m() / 2 );
|
int by = (int)( range*SG_NM_TO_METER / buck.get_height_m() / 2 );
|
||||||
|
|
||||||
// loop over bucket range
|
// loop over bucket range
|
||||||
for ( int i=-bx; i<=bx; i++) {
|
for ( int i=-bx; i<=bx; i++) {
|
||||||
for ( int j=-by; j<=by; j++) {
|
for ( int j=-by; j<=by; j++) {
|
||||||
buck = sgBucketOffset(lon, lat, i, j);
|
buck = sgBucketOffset(aPos.getLongitudeDeg(), aPos.getLatitudeDeg(), i, j);
|
||||||
long int bucket = buck.gen_index();
|
long int bucket = buck.gen_index();
|
||||||
comm_list_type Fstations = commlist_bck[bucket];
|
comm_list_type Fstations = commlist_bck[bucket];
|
||||||
comm_list_iterator current = Fstations.begin();
|
comm_list_iterator current = Fstations.begin();
|
||||||
comm_list_iterator last = Fstations.end();
|
comm_list_iterator last = Fstations.end();
|
||||||
|
|
||||||
// double az1, az2, s;
|
|
||||||
SGVec3d aircraft = SGVec3d::fromGeod(SGGeod::fromDegM(lon, lat, elev));
|
// double az1, az2, s;
|
||||||
for(; current != last; ++current) {
|
SGVec3d aircraft = SGVec3d::fromGeod(aPos);
|
||||||
if((current->type == tp) || (tp == INVALID)) {
|
double d;
|
||||||
SGVec3d station(current->x, current->y, current->z);
|
for(; current != last; ++current) {
|
||||||
double d = distSqr(aircraft, station);
|
if((current->type == tp) || (tp == INVALID)) {
|
||||||
// NOTE The below is squared since we match to distance3Dsquared (above) to avoid a sqrt.
|
d = distSqr(aircraft, current->cart);
|
||||||
if ( d < (current->range * SG_NM_TO_METER
|
// NOTE The below is squared since we match to distance3Dsquared (above) to avoid a sqrt.
|
||||||
* current->range * SG_NM_TO_METER ) ) {
|
if ( d < (current->range * SG_NM_TO_METER
|
||||||
stations->push_back(*current);
|
* current->range * SG_NM_TO_METER ) ) {
|
||||||
++found;
|
stations->push_back(*current);
|
||||||
}
|
++found;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return found;
|
}
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -213,41 +207,39 @@ int FGCommList::FindByPos(double lon, double lat, double elev, double range, com
|
||||||
// Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if
|
// Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if
|
||||||
// say 300 miles is specifed 10, then 100, then 1000 will be searched, breaking at first result
|
// say 300 miles is specifed 10, then 100, then 1000 will be searched, breaking at first result
|
||||||
// and giving up after 1000.
|
// and giving up after 1000.
|
||||||
double FGCommList::FindClosest( double lon, double lat, double elev, ATCData& ad, atc_type tp, double max_range) {
|
double FGCommList::FindClosest(const SGGeod& aPos, ATCData& ad, atc_type tp, double max_range) {
|
||||||
int num_stations = 0;
|
int num_stations = 0;
|
||||||
int range = 10;
|
int range = 10;
|
||||||
comm_list_type stations;
|
comm_list_type stations;
|
||||||
comm_list_iterator itr;
|
comm_list_iterator itr;
|
||||||
double distance = -9999.0;
|
double distance = -9999.0;
|
||||||
|
|
||||||
while(num_stations == 0) {
|
while(num_stations == 0) {
|
||||||
num_stations = FindByPos(lon, lat, elev, range, &stations, tp);
|
num_stations = FindByPos(aPos, range, &stations, tp);
|
||||||
if(num_stations) {
|
if(num_stations) {
|
||||||
double closest = max_range * SG_NM_TO_METER;
|
double closest = max_range * SG_NM_TO_METER;
|
||||||
double tmp;
|
double tmp;
|
||||||
for(itr = stations.begin(); itr != stations.end(); ++itr) {
|
for(itr = stations.begin(); itr != stations.end(); ++itr) {
|
||||||
ATCData ad2 = *itr;
|
ATCData ad2 = *itr;
|
||||||
SGGeod p1 = SGGeod::fromDegM(ad2.lon, ad2.lat, ad2.elev);
|
const FGAirport *a = fgFindAirportID(ad2.ident);
|
||||||
const FGAirport *a = fgFindAirportID(ad2.ident);
|
if (a) {
|
||||||
if (a) {
|
tmp = dclGetHorizontalSeparation(ad2.geod, aPos);
|
||||||
SGGeod p2 = SGGeod::fromDegM(lon, lat, elev);
|
if(tmp <= closest) {
|
||||||
tmp = dclGetHorizontalSeparation(p1, p2);
|
closest = tmp;
|
||||||
if(tmp <= closest) {
|
distance = tmp;
|
||||||
closest = tmp;
|
ad = *itr;
|
||||||
distance = tmp;
|
}
|
||||||
ad = *itr;
|
}
|
||||||
}
|
}
|
||||||
}
|
//cout << "Closest station is " << ad.ident << " at a range of " << distance << " meters\n";
|
||||||
}
|
return(distance);
|
||||||
//cout << "Closest station is " << ad.ident << " at a range of " << distance << " meters\n";
|
}
|
||||||
return(distance);
|
if(range > max_range) {
|
||||||
}
|
break;
|
||||||
if(range > max_range) {
|
}
|
||||||
break;
|
range *= 10;
|
||||||
}
|
}
|
||||||
range *= 10;
|
return(-9999.0);
|
||||||
}
|
|
||||||
return(-9999.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -257,72 +249,53 @@ double FGCommList::FindClosest( double lon, double lat, double elev, ATCData& ad
|
||||||
bool FGCommList::FindByCode( const string& ICAO, ATCData& ad, atc_type tp ) {
|
bool FGCommList::FindByCode( const string& ICAO, ATCData& ad, atc_type tp ) {
|
||||||
const FGAirport *a = fgFindAirportID( ICAO);
|
const FGAirport *a = fgFindAirportID( ICAO);
|
||||||
if ( a) {
|
if ( a) {
|
||||||
comm_list_type stations;
|
comm_list_type stations;
|
||||||
int found = FindByPos(a->getLongitude(), a->getLatitude(), a->getElevation(), 10.0, &stations, tp);
|
int found = FindByPos(a->geod(), 10.0, &stations, tp);
|
||||||
if(found) {
|
if(found) {
|
||||||
comm_list_iterator itr = stations.begin();
|
comm_list_iterator itr = stations.begin();
|
||||||
while(itr != stations.end()) {
|
while(itr != stations.end()) {
|
||||||
if(((*itr).ident == ICAO) && ((*itr).type == tp)) {
|
if(((*itr).ident == ICAO) && ((*itr).type == tp)) {
|
||||||
ad = *itr;
|
ad = *itr;
|
||||||
return true;
|
//cout << "FindByCode returns " << ICAO
|
||||||
}
|
// << " type: " << tp
|
||||||
++itr;
|
// << " freq: " << itr->freq
|
||||||
}
|
// << endl;
|
||||||
}
|
return true;
|
||||||
} else {
|
}
|
||||||
return false;
|
++itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO - this function should move somewhere else eventually!
|
// TODO - this function should move somewhere else eventually!
|
||||||
// Return an appropriate call-sign for an ATIS transmission.
|
// Return an appropriate sequence number for an ATIS transmission.
|
||||||
int FGCommList::GetCallSign( const string& apt_id, int hours, int mins )
|
// Return sequence number + 2600 if sequence is unchanged since
|
||||||
|
// last time.
|
||||||
|
int FGCommList::GetAtisSequence( const string& apt_id,
|
||||||
|
const double tstamp, const int interval, const int special)
|
||||||
{
|
{
|
||||||
atis_transmission_type tran;
|
atis_transmission_type tran;
|
||||||
|
|
||||||
if(atislog.find(apt_id) == atislog.end()) {
|
if(atislog.find(apt_id) == atislog.end()) { // New station
|
||||||
// This station has not transmitted yet - return a random identifier
|
tran.tstamp = tstamp - interval;
|
||||||
// and add the transmission to the log
|
// Random number between 0 and 25 inclusive, i.e. 26 equiprobable outcomes:
|
||||||
tran.hours = hours;
|
tran.sequence = int(sg_random() * LTRS);
|
||||||
tran.mins = mins;
|
atislog[apt_id] = tran;
|
||||||
sg_srandom_time();
|
//cout << "New ATIS station: " << apt_id << " seq-1: "
|
||||||
tran.callsign = int(sg_random() * 25) + 1; // This *should* give a random int between 1 and 26
|
// << tran.sequence << endl;
|
||||||
//atislog[apt_id].push_back(tran);
|
}
|
||||||
atislog[apt_id] = tran;
|
|
||||||
} else {
|
// calculate the appropriate identifier and update the log
|
||||||
// This station has transmitted - calculate the appropriate identifier
|
tran = atislog[apt_id];
|
||||||
// and add the transmission to the log if it has changed
|
|
||||||
tran = atislog[apt_id];
|
int delta = int((tstamp - tran.tstamp) / interval);
|
||||||
// This next bit assumes that no-one comes back to the same ATIS station
|
tran.tstamp += delta * interval;
|
||||||
// after running FlightGear for more than 24 hours !!
|
if (special && !delta) delta++; // a "special" ATIS update is required
|
||||||
if((tran.hours == hours) && (tran.mins == mins)) {
|
tran.sequence = (tran.sequence + delta) % LTRS;
|
||||||
return(tran.callsign);
|
atislog[apt_id] = tran;
|
||||||
} else {
|
//if (delta) cout << "New ATIS sequence: " << tran.sequence
|
||||||
if(tran.hours == hours) {
|
// << " Delta: " << delta << endl;
|
||||||
// The minutes must have changed
|
return(tran.sequence + (delta ? 0 : LTRS*1000));
|
||||||
tran.mins = mins;
|
|
||||||
tran.callsign++;
|
|
||||||
} else {
|
|
||||||
if(hours < tran.hours) {
|
|
||||||
hours += 24;
|
|
||||||
}
|
|
||||||
tran.callsign += (hours - tran.hours);
|
|
||||||
if(mins != 0) {
|
|
||||||
// Assume transmissions were made on every hour
|
|
||||||
tran.callsign++;
|
|
||||||
}
|
|
||||||
tran.hours = hours;
|
|
||||||
tran.mins = mins;
|
|
||||||
}
|
|
||||||
// Wrap if we've exceeded Zulu
|
|
||||||
if(tran.callsign > 26) {
|
|
||||||
tran.callsign -= 26;
|
|
||||||
}
|
|
||||||
// And write the new transmission to the log
|
|
||||||
atislog[apt_id] = tran;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(tran.callsign);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
|
|
||||||
|
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
#include <simgear/misc/sg_path.hxx>
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
@ -44,24 +43,21 @@
|
||||||
#include "ATC.hxx"
|
#include "ATC.hxx"
|
||||||
#include "atis.hxx"
|
#include "atis.hxx"
|
||||||
|
|
||||||
using std::list;
|
class SGPath;
|
||||||
using std::map;
|
|
||||||
using std::vector;
|
|
||||||
using std::string;
|
|
||||||
|
|
||||||
// A list of ATC stations
|
// A list of ATC stations
|
||||||
typedef list < ATCData > comm_list_type;
|
typedef std::list < ATCData > comm_list_type;
|
||||||
typedef comm_list_type::iterator comm_list_iterator;
|
typedef comm_list_type::iterator comm_list_iterator;
|
||||||
typedef comm_list_type::const_iterator comm_list_const_iterator;
|
typedef comm_list_type::const_iterator comm_list_const_iterator;
|
||||||
|
|
||||||
// A map of ATC station lists
|
// A map of ATC station lists
|
||||||
typedef map < int, comm_list_type > comm_map_type;
|
typedef std::map < int, comm_list_type > comm_map_type;
|
||||||
typedef comm_map_type::iterator comm_map_iterator;
|
typedef comm_map_type::iterator comm_map_iterator;
|
||||||
typedef comm_map_type::const_iterator comm_map_const_iterator;
|
typedef comm_map_type::const_iterator comm_map_const_iterator;
|
||||||
|
|
||||||
|
|
||||||
class FGCommList {
|
class FGCommList {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FGCommList();
|
FGCommList();
|
||||||
|
@ -71,60 +67,59 @@ public:
|
||||||
bool init( const SGPath& path );
|
bool init( const SGPath& path );
|
||||||
|
|
||||||
// query the database for the specified frequency, lon and lat are
|
// query the database for the specified frequency, lon and lat are
|
||||||
// in degrees, elev is in meters.
|
// If no atc_type is specified, it returns true if any non-invalid type is found.
|
||||||
// If no atc_type is specified, it returns true if any non-invalid type is found.
|
// If atc_type is specifed, returns true only if the specified type is found.
|
||||||
// If atc_type is specifed, returns true only if the specified type is found.
|
// Returns the station closest to the supplied position.
|
||||||
// Returns the station closest to the supplied position.
|
// The data found is written into the passed-in ATCData structure.
|
||||||
// The data found is written into the passed-in ATCData structure.
|
bool FindByFreq(const SGGeod& aPos, double freq, ATCData* ad, atc_type tp = INVALID );
|
||||||
bool FindByFreq( double lon, double lat, double elev, double freq, ATCData* ad, atc_type tp = INVALID );
|
|
||||||
|
|
||||||
// query the database by location, lon and lat are in degrees, elev is in meters, range is in nautical miles.
|
// query the database by location, lon and lat are in degrees, elev is in meters, range is in nautical miles.
|
||||||
// Returns the number of stations of the specified atc_type tp that are in range of the position defined by
|
// Returns the number of stations of the specified atc_type tp that are in range of the position defined by
|
||||||
// lon, lat and elev, and pushes them into stations.
|
// lon, lat and elev, and pushes them into stations.
|
||||||
// If no atc_type is specifed, returns the number of all stations in range, and pushes them into stations
|
// If no atc_type is specifed, returns the number of all stations in range, and pushes them into stations
|
||||||
// ** stations is erased before use **
|
// ** stations is erased before use **
|
||||||
int FindByPos( double lon, double lat, double elev, double range, comm_list_type* stations, atc_type tp = INVALID );
|
int FindByPos(const SGGeod& aPos, double range, comm_list_type* stations, atc_type tp = INVALID );
|
||||||
|
|
||||||
// Returns the distance in meters to the closest station of a given type,
|
// Returns the distance in meters to the closest station of a given type,
|
||||||
// with the details written into ATCData& ad. If no type is specifed simply
|
// with the details written into ATCData& ad. If no type is specifed simply
|
||||||
// returns the distance to the closest station of any type.
|
// returns the distance to the closest station of any type.
|
||||||
// Returns -9999 if no stations found within max_range in nautical miles (default 100 miles).
|
// Returns -9999 if no stations found within max_range in nautical miles (default 100 miles).
|
||||||
// Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if
|
// Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if
|
||||||
// say 300 miles is specifed 10, then 100, then 1000 will be searched, breaking at first result
|
// say 300 miles is specifed 10, then 100, then 1000 will be searched, breaking at first result
|
||||||
// and giving up after 1000.
|
// and giving up after 1000.
|
||||||
// !!!Be warned that searching anything over 100 miles will pause the sim unacceptably!!!
|
// !!!Be warned that searching anything over 100 miles will pause the sim unacceptably!!!
|
||||||
// (The ability to search longer ranges should be used during init only).
|
// (The ability to search longer ranges should be used during init only).
|
||||||
double FindClosest( double lon, double lat, double elev, ATCData& ad, atc_type tp = INVALID, double max_range = 100.0 );
|
double FindClosest(const SGGeod& aPos, ATCData& ad, atc_type tp = INVALID, double max_range = 100.0 );
|
||||||
|
|
||||||
// Find by Airport code.
|
// Find by Airport code.
|
||||||
bool FindByCode( const string& ICAO, ATCData& ad, atc_type tp = INVALID );
|
bool FindByCode( const std::string& ICAO, ATCData& ad, atc_type tp = INVALID );
|
||||||
|
|
||||||
// Return the callsign for an ATIS transmission given transmission time and airport id
|
// Return the sequence letter for an ATIS transmission given transmission time and airport id
|
||||||
// This maybe should get moved somewhere else!!
|
// This maybe should get moved somewhere else!!
|
||||||
int GetCallSign( const string& apt_id, int hours, int mins );
|
int GetAtisSequence( const std::string& apt_id, const double tstamp,
|
||||||
|
const int interval, const int flush=0);
|
||||||
|
|
||||||
|
// Comm stations mapped by frequency
|
||||||
|
comm_map_type commlist_freq;
|
||||||
|
|
||||||
|
// Comm stations mapped by bucket
|
||||||
|
comm_map_type commlist_bck;
|
||||||
|
|
||||||
|
// Load comms from a specified path (which must include the filename)
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Comm stations mapped by frequency
|
|
||||||
comm_map_type commlist_freq;
|
|
||||||
|
|
||||||
// Comm stations mapped by bucket
|
|
||||||
comm_map_type commlist_bck;
|
|
||||||
|
|
||||||
// Load comms from a specified path (which must include the filename)
|
bool LoadComms(const SGPath& path);
|
||||||
bool LoadComms(const SGPath& path);
|
|
||||||
|
|
||||||
//----------- This stuff is left over from atislist.[ch]xx and maybe should move somewhere else
|
//----------- This stuff is left over from atislist.[ch]xx and maybe should move somewhere else
|
||||||
// Add structure and map for storing a log of atis transmissions
|
// Add structure and map for storing a log of atis transmissions
|
||||||
// made in this session of FlightGear. This allows the callsign
|
// made in this session of FlightGear. This allows the callsign
|
||||||
// to be allocated correctly wrt time.
|
// to be allocated correctly wrt time.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int hours;
|
double tstamp;
|
||||||
int mins;
|
int sequence;
|
||||||
int callsign;
|
} atis_transmission_type;
|
||||||
} atis_transmission_type;
|
|
||||||
|
|
||||||
typedef map < string, atis_transmission_type > atis_log_type;
|
typedef std::map < std::string, atis_transmission_type > atis_log_type;
|
||||||
typedef atis_log_type::iterator atis_log_iterator;
|
typedef atis_log_type::iterator atis_log_iterator;
|
||||||
typedef atis_log_type::const_iterator atis_log_const_iterator;
|
typedef atis_log_type::const_iterator atis_log_const_iterator;
|
||||||
|
|
||||||
|
@ -136,5 +131,6 @@ private:
|
||||||
|
|
||||||
extern FGCommList *current_commlist;
|
extern FGCommList *current_commlist;
|
||||||
|
|
||||||
|
|
||||||
#endif // _FG_COMMLIST_HXX
|
#endif // _FG_COMMLIST_HXX
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1409,8 +1409,9 @@ void FGTower::CheckDepartureList(double dt) {
|
||||||
//cout << "Dep list, checking " << t->plane.callsign;
|
//cout << "Dep list, checking " << t->plane.callsign;
|
||||||
|
|
||||||
double distout; // meters
|
double distout; // meters
|
||||||
if(t->isUser) distout = dclGetHorizontalSeparation(SGGeod::fromDegM(lon, lat, elev), SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue()));
|
if(t->isUser) distout = dclGetHorizontalSeparation(_geod,
|
||||||
else distout = dclGetHorizontalSeparation(SGGeod::fromDegM(lon, lat, elev), t->planePtr->getPos());
|
SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue()));
|
||||||
|
else distout = dclGetHorizontalSeparation(_geod, t->planePtr->getPos());
|
||||||
//cout << " distout = " << distout << '\n';
|
//cout << " distout = " << distout << '\n';
|
||||||
if(t->isUser && !(t->clearedToTakeOff)) { // HACK - we use clearedToTakeOff to check if ATC already contacted with plane (and cleared take-off) or not
|
if(t->isUser && !(t->clearedToTakeOff)) { // HACK - we use clearedToTakeOff to check if ATC already contacted with plane (and cleared take-off) or not
|
||||||
if(!OnAnyRunway(SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), false)) {
|
if(!OnAnyRunway(SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), false)) {
|
||||||
|
@ -1563,7 +1564,7 @@ bool FGTower::OnActiveRunway(const SGGeod& pt) {
|
||||||
// Only call this at startup - reading the runways database is expensive and needs to be fixed!
|
// Only call this at startup - reading the runways database is expensive and needs to be fixed!
|
||||||
bool FGTower::OnAnyRunway(const SGGeod& pt, bool onGround) {
|
bool FGTower::OnAnyRunway(const SGGeod& pt, bool onGround) {
|
||||||
ATCData ad;
|
ATCData ad;
|
||||||
double dist = current_commlist->FindClosest(lon, lat, elev, ad, TOWER, 7.0);
|
double dist = current_commlist->FindClosest(_geod, ad, TOWER, 7.0);
|
||||||
if(dist < 0.0) {
|
if(dist < 0.0) {
|
||||||
return(false);
|
return(false);
|
||||||
}
|
}
|
||||||
|
@ -2540,7 +2541,7 @@ string FGTower::GenText(const string& m, int c) {
|
||||||
else if ( strcmp ( tag, "@MI" ) == 0 ) {
|
else if ( strcmp ( tag, "@MI" ) == 0 ) {
|
||||||
char buf[10];
|
char buf[10];
|
||||||
//sprintf( buf, "%3.1f", tpars.miles );
|
//sprintf( buf, "%3.1f", tpars.miles );
|
||||||
int dist_miles = (int)dclGetHorizontalSeparation(SGGeod::fromDegM(lon, lat, elev), SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())) / 1600;
|
int dist_miles = (int)dclGetHorizontalSeparation(_geod, SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())) / 1600;
|
||||||
sprintf(buf, "%i", dist_miles);
|
sprintf(buf, "%i", dist_miles);
|
||||||
strcat( &dum[0], &buf[0] );
|
strcat( &dum[0], &buf[0] );
|
||||||
}
|
}
|
||||||
|
@ -2560,7 +2561,7 @@ string FGTower::GenText(const string& m, int c) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(strcmp(tag, "@CD") == 0) { // @CD = compass direction
|
else if(strcmp(tag, "@CD") == 0) { // @CD = compass direction
|
||||||
double h = GetHeadingFromTo(SGGeod::fromDegM(lon, lat, elev), SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue()));
|
double h = GetHeadingFromTo(_geod, SGGeod::fromDegM(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue()));
|
||||||
while(h < 0.0) h += 360.0;
|
while(h < 0.0) h += 360.0;
|
||||||
while(h > 360.0) h -= 360.0;
|
while(h > 360.0) h -= 360.0;
|
||||||
if(h < 22.5 || h > 337.5) {
|
if(h < 22.5 || h > 337.5) {
|
||||||
|
@ -2625,9 +2626,13 @@ string FGTower::GetWeather() {
|
||||||
}
|
}
|
||||||
|
|
||||||
string FGTower::GetATISID() {
|
string FGTower::GetATISID() {
|
||||||
int hours = fgGetInt("/sim/time/utc/hour");
|
double tstamp = atof(fgGetString("sim/time/elapsed-sec"));
|
||||||
int phonetic_id = current_commlist->GetCallSign(ident, hours, 0);
|
const int minute(60); // in SI units
|
||||||
return GetPhoneticIdent(phonetic_id);
|
int interval = ATIS ? 60*minute : 2*minute; // AWOS updated frequently
|
||||||
|
int sequence = current_commlist->GetAtisSequence(ident,
|
||||||
|
tstamp, interval);
|
||||||
|
|
||||||
|
return GetPhoneticLetter(sequence); // the sequence letter
|
||||||
}
|
}
|
||||||
|
|
||||||
ostream& operator << (ostream& os, tower_traffic_type ttt) {
|
ostream& operator << (ostream& os, tower_traffic_type ttt) {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
#include "ATC.hxx"
|
#include "ATC.hxx"
|
||||||
#include "ATCProjection.hxx"
|
#include "ATCProjection.hxx"
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
# include <config.h>
|
# include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "apt_loader.hxx"
|
||||||
|
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
|
|
||||||
#include <stdlib.h> // atof(), atoi()
|
#include <stdlib.h> // atof(), atoi()
|
||||||
|
@ -43,8 +45,11 @@
|
||||||
#include "simple.hxx"
|
#include "simple.hxx"
|
||||||
#include "runways.hxx"
|
#include "runways.hxx"
|
||||||
#include "pavement.hxx"
|
#include "pavement.hxx"
|
||||||
|
#include <ATCDCL/commlist.hxx>
|
||||||
|
|
||||||
#include "apt_loader.hxx"
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
static FGPositioned::Type fptypeFromRobinType(int aType)
|
static FGPositioned::Type fptypeFromRobinType(int aType)
|
||||||
{
|
{
|
||||||
|
@ -61,9 +66,19 @@ static FGPositioned::Type fptypeFromRobinType(int aType)
|
||||||
class APTLoader
|
class APTLoader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void parseAPT(const string &aptdb_file)
|
|
||||||
|
APTLoader()
|
||||||
|
: last_apt_id(""),
|
||||||
|
last_apt_name(""),
|
||||||
|
last_apt_elev(0.0),
|
||||||
|
last_apt_info(""),
|
||||||
|
last_apt_type("")
|
||||||
|
{}
|
||||||
|
|
||||||
|
void parseAPT(const string &aptdb_file, FGCommList *comm_list)
|
||||||
{
|
{
|
||||||
sg_gzifstream in( aptdb_file );
|
sg_gzifstream in( aptdb_file );
|
||||||
|
|
||||||
if ( !in.is_open() ) {
|
if ( !in.is_open() ) {
|
||||||
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << aptdb_file );
|
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << aptdb_file );
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
@ -139,8 +154,10 @@ public:
|
||||||
// custom startup locations (ignore)
|
// custom startup locations (ignore)
|
||||||
} else if ( line_id == 0 ) {
|
} else if ( line_id == 0 ) {
|
||||||
// ??
|
// ??
|
||||||
} else if ( line_id >= 50 && line_id <= 56 ) {
|
} else if ( line_id == 50 ) {
|
||||||
// frequency entries (ignore)
|
parseATISLine(comm_list, simgear::strutils::split(line));
|
||||||
|
} else if ( line_id >= 51 && line_id <= 56 ) {
|
||||||
|
// other frequency entries (ignore)
|
||||||
} else if ( line_id == 110 ) {
|
} else if ( line_id == 110 ) {
|
||||||
pavement = true;
|
pavement = true;
|
||||||
parsePavementLine850(simgear::strutils::split(line, 0, 4));
|
parsePavementLine850(simgear::strutils::split(line, 0, 4));
|
||||||
|
@ -166,6 +183,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
vector<string> token;
|
||||||
double rwy_lat_accum;
|
double rwy_lat_accum;
|
||||||
double rwy_lon_accum;
|
double rwy_lon_accum;
|
||||||
double last_rwy_heading;
|
double last_rwy_heading;
|
||||||
|
@ -175,7 +193,8 @@ private:
|
||||||
string last_apt_name;
|
string last_apt_name;
|
||||||
double last_apt_elev;
|
double last_apt_elev;
|
||||||
SGGeod tower;
|
SGGeod tower;
|
||||||
int last_apt_type;
|
string last_apt_info;
|
||||||
|
string last_apt_type;
|
||||||
string pavement_ident;
|
string pavement_ident;
|
||||||
bool pavement;
|
bool pavement;
|
||||||
|
|
||||||
|
@ -209,7 +228,7 @@ private:
|
||||||
|
|
||||||
SGGeod pos(SGGeod::fromDegFt(lon, lat, last_apt_elev));
|
SGGeod pos(SGGeod::fromDegFt(lon, lat, last_apt_elev));
|
||||||
FGAirport* apt = new FGAirport(last_apt_id, pos, tower, last_apt_name, false,
|
FGAirport* apt = new FGAirport(last_apt_id, pos, tower, last_apt_name, false,
|
||||||
fptypeFromRobinType(last_apt_type));
|
fptypeFromRobinType(atoi(last_apt_type.c_str())));
|
||||||
|
|
||||||
apt->setRunwaysAndTaxiways(runways, taxiways, pavements);
|
apt->setRunwaysAndTaxiways(runways, taxiways, pavements);
|
||||||
}
|
}
|
||||||
|
@ -232,7 +251,7 @@ private:
|
||||||
last_apt_name += " ";
|
last_apt_name += " ";
|
||||||
}
|
}
|
||||||
last_apt_name += token[token.size() - 1];
|
last_apt_name += token[token.size() - 1];
|
||||||
last_apt_type = atoi( token[0].c_str() );
|
last_apt_type = token[0];
|
||||||
|
|
||||||
// clear runway list for start of next airport
|
// clear runway list for start of next airport
|
||||||
rwy_lon_accum = 0.0;
|
rwy_lon_accum = 0.0;
|
||||||
|
@ -442,15 +461,62 @@ private:
|
||||||
pvt->addNode(pos, num == 113);
|
pvt->addNode(pos, num == 113);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void parseATISLine(FGCommList *comm_list, const vector<string>& token)
|
||||||
|
{
|
||||||
|
if ( rwy_count <= 0 ) {
|
||||||
|
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||||
|
"No runways; skipping AWOS for " + last_apt_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This assumes/requires that any code-50 line (ATIS or AWOS)
|
||||||
|
// applies to the preceding code-1 line (airport ID and name)
|
||||||
|
// and that a full set of code-10 lines (runway descriptors)
|
||||||
|
// has come between the code-1 and code-50 lines.
|
||||||
|
// typical code-50 lines:
|
||||||
|
// 50 11770 ATIS
|
||||||
|
// 50 11770 AWOS 3
|
||||||
|
// This code parallels code found in "operator>>" in ATC.hxx;
|
||||||
|
// FIXME: unify the code.
|
||||||
|
ATCData a;
|
||||||
|
a.geod = SGGeod::fromDegFt(rwy_lon_accum / (double)rwy_count,
|
||||||
|
rwy_lat_accum / (double)rwy_count, last_apt_elev);
|
||||||
|
a.range = 50; // give all ATISs small range
|
||||||
|
a.ident = last_apt_id;
|
||||||
|
a.name = last_apt_name;
|
||||||
|
// short int representing tens of kHz:
|
||||||
|
a.freq = atoi(token[1].c_str());
|
||||||
|
if (token[2] == "ATIS") a.type = ATIS;
|
||||||
|
else a.type = AWOS; // ASOS same as AWOS
|
||||||
|
|
||||||
|
// generate cartesian coordinates
|
||||||
|
a.cart = SGVec3d::fromGeod(a.geod);
|
||||||
|
comm_list->commlist_freq[a.freq].push_back(a);
|
||||||
|
|
||||||
|
SGBucket bucket(a.geod);
|
||||||
|
int bucknum = bucket.gen_index();
|
||||||
|
comm_list->commlist_bck[bucknum].push_back(a);
|
||||||
|
#if 0
|
||||||
|
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||||
|
"Loaded ATIS/AWOS for airport: " << a.ident
|
||||||
|
<< " lat: " << a.geod.getLatitudeDeg()
|
||||||
|
<< " lon: " << a.geod.getLongitudeDeg()
|
||||||
|
<< " freq: " << a.freq
|
||||||
|
<< " type: " << a.type );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load the airport data base from the specified aptdb file. The
|
// Load the airport data base from the specified aptdb file. The
|
||||||
// metar file is used to mark the airports as having metar available
|
// metar file is used to mark the airports as having metar available
|
||||||
// or not.
|
// or not.
|
||||||
bool fgAirportDBLoad( const string &aptdb_file, const string &metar_file )
|
bool fgAirportDBLoad( const string &aptdb_file,
|
||||||
|
FGCommList *comm_list, const std::string &metar_file )
|
||||||
{
|
{
|
||||||
|
|
||||||
APTLoader ld;
|
APTLoader ld;
|
||||||
ld.parseAPT(aptdb_file);
|
|
||||||
|
ld.parseAPT(aptdb_file, comm_list);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Load the metar.dat file and update apt db with stations that
|
// Load the metar.dat file and update apt db with stations that
|
||||||
|
|
|
@ -29,13 +29,14 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "simple.hxx"
|
// forward decls
|
||||||
|
class FGCommList;
|
||||||
|
|
||||||
// Load the airport data base from the specified aptdb file. The
|
// Load the airport data base from the specified aptdb file. The
|
||||||
// metar file is used to mark the airports as having metar available
|
// metar file is used to mark the airports as having metar available
|
||||||
// or not.
|
// or not.
|
||||||
bool fgAirportDBLoad( const string &aptdb_file, const std::string &metar_file );
|
bool fgAirportDBLoad( const std::string &aptdb_file,
|
||||||
|
FGCommList *comm_list, const std::string &metar_file );
|
||||||
|
|
||||||
|
|
||||||
#endif // _FG_APT_LOADER_HXX
|
#endif // _FG_APT_LOADER_HXX
|
||||||
|
|
|
@ -708,7 +708,6 @@ FGEnvironment::_recalc_alt_pt ()
|
||||||
<< " and " << temperature_sea_level_degc
|
<< " and " << temperature_sea_level_degc
|
||||||
<< " :: " << this
|
<< " :: " << this
|
||||||
<< " # " << count);
|
<< " # " << count);
|
||||||
///////////////////////////////////raise(SIGUSR1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -127,6 +127,7 @@
|
||||||
#include "renderer.hxx"
|
#include "renderer.hxx"
|
||||||
#include "viewmgr.hxx"
|
#include "viewmgr.hxx"
|
||||||
#include "main.hxx"
|
#include "main.hxx"
|
||||||
|
#include "ATCDCL/commlist.hxx"
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
# include <CoreFoundation/CoreFoundation.h>
|
# include <CoreFoundation/CoreFoundation.h>
|
||||||
|
@ -968,7 +969,11 @@ fgInitNav ()
|
||||||
SGPath p_metar( globals->get_fg_root() );
|
SGPath p_metar( globals->get_fg_root() );
|
||||||
p_metar.append( "Airports/metar.dat" );
|
p_metar.append( "Airports/metar.dat" );
|
||||||
|
|
||||||
fgAirportDBLoad( aptdb.str(), p_metar.str() );
|
// Initialise the frequency search map BEFORE reading
|
||||||
|
// the airport database:
|
||||||
|
current_commlist = new FGCommList;
|
||||||
|
current_commlist->init( globals->get_fg_root() );
|
||||||
|
fgAirportDBLoad( aptdb.str(), current_commlist, p_metar.str() );
|
||||||
|
|
||||||
FGNavList *navlist = new FGNavList;
|
FGNavList *navlist = new FGNavList;
|
||||||
FGNavList *loclist = new FGNavList;
|
FGNavList *loclist = new FGNavList;
|
||||||
|
|
Loading…
Add table
Reference in a new issue