// And to avoid compiler warnings...
case APPROACH: break;
case ATIS: break;
+ case AWOS: break;
case ENROUTE: break;
case DEPARTURE: break;
case INVALID: break;
lon_node = fgGetNode("/position/longitude-deg", true);
lat_node = fgGetNode("/position/latitude-deg", 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
// Hack alert - Hardwired paths!!
//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!!
static int i = 0;
//cout << "In SearchByPos(...)" << endl;
// get bucket number for plane position
- lon = lon_node->getDoubleValue();
- lat = lat_node->getDoubleValue();
- elev = elev_node->getDoubleValue() * SG_FEET_TO_METER;
- SGBucket buck(lon, lat);
+ _userAircraftPos = SGGeod::fromDegFt(lon_node->getDoubleValue(),
+ lat_node->getDoubleValue(), elev_node->getDoubleValue());
+ SGBucket buck(_userAircraftPos);
// get neigboring buckets
int bx = (int)( range*SG_NM_TO_METER / buck.get_width_m() / 2);
//cout << "i loop\n";
for ( int j=-by; j<=by; j++) {
//cout << "j loop\n";
- buck = sgBucketOffset(lon, lat, i, j);
+ buck = sgBucketOffset(_userAircraftPos.getLongitudeDeg(), _userAircraftPos.getLatitudeDeg(), i, j);
long int bucket = buck.gen_index();
//cout << "bucket is " << bucket << endl;
if(facilities.find(bucket) != facilities.end()) {
comm_list_type towered;
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) {
double closest = 1000000;
string s = "";
for(twd_itr = towered.begin(); twd_itr != towered.end(); twd_itr++) {
// Only activate the closest airport not already activated each time.
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) {
closest = sep;
s = twd_itr->ident;
//cout << c << '\n';
string tmp = "";
tmp += c;
- if(isalpha(c)) s += GetPhoneticIdent(c);
+ if(isalpha(c)) s += GetPhoneticLetter(c);
else s += ConvertNumToSpokenDigits(tmp);
if(i > 1) s += '-';
}
typedef ai_callsigns_map_type::iterator ai_callsigns_map_iterator;
ai_callsigns_map_type ai_callsigns_used;
- // Position of the Users Aircraft
- double lon;
- double lat;
- double elev;
- // Pointers to current users position
- SGPropertyNode_ptr lon_node;
- SGPropertyNode_ptr lat_node;
- SGPropertyNode_ptr elev_node;
+ SGGeod _userAircraftPos;
+ // Pointers to current users position
+ SGPropertyNode_ptr lon_node;
+ SGPropertyNode_ptr lat_node;
+ SGPropertyNode_ptr elev_node;
public:
FGAIMgr();
if(1) {
// For now assume in range !!!
// 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.
}
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) {
ProcessCallback(callback_code);
}
// 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 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());
#ifdef ENABLE_AUDIO_SUPPORT
voice = (voiceOK && fgGetBool("/sim/sound/voice"));
if(voice) {
- int len;
- unsigned char* buf = vPtr->WriteMessage((char*)pending_transmission.c_str(), len, voice);
- if(voice) {
- SGSoundSample* simple = new SGSoundSample(buf, len, 8000);
- // TODO - at the moment the volume is always set off comm1
- // and can't be changed after the transmission has started.
- simple->set_volume(5.0 * fgGetDouble("/instrumentation/comm[0]/volume"));
- globals->get_soundmgr()->add(simple, refname);
- if(repeating) {
- globals->get_soundmgr()->play_looped(refname);
- } else {
- globals->get_soundmgr()->play_once(refname);
- }
+ string buf = vPtr->WriteMessage((char*)pending_transmission.c_str(), voice);
+ if(voice) {
+ SGSoundSample* simple =
+ new SGSoundSample((unsigned char*)buf.c_str(), buf.length(), 8000, AL_FORMAT_MONO8 );
+ // TODO - at the moment the volume can't be changed
+ // after the transmission has started.
+ simple->set_volume(volume);
+ globals->get_soundmgr()->add(simple, refname);
+ if(repeating) {
+ globals->get_soundmgr()->play_looped(refname);
+ } else {
+ globals->get_soundmgr()->play_once(refname);
}
- delete[] buf;
+ }
}
#endif // ENABLE_AUDIO_SUPPORT
if(!voice) {
// 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 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.
// Requires the sound manager refname if audio, else "".
# include <config.h>
#endif
+#include "ATC.hxx"
+
+#include <iostream>
+
#include <simgear/sound/soundmgr_openal.hxx>
#include <simgear/structure/exception.hxx>
#include <Main/globals.hxx>
#include <Main/fg_props.hxx>
-#include "ATC.hxx"
-FGATC::FGATC() {
- freqClear = true;
- receiving = false;
- respond = false;
- runResponseCounter = false;
- _runReleaseCounter = false;
- responseID = "";
- responseReqd = false;
- _type = INVALID;
- _display = false;
- _displaying = false;
-
+
+FGATC::FGATC() :
+ _voiceOK(false),
+ freqClear(true),
+ receiving(false),
+ respond(false),
+ responseID(""),
+ runResponseCounter(false),
+ _runReleaseCounter(false),
+ responseReqd(false),
+ _type(INVALID),
+ _display(false),
// Transmission timing stuff
- pending_transmission = "";
- _timeout = 0;
- _pending = false;
- _callback_code = 0;
- _transmit = false;
- _transmitting = false;
- _counter = 0.0;
- _max_count = 5.0;
-
- _voiceOK = false;
+ pending_transmission(""),
+ _timeout(0),
+ _pending(false),
+ _callback_code(0),
+ _transmit(false),
+ _transmitting(false),
+ _counter(0.0),
+ _max_count(5.0)
+{
}
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) {
if(runResponseCounter) {
//cout << responseCounter << '\t' << responseTime << '\n';
}
void FGATC::SetData(ATCData* d) {
- lon = d->lon;
- lat = d->lat;
- elev = d->elev;
- x = d->x;
- y = d->y;
- z = d->z;
+ _type = d->type;
+ _geod = d->geod;
+ _cart = d->cart;
range = d->range;
ident = d->ident;
name = d->name;
// 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 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)
fgSetString("/sim/messages/atis", msg.c_str());
else
#ifdef ENABLE_AUDIO_SUPPORT
_voice = (_voiceOK && fgGetBool("/sim/sound/voice"));
if(_voice) {
- int len;
- unsigned char* buf = _vPtr->WriteMessage((char*)msg.c_str(), len, _voice);
- if(_voice) {
- try {
- SGSoundSample *simple
- = new SGSoundSample(buf, len, 8000);
- // TODO - at the moment the volume is always set off comm1
- // and can't be changed after the transmission has started.
- simple->set_volume(5.0 * fgGetDouble("/instrumentation/comm[0]/volume"));
- globals->get_soundmgr()->add(simple, refname);
- if(repeating) {
- globals->get_soundmgr()->play_looped(refname);
- } else {
- globals->get_soundmgr()->play_once(refname);
- }
- } catch ( sg_io_exception &e ) {
- SG_LOG(SG_GENERAL, SG_ALERT, e.getFormattedMessage());
- }
+ string buf = _vPtr->WriteMessage((char*)msg.c_str(), _voice);
+ if(_voice) {
+ NoRender(refname);
+ try {
+// >>> Beware: must pass a (new) object to the (add) method,
+// >>> because the (remove) method is going to do a (delete)
+// >>> whether that's what you want or not.
+ SGSoundSample *simple =
+ new SGSoundSample((unsigned char*) buf.c_str(),
+ buf.length(), 8000, AL_FORMAT_MONO8);
+ // TODO - at the moment the volume can't be changed
+ // after the transmission has started.
+ simple->set_volume(volume);
+ globals->get_soundmgr()->add(simple, refname);
+ if(repeating) {
+ globals->get_soundmgr()->play_looped(refname);
+ } 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
if(!_voice) {
ostream& operator << (ostream& os, atc_type atc) {
switch(atc) {
- case(INVALID): return(os << "INVALID");
+ case(AWOS): return(os << "AWOS");
case(ATIS): return(os << "ATIS");
case(GROUND): return(os << "GROUND");
case(TOWER): return(os << "TOWER");
case(APPROACH): return(os << "APPROACH");
case(DEPARTURE): return(os << "DEPARTURE");
case(ENROUTE): return(os << "ENROUTE");
+ case(INVALID): return(os << "INVALID");
}
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;
+}
+
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/debug/logstream.hxx>
-#include <istream>
-#include <ostream>
-
+#include <iosfwd>
#include <string>
#include "ATCVoice.hxx"
-using std::ostream;
-using std::string;
-using std::ios;
-
+// Convert a frequency in MHz to tens of kHz
+// so we can use it e.g. as an index into commlist_freq
+//
+// 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 {
UNKNOWN,
GA_SINGLE,
// Possible types of ATC type that the radios may be tuned to.
// INVALID implies not tuned in to anything.
enum atc_type {
- INVALID,
+ AWOS,
ATIS,
GROUND,
TOWER,
APPROACH,
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
struct ATCData {
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.
- // In fact, we could probably ditch x, y and z and generate on the fly as needed.
- // 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;
+ SGGeod geod;
+ SGVec3d cart;
unsigned short int freq;
- //int range;
unsigned short int range;
std::string ident;
std::string name;
std::ostream& operator << (std::ostream& os, atc_type atc);
class FGATC {
-
+ friend class FGATCMgr;
public:
FGATC();
virtual ~FGATC();
+ virtual void Init()=0;
+
// Run the internal calculations
// Derived classes should call this method from their own Update methods if they
// wish to use the response timer functionality.
// Set the core ATC data
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 void set_freq(const int fq) {freq = fq;}
inline int get_range() const { return range; }
// 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 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.
// Requires the sound manager refname if audio, else "".
virtual void ProcessCallback(int code);
- double lon, lat, elev;
- double x, y, z;
+ SGGeod _geod;
+ SGVec3d _cart;
int freq;
+ std::map<std::string,int> active_on;
+
int range;
std::string ident; // Code of the airport its at.
std::string name; // Name transmitted in the broadcast.
- atc_type _type;
+
// Rendering related stuff
bool _voice; // Flag - true if we are using voice
bool _voiceOK; // Flag - true if at least one voice has loaded OK
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 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
// 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
- 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(...)
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 _releaseCounter;
-
+ atc_type _type;
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:
// Transmission timing stuff.
- bool _pending;
double _timeout;
+ bool _pending;
+
int _callback_code; // A callback code to be notified and processed by the derived classes
// A value of zero indicates no callback required
bool _transmit; // we are to transmit
double _max_count;
};
-inline 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;
- }
-
- 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;
-}
-
+std::istream& operator>> ( std::istream& fin, ATCData& a );
#endif // _FG_ATC_HXX
ATCMenuEntry::~ATCMenuEntry() {
}
-static void atcUppercase(string &s) {
+void atcUppercase(string &s) {
for(unsigned int i=0; i<s.size(); ++i) {
s[i] = toupper(s[i]);
}
// Find the ATC stations within a reasonable range
comm_list_type atc_stations;
comm_list_iterator atc_stat_itr;
-
- double lon = fgGetDouble("/position/longitude-deg");
- double lat = fgGetDouble("/position/latitude-deg");
- double elev = fgGetDouble("/position/altitude-ft");
- SGVec3d aircraft = SGVec3d::fromGeod(SGGeod::fromDegM(lon, lat, elev));
+
+ SGGeod geod(SGGeod::fromDegFt(fgGetDouble("/position/longitude-deg"),
+ fgGetDouble("/position/latitude-deg"), fgGetDouble("/position/altitude-ft")));
+ SGVec3d aircraft = SGVec3d::fromGeod(geod);
// 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) {
map<atcdata, bool> uniq;
// fill map (sorts by distance and removes duplicates)
comm_list_iterator itr = atc_stations.begin();
for (; itr != atc_stations.end(); ++itr) {
- SGVec3d station(itr->x, itr->y, itr->z);
- double distance = distSqr(aircraft, station);
+ double distance = distSqr(aircraft, itr->cart);
uniq[atcdata(itr->ident, itr->name, distance)] = true;
}
// create button per map entry (modified copy of <button-template>)
int n = 0; // Number of ATC frequencies at this airport
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) {
ostringstream ostr;
comm_list_iterator itr = stations.begin();
typedef map < string, atcmentry_vec_type > atcmentry_map_type;
typedef atcmentry_map_type::iterator atcmentry_map_iterator;
-//void ATCDialogInit();
+void atcUppercase(string &s);
+//void ATCDialogInit();
//void ATCDoDialog(atc_type type);
class FGATCDialog {
# include <config.h>
#endif
+#include "ATCVoice.hxx"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <fstream>
+#include <list>
+
#include <simgear/misc/sg_path.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/math/sg_random.h>
-#include <Main/globals.hxx>
-
-#include "ATCVoice.hxx"
+#include <simgear/sound/sample_openal.hxx>
-#include <stdlib.h>
+#include <Main/globals.hxx>
FGATCVoice::FGATCVoice() {
SoundData = 0;
// Load the two voice files - one containing the raw sound data (.wav) and one containing the word positions (.vce).
// Return true if successful.
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;
- ifstream fin;
+ std::ifstream fin;
SGPath path = globals->get_fg_root();
path.append( "ATC" );
string file = voice + ".wav";
- SoundData = new SGSoundSample();
- rawSoundData = (char *)SoundData->load_file(path.c_str(), file.c_str());
- rawDataSize = SoundData->get_size();
-
+ SGSoundSample SoundData;
+ rawSoundData = (char *)SoundData.load_file(path.c_str(), file.c_str());
+ 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();
string wordPath = "ATC/" + voice + ".vce";
path.append(wordPath);
wd.offset = wrdOffset;
wd.length = wrdLength;
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 << i << '\n';
}
typedef list < string > tokenList_type;
typedef tokenList_type::iterator tokenList_iterator;
-// Given a desired message, return a pointer to the data buffer and write the buffer length into len.
-unsigned char* FGATCVoice::WriteMessage(char* message, int& len, bool& dataOK) {
+// Given a desired message, return a string containing the
+// sound-sample data
+string FGATCVoice::WriteMessage(const char* message, bool& dataOK) {
// What should we do here?
// First - parse the message into a list of tokens.
// 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.
+ size_t n1 = 1+strlen(message);
+ char msg[n1];
+ strncpy(msg, message, n1); // strtok requires a non-const char*
char* token;
- char mes[1000];
int numWords = 0;
- strcpy(mes, message);
- const char delimiters[] = " \t.,;:\"";
- token = strtok(mes, delimiters);
+ const char delimiters[] = " \t.,;:\"\n";
+ char* context;
+ token = strtok_r(msg, delimiters, &context);
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);
++numWords;
- //cout << "token = " << token << '\n';
- token = strtok(NULL, delimiters);
+ SG_LOG(SG_ATC, SG_DEBUG, "voice synth: token: '"
+ << token << "'");
+ token = strtok_r(NULL, delimiters, &context);
}
- WordData* wdptr = new WordData[numWords];
+ WordData wdptr[numWords];
int word = 0;
unsigned int cumLength = 0;
tokenListItr = tokenList.begin();
while(tokenListItr != tokenList.end()) {
if(wordMap.find(*tokenListItr) == wordMap.end()) {
- // Oh dear - the token isn't in the sound file
- //cout << "word " << *tokenListItr << " not found :-(\n";
+ // Oh dear - the token isn't in the sound file
+ SG_LOG(SG_ATC, SG_ALERT, "voice synth: word '"
+ << *tokenListItr << "' not found");
} else {
wdptr[word] = wordMap[*tokenListItr];
cumLength += wdptr[word].length;
// Check for no tokens found else slScheduler can be crashed
if(!word) {
dataOK = false;
- delete[] wdptr;
- return(NULL);
+ return "";
}
- unsigned char* tmpbuf = new unsigned char[cumLength];
- unsigned char* outbuf = new unsigned char[cumLength];
- len = cumLength;
+ char tmpbuf[cumLength];
unsigned int bufpos = 0;
for(int i=0; i<word; ++i) {
/*
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
<< " exceeds rawdata size: " << rawDataSize << endl);
- delete[] wdptr;
- delete[] tmpbuf;
- delete[] outbuf;
+
dataOK = false;
- return(NULL);
+ return "";
}
memcpy(tmpbuf + bufpos, rawSoundData + wdptr[i].offset, wdptr[i].length);
bufpos += wdptr[i].length;
// 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());
if(offsetIn > cumLength) offsetIn = cumLength;
- memcpy(outbuf, tmpbuf + offsetIn, (cumLength - offsetIn));
- memcpy(outbuf + (cumLength - offsetIn), tmpbuf, offsetIn);
-
- delete[] tmpbuf;
- delete[] wdptr;
+
+ string front(tmpbuf, offsetIn);
+ string back(tmpbuf+offsetIn, cumLength - offsetIn);
dataOK = true;
- return(outbuf);
+ return back + front;
}
#include <simgear/compiler.h>
-# include <fstream>
#include <map>
-#include <list>
#include <string>
-#include <simgear/sound/sample_openal.hxx>
-
-using std::map;
-using std::list;
-using std::string;
-
+class SGSoundSample;
struct WordData {
unsigned int offset; // Offset of beginning of word sample into raw sound sample
// 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.
- unsigned char* WriteMessage(char* message, int& len, bool& dataOK);
-
+ std::string WriteMessage(const char* message, bool& dataOK);
private:
#include <simgear/misc/sg_path.hxx>
#include <simgear/debug/logstream.hxx>
+
#include <Airports/simple.hxx>
#include "ATCmgr.hxx"
/*
// periodic radio station search wrapper
static void fgATCSearch( void ) {
- globals->get_ATC_mgr()->Search();
+ globals->get_ATC_mgr()->Search();
}
*/ //This wouldn't compile - including Time/event.hxx breaks it :-(
// Is this still true?? -EMH-
AirportATC::AirportATC() :
- lon(0.0),
- lat(0.0),
- elev(0.0),
atis_freq(0.0),
atis_active(false),
tower_freq(0.0),
ground_freq(0.0),
ground_active(false),
set_by_AI(false),
- numAI(0)
- //airport_atc_map.clear();
+ numAI(0)
+ //airport_atc_map.clear();
{
- for(int i=0; i<ATC_NUM_TYPES; ++i) {
- set_by_comm[0][i] = false;
- set_by_comm[1][i] = false;
- }
}
-FGATCMgr::FGATCMgr() {
- comm_ident[0] = "";
- comm_ident[1] = "";
- //last_comm_ident[0] = "";
- //last_comm_ident[1] = "";
- //approach_ident = "";
- last_in_range = false;
- comm_type[0] = INVALID;
- comm_type[1] = INVALID;
- comm_atc_ptr[0] = NULL;
- comm_atc_ptr[1] = NULL;
- comm_valid[0] = false;
- comm_valid[1] = false;
-
- initDone = false;
+FGATCMgr::FGATCMgr() :
+ initDone(false),
+ atc_list(new atc_list_type),
+ last_in_range(false)
+{
}
FGATCMgr::~FGATCMgr() {
- delete v1;
+ delete v1;
}
void FGATCMgr::bind() {
}
void FGATCMgr::init() {
- //cout << "ATCMgr::init called..." << endl;
-
- comm_node[0] = fgGetNode("/instrumentation/comm[0]/frequencies/selected-mhz", true);
- comm_node[1] = fgGetNode("/instrumentation/comm[1]/frequencies/selected-mhz", true);
- lon_node = fgGetNode("/position/longitude-deg", true);
- lat_node = fgGetNode("/position/latitude-deg", true);
- elev_node = fgGetNode("/position/altitude-ft", true);
- atc_list_itr = atc_list.begin();
-
- // Search for connected ATC stations once per 0.8 seconds or so
- // globals->get_event_mgr()->add( "fgATCSearch()", fgATCSearch,
+ //cout << "ATCMgr::init called..." << endl;
+
+ lon_node = fgGetNode("/position/longitude-deg", true);
+ lat_node = fgGetNode("/position/latitude-deg", true);
+ elev_node = fgGetNode("/position/altitude-ft", true);
+ atc_list_itr = atc_list->begin();
+
+ // Search for connected ATC stations once per 0.8 seconds or so
+ // globals->get_event_mgr()->add( "fgATCSearch()", fgATCSearch,
// FGEvent::FG_EVENT_READY, 800);
//
- // For some reason the above doesn't compile - including Time/event.hxx stops compilation.
+ // For some reason the above doesn't compile - including Time/event.hxx stops compilation.
// Is this still true after the reorganization of the event managar??
// -EMH-
-
- // Initialise the frequency search map
- current_commlist = new FGCommList;
- SGPath p_comm( globals->get_fg_root() );
- current_commlist->init( p_comm );
-
-#ifdef ENABLE_AUDIO_SUPPORT
- // Load all available voices.
- // For now we'll do one hardwired one
-
- v1 = new FGATCVoice;
- try {
- voiceOK = v1->LoadVoice("default");
- voice = true;
- } catch ( sg_io_exception & ) {
- voiceOK = false;
- voice = false;
- delete v1;
- v1 = 0;
- }
-
- /* I've loaded the voice even if /sim/sound/pause is true
- * since I know no way of forcing load of the voice if the user
- * subsequently switches /sim/sound/audible to true.
+
+#ifdef ENABLE_AUDIO_SUPPORT
+ // Load all available voices.
+ // For now we'll do one hardwired one
+
+ v1 = new FGATCVoice;
+ try {
+ voiceOK = v1->LoadVoice("default");
+ voice = true;
+ } catch ( sg_io_exception & ) {
+ voiceOK = false;
+ voice = false;
+ delete v1;
+ v1 = 0;
+ }
+
+ /* I've loaded the voice even if /sim/sound/pause is true
+ * since I know no way of forcing load of the voice if the user
+ * subsequently switches /sim/sound/audible to true.
* (which is the right thing to do -- CLO) :-) */
#else
- voice = false;
+ voice = false;
#endif
- // Initialise the ATC Dialogs
- //cout << "Initing Transmissions..." << endl;
+ // Initialise the ATC Dialogs
+ //cout << "Initing Transmissions..." << endl;
SG_LOG(SG_ATC, SG_INFO, " ATC Transmissions");
current_transmissionlist = new FGTransmissionList;
SGPath p_transmission( globals->get_fg_root() );
p_transmission.append( "ATC/default.transmissions" );
current_transmissionlist->init( p_transmission );
- //cout << "Done Transmissions" << endl;
+ //cout << "Done Transmissions" << endl;
SG_LOG(SG_ATC, SG_INFO, " ATC Dialog System");
current_atcdialog = new FGATCDialog;
current_atcdialog->Init();
- initDone = true;
- //cout << "ATCmgr::init done!" << endl;
+ initDone = true;
+ //cout << "ATCmgr::init done!" << endl;
}
void FGATCMgr::update(double dt) {
- if(!initDone) {
- init();
- SG_LOG(SG_ATC, SG_WARN, "Warning - ATCMgr::update(...) called before ATCMgr::init()");
- }
-
- current_atcdialog->Update(dt);
-
- //cout << "Entering update..." << endl;
- //Traverse the list of active stations.
- //Only update one class per update step to avoid the whole ATC system having to calculate between frames.
- //Eventually we should only update every so many steps.
- //cout << "In FGATCMgr::update - atc_list.size = " << atc_list.size() << endl;
- if(atc_list.size()) {
- if(atc_list_itr == atc_list.end()) {
- atc_list_itr = atc_list.begin();
- }
- //cout << "Updating " << (*atc_list_itr)->get_ident() << ' ' << (*atc_list_itr)->GetType() << '\n';
- //cout << "Freq = " << (*atc_list_itr)->get_freq() << '\n';
- (*atc_list_itr)->Update(dt * atc_list.size());
- //cout << "Done ATC update..." << endl;
- ++atc_list_itr;
- }
-
- /*
- cout << "ATC_LIST: " << atc_list.size() << ' ';
- for(atc_list_iterator it = atc_list.begin(); it != atc_list.end(); it++) {
- cout << (*it)->get_ident() << ' ';
- }
- cout << '\n';
- */
-
- // Search the tuned frequencies every now and then - this should be done with the event scheduler
- static int i = 0; // Very ugly - but there should only ever be one instance of FGATCMgr.
- /*
- if(i == 7) {
- //cout << "About to AreaSearch()" << endl;
- AreaSearch();
- }
- */
- if(i == 15) {
- //cout << "About to search(1)" << endl;
- FreqSearch(1);
- }
- if(i == 30) {
- //cout << "About to search(2)" << endl;
- FreqSearch(2);
- i = 0;
- }
- ++i;
-
- //cout << "comm1 type = " << comm_type[0] << '\n';
- //cout << "Leaving update..." << endl;
+ if(!initDone) {
+ init();
+ SG_LOG(SG_ATC, SG_WARN, "Warning - ATCMgr::update(...) called before ATCMgr::init()");
+ }
+
+ current_atcdialog->Update(dt);
+
+ //cout << "Entering update..." << endl;
+ //Traverse the list of active stations.
+ //Only update one class per update step to avoid the whole ATC system having to calculate between frames.
+ //Eventually we should only update every so many steps.
+ //cout << "In FGATCMgr::update - atc_list.size = " << atc_list->size() << endl;
+ if(atc_list->size()) {
+ if(atc_list_itr == atc_list->end()) {
+ atc_list_itr = atc_list->begin();
+ }
+ //cout << "Updating " << (*atc_list_itr)->get_ident() << ' ' << (*atc_list_itr)->GetType() << '\n';
+ //cout << "Freq = " << (*atc_list_itr)->get_freq() << '\n';
+ (*atc_list_itr).second->Update(dt * atc_list->size());
+ //cout << "Done ATC update..." << endl;
+ ++atc_list_itr;
+ }
+
+#ifdef ATC_TEST
+ cout << "ATC_LIST: " << atc_list->size() << ' ';
+ for(atc_list_iterator it = atc_list->begin(); it != atc_list->end(); it++) {
+ cout << (*it)->get_ident() << ' ';
+ }
+ cout << '\n';
+#endif
+
+ // Search the tuned frequencies every now and then - this should be done with the event scheduler
+ static int i = 0; // Very ugly - but there should only ever be one instance of FGATCMgr.
+ /*** Area search is defeated. Why?
+ if(i == 7) {
+ //cout << "About to AreaSearch()" << endl;
+ AreaSearch();
+ }
+ ***/
+ if(i == 15) {
+ //cout << "About to search navcomm1" << endl;
+ FreqSearch("comm", 0);
+ FreqSearch("nav", 0);
+ }
+ if(i == 30) {
+ //cout << "About to search navcomm2" << endl;
+ FreqSearch("comm", 1);
+ FreqSearch("nav", 1);
+ i = 0;
+ }
+ ++i;
+
+ //cout << "comm1 type = " << comm_type[0] << '\n';
+ //cout << "Leaving update..." << endl;
}
// Returns frequency in KHz - should I alter this to return in MHz?
unsigned short int FGATCMgr::GetFrequency(const string& ident, const atc_type& tp) {
- ATCData test;
- bool ok = current_commlist->FindByCode(ident, test, tp);
- return(ok ? test.freq : 0);
-}
+ ATCData test;
+ bool ok = current_commlist->FindByCode(ident, test, tp);
+ return(ok ? test.freq : 0);
+}
// Register the fact that the AI system wants to activate an airport
// Might need more sophistication in this in the future - eg registration by aircraft call-sign.
bool FGATCMgr::AIRegisterAirport(const string& ident) {
- SG_LOG(SG_ATC, SG_BULK, "AI registered airport " << ident << " with the ATC system");
- //cout << "AI registered airport " << ident << " with the ATC system" << '\n';
- if(airport_atc_map.find(ident) != airport_atc_map.end()) {
- airport_atc_map[ident]->set_by_AI = true;
- airport_atc_map[ident]->numAI++;
- return(true);
- } else {
- const FGAirport *ap = fgFindAirportID(ident);
- if (ap) {
- //cout << "ident = " << ident << '\n';
- AirportATC *a = new AirportATC;
- // I'm not entirely sure that this AirportATC structure business is actually needed - it just duplicates what we can find out anyway!
- a->lon = ap->getLongitude();
- a->lat = ap->getLatitude();
- a->elev = ap->getElevation();
- a->atis_freq = GetFrequency(ident, ATIS);
- //cout << "ATIS freq = " << a->atis_freq << '\n';
- a->atis_active = false;
- a->tower_freq = GetFrequency(ident, TOWER);
- //cout << "Tower freq = " << a->tower_freq << '\n';
- a->tower_active = false;
- a->ground_freq = GetFrequency(ident, GROUND);
- //cout << "Ground freq = " << a->ground_freq << '\n';
- a->ground_active = false;
- // TODO - some airports will have a tower/ground frequency but be inactive overnight.
- a->set_by_AI = true;
- a->numAI = 1;
- airport_atc_map[ident] = a;
- return(true);
- } else {
- SG_LOG(SG_ATC, SG_ALERT, "ERROR - can't find airport " << ident << " in AIRegisterAirport(...)");
- }
- }
- return(false);
+ SG_LOG(SG_ATC, SG_BULK, "AI registered airport " << ident << " with the ATC system");
+ //cout << "AI registered airport " << ident << " with the ATC system" << '\n';
+ if(airport_atc_map.find(ident) != airport_atc_map.end()) {
+ airport_atc_map[ident]->set_by_AI = true;
+ airport_atc_map[ident]->numAI++;
+ return(true);
+ } else {
+ const FGAirport *ap = fgFindAirportID(ident);
+ if (ap) {
+ //cout << "ident = " << ident << '\n';
+ AirportATC *a = new AirportATC;
+ // I'm not entirely sure that this AirportATC structure business is actually needed - it just duplicates what we can find out anyway!
+ a->geod = ap->geod();
+ a->atis_freq = GetFrequency(ident, ATIS)
+ || GetFrequency(ident, AWOS);
+ //cout << "ATIS freq = " << a->atis_freq << '\n';
+ a->atis_active = false;
+ a->tower_freq = GetFrequency(ident, TOWER);
+ //cout << "Tower freq = " << a->tower_freq << '\n';
+ a->tower_active = false;
+ a->ground_freq = GetFrequency(ident, GROUND);
+ //cout << "Ground freq = " << a->ground_freq << '\n';
+ a->ground_active = false;
+ // TODO - some airports will have a tower/ground frequency but be inactive overnight.
+ a->set_by_AI = true;
+ a->numAI = 1;
+ airport_atc_map[ident] = a;
+ return(true);
+ } else {
+ SG_LOG(SG_ATC, SG_ALERT, "ERROR - can't find airport " << ident << " in AIRegisterAirport(...)");
+ }
+ }
+ return(false);
}
-
// Register the fact that the comm radio is tuned to an airport
// Channel is zero based
bool FGATCMgr::CommRegisterAirport(const string& ident, int chan, const atc_type& tp) {
- SG_LOG(SG_ATC, SG_BULK, "Comm channel " << chan << " registered airport " << ident);
- //cout << "Comm channel " << chan << " registered airport " << ident << ' ' << tp << '\n';
- if(airport_atc_map.find(ident) != airport_atc_map.end()) {
- //cout << "IN MAP - flagging set by comm..." << endl;
- airport_atc_map[ident]->set_by_comm[chan][tp] = true;
- if(tp == ATIS) {
- airport_atc_map[ident]->atis_active = true;
- } else if(tp == TOWER) {
- airport_atc_map[ident]->tower_active = true;
- } else if(tp == GROUND) {
- airport_atc_map[ident]->ground_active = true;
- } else if(tp == APPROACH) {
- //a->approach_active = true;
- } // TODO - there *must* be a better way to do this!!!
- return(true);
- } else {
- //cout << "NOT IN MAP - creating new..." << endl;
- const FGAirport *ap = fgFindAirportID(ident);
- if (ap) {
- AirportATC *a = new AirportATC;
- // I'm not entirely sure that this AirportATC structure business is actually needed - it just duplicates what we can find out anyway!
- a->lon = ap->getLongitude();
- a->lat = ap->getLatitude();
- a->elev = ap->getElevation();
- a->atis_freq = GetFrequency(ident, ATIS);
- a->atis_active = false;
- a->tower_freq = GetFrequency(ident, TOWER);
- a->tower_active = false;
- a->ground_freq = GetFrequency(ident, GROUND);
- a->ground_active = false;
- if(tp == ATIS) {
- a->atis_active = true;
- } else if(tp == TOWER) {
- a->tower_active = true;
- } else if(tp == GROUND) {
- a->ground_active = true;
- } else if(tp == APPROACH) {
- //a->approach_active = true;
- } // TODO - there *must* be a better way to do this!!!
- // TODO - some airports will have a tower/ground frequency but be inactive overnight.
- a->set_by_AI = false;
- a->numAI = 0;
- a->set_by_comm[chan][tp] = true;
- airport_atc_map[ident] = a;
- return(true);
- }
- }
- return(false);
+ SG_LOG(SG_ATC, SG_BULK, "Comm channel " << chan << " registered airport " << ident);
+ //cout << "Comm channel " << chan << " registered airport " << ident << ' ' << tp << '\n';
+ if(airport_atc_map.find(ident) != airport_atc_map.end()) {
+ //cout << "IN MAP - flagging set by comm..." << endl;
+//xx airport_atc_map[ident]->set_by_comm[chan][tp] = true;
+ if(tp == ATIS || tp == AWOS) {
+ airport_atc_map[ident]->atis_active = true;
+ } else if(tp == TOWER) {
+ airport_atc_map[ident]->tower_active = true;
+ } else if(tp == GROUND) {
+ airport_atc_map[ident]->ground_active = true;
+ } else if(tp == APPROACH) {
+ //a->approach_active = true;
+ } // TODO - there *must* be a better way to do this!!!
+ return(true);
+ } else {
+ //cout << "NOT IN MAP - creating new..." << endl;
+ const FGAirport *ap = fgFindAirportID(ident);
+ if (ap) {
+ AirportATC *a = new AirportATC;
+ // I'm not entirely sure that this AirportATC structure business is actually needed - it just duplicates what we can find out anyway!
+ a->geod = ap->geod();
+ a->atis_freq = GetFrequency(ident, ATIS)
+ || GetFrequency(ident, AWOS);
+ a->atis_active = false;
+ a->tower_freq = GetFrequency(ident, TOWER);
+ a->tower_active = false;
+ a->ground_freq = GetFrequency(ident, GROUND);
+ a->ground_active = false;
+ if(tp == ATIS || tp == AWOS) {
+ a->atis_active = true;
+ } else if(tp == TOWER) {
+ a->tower_active = true;
+ } else if(tp == GROUND) {
+ a->ground_active = true;
+ } else if(tp == APPROACH) {
+ //a->approach_active = true;
+ } // TODO - there *must* be a better way to do this!!!
+ // TODO - some airports will have a tower/ground frequency but be inactive overnight.
+ a->set_by_AI = false;
+ a->numAI = 0;
+//xx a->set_by_comm[chan][tp] = true;
+ airport_atc_map[ident] = a;
+ return(true);
+ }
+ }
+ return(false);
}
-
-// Remove from list only if not needed by the AI system or the other comm channel
-// Note that chan is zero based.
-void FGATCMgr::CommRemoveFromList(const string& id, const atc_type& tp, int chan) {
- SG_LOG(SG_ATC, SG_BULK, "CommRemoveFromList called for airport " << id << " " << tp << " by channel " << chan);
- //cout << "CommRemoveFromList called for airport " << id << " " << tp << " by channel " << chan << '\n';
- if(airport_atc_map.find(id) != airport_atc_map.end()) {
- AirportATC* a = airport_atc_map[id];
- //cout << "In CommRemoveFromList, a->ground_freq = " << a->ground_freq << endl;
- if(a->set_by_AI && tp != ATIS) {
- // Set by AI, so don't remove simply because user isn't tuned in any more - just stop displaying
- SG_LOG(SG_ATC, SG_BULK, "In CommRemoveFromList, service was set by AI\n");
- FGATC* aptr = GetATCPointer(id, tp);
- switch(chan) {
- case 0:
- //cout << "chan 1\n";
- a->set_by_comm[0][tp] = false;
- if(!a->set_by_comm[1][tp]) {
- //cout << "not set by comm2\n";
- if(aptr != NULL) {
- //cout << "Got pointer\n";
- aptr->SetNoDisplay();
- //cout << "Setting no display...\n";
- } else {
- //cout << "Not got pointer\n";
- }
- }
- break;
- case 1:
- a->set_by_comm[1][tp] = false;
- if(!a->set_by_comm[0][tp]) {
- if(aptr != NULL) {
- aptr->SetNoDisplay();
- //cout << "Setting no display...\n";
- }
- }
- break;
- }
- //airport_atc_map[id] = a;
- return;
- } else {
- switch(chan) {
- case 0:
- a->set_by_comm[0][tp] = false;
- // Remove only if not also set by the other comm channel
- if(!a->set_by_comm[1][tp]) {
- a->tower_active = false;
- a->ground_active = false;
- RemoveFromList(id, tp);
- }
- break;
- case 1:
- a->set_by_comm[1][tp] = false;
- if(!a->set_by_comm[0][tp]) {
- a->tower_active = false;
- a->ground_active = false;
- RemoveFromList(id, tp);
- }
- break;
- }
- }
- }
-}
-
-
-// Remove from list - should only be called from above or similar
-// This function *will* remove it from the list regardless of who else might want it.
-void FGATCMgr::RemoveFromList(const string& id, const atc_type& tp) {
- //cout << "FGATCMgr::RemoveFromList called..." << endl;
- //cout << "Requested type = " << tp << endl;
- //cout << "id = " << id << endl;
- atc_list_iterator it = atc_list.begin();
- while(it != atc_list.end()) {
- //cout << "type = " << (*it)->GetType() << '\n';
- //cout << "Ident = " << (*it)->get_ident() << '\n';
- if( ((*it)->get_ident() == id)
- && ((*it)->GetType() == tp) ) {
- //Before removing it stop it transmitting!!
- //cout << "OBLITERATING FROM LIST!!!\n";
- (*it)->SetNoDisplay();
- (*it)->Update(0.00833);
- delete (*it);
- atc_list.erase(it);
- atc_list_itr = atc_list.begin(); // Reset the persistent itr incase we've left it off the end.
- break;
- }
- ++it;
- }
+typedef map<string,int> MSI;
+
+void FGATCMgr::ZapOtherService(const string ncunit, const string svc_name){
+ for (atc_list_iterator svc = atc_list->begin(); svc != atc_list->end(); svc++) {
+ //cout << "Zapping " << navcomm
+ // << "[" << unit << "]" << " otherthan: " << svc_name << endl;
+ if (svc->first != svc_name) {
+ MSI &actv = svc->second->active_on;
+ // OK, we have found some OTHER service;
+ // see if it is (was) active on our unit:
+ if (!actv.count(ncunit)) continue;
+ // cout << "Eradicating '" << svc->first << "' from: " << ncunit << endl;
+ actv.erase(ncunit);
+ if (!actv.size()) {
+ //cout << "Eradicating service: '" << svc->first << "'" << endl;
+ svc->second->SetNoDisplay();
+ svc->second->Update(0); // one last update
+ delete svc->second;
+ atc_list->erase(svc);
+// ALL pointers into the ATC list are now invalid,
+// so let's reset them:
+ atc_list_itr = atc_list->begin();
+ }
+ break; // cannot be duplicates in the active list
+ }
+ }
}
// Return NULL if the given service is not in the list
// - *** THE CALLING FUNCTION MUST CHECK FOR THIS ***
FGATC* FGATCMgr::FindInList(const string& id, const atc_type& tp) {
- //cout << "Entering FindInList for " << id << ' ' << tp << endl;
- atc_list_iterator it = atc_list.begin();
- while(it != atc_list.end()) {
- if( ((*it)->get_ident() == id)
- && ((*it)->GetType() == tp) ) {
- return(*it);
- }
- ++it;
- }
- // If we get here it's not in the list
- //cout << "Couldn't find it in the list though :-(" << endl;
- return(NULL);
+ string ndx = id + decimalNumeral(tp);
+ if (!atc_list->count(ndx)) return 0;
+ return (*atc_list)[ndx];
}
// Returns true if the airport is found in the map
bool FGATCMgr::GetAirportATCDetails(const string& icao, AirportATC* a) {
- if(airport_atc_map.find(icao) != airport_atc_map.end()) {
- *a = *airport_atc_map[icao];
- return(true);
- } else {
- return(false);
- }
+ if(airport_atc_map.find(icao) != airport_atc_map.end()) {
+ *a = *airport_atc_map[icao];
+ return(true);
+ } else {
+ return(false);
+ }
}
// We really ought to make this private and call it from the CommRegisterAirport / AIRegisterAirport functions
// - at the moment all these GetATC... functions exposed are just too complicated.
FGATC* FGATCMgr::GetATCPointer(const string& icao, const atc_type& type) {
- if(airport_atc_map.find(icao) == airport_atc_map.end()) {
- //cout << "Unable to find " << icao << ' ' << type << " in the airport_atc_map" << endl;
- return NULL;
- }
- //cout << "In GetATCPointer, found " << icao << ' ' << type << endl;
- AirportATC *a = airport_atc_map[icao];
- //cout << "a->lon = " << a->lon << '\n';
- //cout << "a->elev = " << a->elev << '\n';
- //cout << "a->tower_freq = " << a->tower_freq << '\n';
- switch(type) {
- case TOWER:
- if(a->tower_active) {
- // Get the pointer from the list
- return(FindInList(icao, type));
- } else {
- ATCData data;
- if(current_commlist->FindByFreq(a->lon, a->lat, a->elev, a->tower_freq, &data, TOWER)) {
- FGTower* t = new FGTower;
- t->SetData(&data);
- atc_list.push_back(t);
- a->tower_active = true;
- airport_atc_map[icao] = a;
- //cout << "Initing tower " << icao << " in GetATCPointer()\n";
- t->Init();
- return(t);
- } else {
- SG_LOG(SG_ATC, SG_ALERT, "ERROR - tower that should exist in FGATCMgr::GetATCPointer for airport " << icao << " not found");
- }
- }
- break;
- case APPROACH:
- break;
- case ATIS:
- SG_LOG(SG_ATC, SG_ALERT, "ERROR - ATIS station should not be requested from FGATCMgr::GetATCPointer");
- break;
- case GROUND:
- //cout << "IN CASE GROUND" << endl;
- if(a->ground_active) {
- // Get the pointer from the list
- return(FindInList(icao, type));
- } else {
- ATCData data;
- if(current_commlist->FindByFreq(a->lon, a->lat, a->elev, a->ground_freq, &data, GROUND)) {
- FGGround* g = new FGGround;
- g->SetData(&data);
- atc_list.push_back(g);
- a->ground_active = true;
- airport_atc_map[icao] = a;
- g->Init();
- return(g);
- } else {
- SG_LOG(SG_ATC, SG_ALERT, "ERROR - ground control that should exist in FGATCMgr::GetATCPointer for airport " << icao << " not found");
- }
- }
- break;
- case INVALID:
- break;
- case ENROUTE:
- break;
- case DEPARTURE:
- break;
- }
-
- SG_LOG(SG_ATC, SG_ALERT, "ERROR IN FGATCMgr - reached end of GetATCPointer");
- //cout << "ERROR IN FGATCMgr - reached end of GetATCPointer" << endl;
-
- return(NULL);
+ if(airport_atc_map.find(icao) == airport_atc_map.end()) {
+ //cout << "Unable to find " << icao << ' ' << type << " in the airport_atc_map" << endl;
+ return NULL;
+ }
+ //cout << "In GetATCPointer, found " << icao << ' ' << type << endl;
+ AirportATC *a = airport_atc_map[icao];
+ //cout << "a->lon = " << a->lon << '\n';
+ //cout << "a->elev = " << a->elev << '\n';
+ //cout << "a->tower_freq = " << a->tower_freq << '\n';
+ switch(type) {
+ case TOWER:
+ if(a->tower_active) {
+ // Get the pointer from the list
+ return(FindInList(icao, type));
+ } else {
+ ATCData data;
+ if(current_commlist->FindByFreq(a->geod, a->tower_freq, &data, TOWER)) {
+ FGTower* t = new FGTower;
+ t->SetData(&data);
+ (*atc_list)[icao+decimalNumeral(type)] = t;
+ a->tower_active = true;
+ airport_atc_map[icao] = a;
+ //cout << "Initing tower " << icao << " in GetATCPointer()\n";
+ t->Init();
+ return(t);
+ } else {
+ SG_LOG(SG_ATC, SG_ALERT, "ERROR - tower that should exist in FGATCMgr::GetATCPointer for airport " << icao << " not found");
+ }
+ }
+ break;
+ case APPROACH:
+ break;
+ case ATIS: case AWOS:
+ SG_LOG(SG_ATC, SG_ALERT, "ERROR - ATIS station should not be requested from FGATCMgr::GetATCPointer");
+ break;
+ case GROUND:
+ //cout << "IN CASE GROUND" << endl;
+ if(a->ground_active) {
+ // Get the pointer from the list
+ return(FindInList(icao, type));
+ } else {
+ ATCData data;
+ if(current_commlist->FindByFreq(a->geod, a->ground_freq, &data, GROUND)) {
+ FGGround* g = new FGGround;
+ g->SetData(&data);
+ (*atc_list)[icao+decimalNumeral(type)] = g;
+ a->ground_active = true;
+ airport_atc_map[icao] = a;
+ g->Init();
+ return(g);
+ } else {
+ SG_LOG(SG_ATC, SG_ALERT, "ERROR - ground control that should exist in FGATCMgr::GetATCPointer for airport " << icao << " not found");
+ }
+ }
+ break;
+ case INVALID:
+ break;
+ case ENROUTE:
+ break;
+ case DEPARTURE:
+ break;
+ }
+
+ SG_LOG(SG_ATC, SG_ALERT, "ERROR IN FGATCMgr - reached end of GetATCPointer");
+ //cout << "ERROR IN FGATCMgr - reached end of GetATCPointer" << endl;
+
+ return(NULL);
}
// Return a pointer to an appropriate voice for a given type of ATC
// specific voices, and possible make sure that the same voice doesn't get used
// at different airports in quick succession if a large enough selection are available.
FGATCVoice* FGATCMgr::GetVoicePointer(const atc_type& type) {
- // TODO - implement me better - maintain a list of loaded voices and other voices!!
- if(voice) {
- switch(type) {
- case ATIS:
- if(voiceOK) {
- return(v1);
- }
- case TOWER:
- return(NULL);
- case APPROACH:
- return(NULL);
- case GROUND:
- return(NULL);
- default:
- return(NULL);
- }
- return(NULL);
- } else {
- return(NULL);
- }
+ // TODO - implement me better - maintain a list of loaded voices and other voices!!
+ if(voice) {
+ switch(type) {
+ case ATIS: case AWOS:
+ if(voiceOK) {
+ return(v1);
+ }
+ case TOWER:
+ return(NULL);
+ case APPROACH:
+ return(NULL);
+ case GROUND:
+ return(NULL);
+ default:
+ return(NULL);
+ }
+ return(NULL);
+ } else {
+ return(NULL);
+ }
}
// Search for ATC stations by frequency
-void FGATCMgr::FreqSearch(int channel) {
- int chan = channel - 1; // Convert to zero-based for the arrays
-
- ATCData data;
- double freq = comm_node[chan]->getDoubleValue();
- lon = lon_node->getDoubleValue();
- lat = lat_node->getDoubleValue();
- elev = elev_node->getDoubleValue() * SG_FEET_TO_METER;
-
- // Query the data store and get the closest match if any
- if(current_commlist->FindByFreq(lon, lat, elev, freq, &data)) {
- // We have a match
- // What's the logic?
- // If this channel not previously valid then easy - add ATC to list
- // If this channel was valid then - Have we tuned to a different service?
- // If so - de-register one and add the other
- if(comm_valid[chan]) {
- if((comm_ident[chan] == data.ident) && (comm_type[chan] == data.type)) {
- // Then we're still tuned into the same service so do nought and return
- return;
- } else {
- // Something's changed - either the location or the service type
- // We need to feed the channel in so we're not removing it if we're also tuned in on the other channel
- CommRemoveFromList(comm_ident[chan], comm_type[chan], chan);
- }
- }
- // At this point we can assume that we need to add the service.
- comm_ident[chan] = data.ident;
- comm_type[chan] = data.type;
- comm_x[chan] = (double)data.x;
- comm_y[chan] = (double)data.y;
- comm_z[chan] = (double)data.z;
- comm_lon[chan] = (double)data.lon;
- comm_lat[chan] = (double)data.lat;
- comm_elev[chan] = (double)data.elev;
- comm_valid[chan] = true;
-
- // This was a switch-case statement but the compiler didn't like the new variable creation with it.
- if(comm_type[chan] == ATIS) {
- CommRegisterAirport(comm_ident[chan], chan, ATIS);
- FGATC* app = FindInList(comm_ident[chan], ATIS);
- if(app != NULL) {
- // The station is already in the ATC list
- //cout << "In list - flagging SetDisplay..." << endl;
- comm_atc_ptr[chan] = app;
- app->SetDisplay();
- } else {
- // Generate the station and put in the ATC list
- //cout << "Not in list - generating..." << endl;
- FGATIS* a = new FGATIS;
- a->SetData(&data);
- comm_atc_ptr[chan] = a;
- a->SetDisplay();
- //a->Init();
- atc_list.push_back(a);
- }
- } else if (comm_type[chan] == TOWER) {
- //cout << "TOWER TOWER TOWER\n";
- CommRegisterAirport(comm_ident[chan], chan, TOWER);
- //cout << "Done (TOWER)" << endl;
- FGATC* app = FindInList(comm_ident[chan], TOWER);
- if(app != NULL) {
- // The station is already in the ATC list
- SG_LOG(SG_GENERAL, SG_DEBUG, comm_ident[chan] << " is in list - flagging SetDisplay...");
- //cout << comm_ident[chan] << " is in list - flagging SetDisplay...\n";
- comm_atc_ptr[chan] = app;
- app->SetDisplay();
- } else {
- // Generate the station and put in the ATC list
- SG_LOG(SG_GENERAL, SG_DEBUG, comm_ident[chan] << " is not in list - generating...");
- //cout << comm_ident[chan] << " is not in list - generating...\n";
- FGTower* t = new FGTower;
- t->SetData(&data);
- comm_atc_ptr[chan] = t;
- //cout << "Initing tower in FreqSearch()\n";
- t->Init();
- t->SetDisplay();
- atc_list.push_back(t);
- }
- } else if (comm_type[chan] == GROUND) {
- CommRegisterAirport(comm_ident[chan], chan, GROUND);
- //cout << "Done (GROUND)" << endl;
- FGATC* app = FindInList(comm_ident[chan], GROUND);
- if(app != NULL) {
- // The station is already in the ATC list
- comm_atc_ptr[chan] = app;
- app->SetDisplay();
- } else {
- // Generate the station and put in the ATC list
- FGGround* g = new FGGround;
- g->SetData(&data);
- comm_atc_ptr[chan] = g;
- g->Init();
- g->SetDisplay();
- atc_list.push_back(g);
- }
- } else if (comm_type[chan] == APPROACH) {
- // We have to be a bit more carefull here since approaches are also searched by area
- CommRegisterAirport(comm_ident[chan], chan, APPROACH);
- //cout << "Done (APPROACH)" << endl;
- FGATC* app = FindInList(comm_ident[chan], APPROACH);
- if(app != NULL) {
- // The station is already in the ATC list
- app->AddPlane("Player");
- app->SetDisplay();
- comm_atc_ptr[chan] = app;
- } else {
- // Generate the station and put in the ATC list
- FGApproach* a = new FGApproach;
- a->SetData(&data);
- comm_atc_ptr[chan] = a;
- a->Init();
- a->SetDisplay();
- a->AddPlane("Player");
- atc_list.push_back(a);
- }
- }
- } else {
- if(comm_valid[chan]) {
- if(comm_type[chan] != APPROACH) {
- // Currently approaches are removed by Alexander's out-of-range mechanism
- CommRemoveFromList(comm_ident[chan], comm_type[chan], chan);
- }
- // Note that we *don't* call SetNoDisplay() here because the other comm channel
- // might be tuned into the same station - this is handled by CommRemoveFromList(...)
- comm_type[chan] = INVALID;
- comm_atc_ptr[chan] = NULL;
- comm_valid[chan] = false;
- }
- }
+void FGATCMgr::FreqSearch(const string navcomm, const int unit) {
+
+
+ string ncunit = navcomm + "[" + decimalNumeral(unit) + "]";
+ string commbase = "/instrumentation/" + ncunit;
+ string commfreq = commbase + "/frequencies/selected-mhz";
+ SGPropertyNode_ptr comm_node = fgGetNode(commfreq.c_str(), false);
+
+ //cout << "FreqSearch: " << ncunit
+ // << " node: " << comm_node << endl;
+ if (!comm_node) return; // no such radio unit
+
+ ATCData data;
+ double freq = comm_node->getDoubleValue();
+ _aircraftPos = SGGeod::fromDegFt(lon_node->getDoubleValue(),
+ lat_node->getDoubleValue(), elev_node->getDoubleValue());
+
+// Query the data store and get the closest match if any
+ //cout << "Will FindByFreq: " << lat << " " << lon << " " << elev
+ // << " freq: " << freq << endl;
+ if(current_commlist->FindByFreq(_aircraftPos, freq, &data)) {
+ //cout << "FoundByFreq: " << freq
+ // << " ident: " << data.ident
+ // << " type: " << data.type << " ***" << endl;
+// We are in range of something.
+
+
+// Get rid of any *other* service that was on this radio unit:
+ string svc_name = data.ident+decimalNumeral(data.type);
+ ZapOtherService(ncunit, svc_name);
+// See if the service already exists, possibly connected to
+// some other radio unit:
+ if (atc_list->count(svc_name)) {
+ // make sure the service knows it's tuned on this radio:
+ FGATC* svc = (*atc_list)[svc_name];
+ svc->active_on[ncunit] = 1;
+ svc->SetDisplay();
+ if (data.type == APPROACH) svc->AddPlane("Player");
+ return;
+ }
+
+ CommRegisterAirport(data.ident, unit, data.type);
+
+// This was a switch-case statement but the compiler didn't like
+// the new variable creation with it.
+ if (data.type == ATIS
+ || data.type == AWOS) (*atc_list)[svc_name] = new FGATIS;
+ else if (data.type == TOWER) (*atc_list)[svc_name] = new FGTower;
+ else if (data.type == GROUND) (*atc_list)[svc_name] = new FGGround;
+ else if (data.type == APPROACH) (*atc_list)[svc_name] = new FGApproach;
+ FGATC* svc = (*atc_list)[svc_name];
+ svc->SetData(&data);
+ svc->active_on[ncunit] = 1;
+ svc->SetDisplay();
+ svc->Init();
+ if (data.type == APPROACH) svc->AddPlane("Player");
+ } else {
+ // No services in range. Zap any service on this unit.
+ ZapOtherService(ncunit, "x x x");
+ }
}
+#ifdef AREA_SEARCH
+/* I don't think AreaSearch ever gets called */
// Search ATC stations by area in order that we appear 'on the radar'
void FGATCMgr::AreaSearch() {
- // Search for Approach stations
- comm_list_type approaches;
- comm_list_iterator app_itr;
-
- lon = lon_node->getDoubleValue();
- lat = lat_node->getDoubleValue();
- elev = elev_node->getDoubleValue() * SG_FEET_TO_METER;
-
- // search stations in range
- int num_app = current_commlist->FindByPos(lon, lat, elev, 100.0, &approaches, APPROACH);
- if (num_app != 0) {
- //cout << num_app << " approaches found in radiostack search !!!!" << endl;
-
- for(app_itr = approaches.begin(); app_itr != approaches.end(); app_itr++) {
-
- FGATC* app = FindInList(app_itr->ident, app_itr->type);
- if(app != NULL) {
- // The station is already in the ATC list
- //cout << "In list adding player\n";
- app->AddPlane("Player");
- //app->Update();
- } else {
- // Generate the station and put in the ATC list
- FGApproach* a = new FGApproach;
- a->SetData(&(*app_itr));
- //cout << "Adding player\n";
- a->AddPlane("Player");
- //a->Update();
- atc_list.push_back(a);
- }
- }
- }
-
- // remove planes which are out of range
- // TODO - I'm not entirely sure that this belongs here.
- atc_list_iterator it = atc_list.begin();
- while(it != atc_list.end()) {
- if((*it)->GetType() == APPROACH ) {
- int np = (*it)->RemovePlane();
- // if approach has no planes left remove it from ATC list
- if ( np == 0) {
- //cout << "REMOVING AN APPROACH STATION WITH NO PLANES..." << endl;
- (*it)->SetNoDisplay();
- (*it)->Update(0.00833);
- delete (*it);
- atc_list.erase(it);
- atc_list_itr = atc_list.begin(); // Reset the persistent itr incase we've left it off the end.
- break; // the other stations will be checked next time
- }
- }
- ++it;
- }
+ const string AREA("AREA");
+ // Search for Approach stations
+ comm_list_type approaches;
+ comm_list_iterator app_itr;
+
+ lon = lon_node->getDoubleValue();
+ lat = lat_node->getDoubleValue();
+ elev = elev_node->getDoubleValue() * SG_FEET_TO_METER;
+ for (atc_list_iterator svc = atc_list->begin(); svc != atc_list->end(); svc++) {
+ MSI &actv = svc->second->active_on;
+ if (actv.count(AREA)) actv[AREA] = 0; // Mark all as maybe not in range
+ }
+
+ // search stations in range
+ int num_app = current_commlist->FindByPos(lon, lat, elev, 100.0, &approaches, APPROACH);
+ if (num_app != 0) {
+ //cout << num_app << " approaches found in area search !!!!" << endl;
+
+ for(app_itr = approaches.begin(); app_itr != approaches.end(); app_itr++) {
+ FGATC* app = FindInList(app_itr->ident, app_itr->type);
+ string svc_name = app_itr->ident+decimalNumeral(app_itr->type);
+ if(app != NULL) {
+ // The station is already in the ATC list
+ app->AddPlane("Player");
+ } else {
+ // Generate the station and put in the ATC list
+ FGApproach* a = new FGApproach;
+ a->SetData(&(*app_itr));
+ a->AddPlane("Player");
+ (*atc_list)[svc_name] = a;
+ //cout << "New area service: " << svc_name << endl;
+ }
+ FGATC* svc = (*atc_list)[svc_name];
+ svc->active_on[AREA] = 1;
+ }
+ }
+
+ for (atc_list_iterator svc = atc_list->begin(); svc != atc_list->end(); svc++) {
+ MSI &actv = svc->second->active_on;
+ if (!actv.count(AREA)) continue;
+ if (!actv[AREA]) actv.erase(AREA);
+ if (!actv.size()) { // this service no longer active at all
+ cout << "Eradicating area service: " << svc->first << endl;
+ svc->second->SetNoDisplay();
+ svc->second->Update(0);
+ delete (svc->second);
+ atc_list->erase(svc);
+// Reset the persistent iterator, since any erase() makes it invalid:
+ atc_list_itr = atc_list->begin();
+// Hope we only move out of one approach-area;
+// others will not be noticed until next update:
+ break;
+ }
+ }
}
+#endif
struct AirportATC {
AirportATC();
- float lon;
- float lat;
- float elev;
+ SGGeod geod;
float atis_freq;
bool atis_active;
float tower_freq;
// Flags to ensure the stations don't get wrongly deactivated
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
- 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
airport_atc_map_iterator airport_atc_map_itr;
// 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::const_iterator atc_list_const_iterator;
// Everything put in this list should be created dynamically
// on the heap and ***DELETED WHEN REMOVED!!!!!***
- atc_list_type atc_list;
+ atc_list_type* atc_list;
atc_list_iterator atc_list_itr;
// Any member function of FGATCMgr is permitted to leave this iterator pointing
// 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.
// Position of the Users Aircraft
- double lon;
- 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];
+ SGGeod _aircraftPos;
// Pointers to current users position
SGPropertyNode_ptr lon_node;
SGPropertyNode_ptr lat_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;
bool last_in_range;
// at different airports in quick succession if a large enough selection are available.
FGATCVoice* GetVoicePointer(const atc_type& type);
- atc_type GetComm1ATCType() { return(comm_type[0]); }
- FGATC* GetComm1ATCPointer() { return(comm_atc_ptr[0]); }
- atc_type GetComm2ATCType() { return(comm_type[1]); }
- FGATC* GetComm2ATCPointer() { return(comm_atc_ptr[1]); }
+ atc_type GetComm1ATCType() { return(INVALID/* kludge */); }
+ FGATC* GetComm1ATCPointer() { return(0/* kludge */); }
+ atc_type GetComm2ATCType() { return(INVALID); }
+ FGATC* GetComm2ATCPointer() { return(0/* kludge */); }
// Get the frequency of a given service at a given airport
// Returns zero if not found
// Remove a class from the atc_list and delete it from memory
// *if* no other comm channel or AI plane is using it.
- void CommRemoveFromList(const string& id, const atc_type& tp, int chan);
-
- // 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);
+ void ZapOtherService(const string ncunit, const string svc_name);
// Return a pointer to a class in the list given ICAO code and type
// (external interface to this is through GetATCPointer)
// - *** THE CALLING FUNCTION MUST CHECK FOR THIS ***
FGATC* FindInList(const string& id, const atc_type& tp);
- // Search the specified channel for stations on the same frequency and in range.
- void FreqSearch(int channel);
+ // Search the specified radio for stations on the same frequency and in range.
+ void FreqSearch(const string navcomm, const int unit);
- // Search ATC stations by area in order that we appear 'on the radar'
- void AreaSearch();
+#ifdef AREA_SEARCH
+ // Search ATC stations by area in order that we appear 'on the radar'
+ void AreaSearch();
+#endif
};
#include "ATCutils.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
string ConvertNumToSpokenDigits(const string &n) {
return(str);
}
-
-// Convert an integer to spoken digits
-string ConvertNumToSpokenDigits(int n) {
+// Convert an integer to a decimal numeral string
+string decimalNumeral(const int& n) {
std::ostringstream buf;
buf << n;
- return(ConvertNumToSpokenDigits(buf.str()));
+ return buf.str();
}
-
-// Convert a 2 digit rwy number to a spoken-style string
-string ConvertRwyNumToSpokenString(int n) {
- // 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);
+// Convert an integer to spoken digits
+string ConvertNumToSpokenDigits(const int& n) {
+ return ConvertNumToSpokenDigits(decimalNumeral(n));
}
-// Assumes we get a two-digit string optionally appended with L, R or C
-// eg 01 07L 29R 36
+
+// Assumes we get a string of digits optionally appended with L, R or C
+// eg 1 7L 29R 36
// Anything else is not guaranteed to be handled correctly!
-string ConvertRwyNumToSpokenString(const string &s) {
- if(s.size() < 3) {
- return(ConvertRwyNumToSpokenString(atoi(s.c_str())));
- } else {
- string r = ConvertRwyNumToSpokenString(atoi(s.substr(0,2).c_str()));
- if(s.substr(2,1) == "L") {
- r += " left";
- } else if(s.substr(2,1) == "R") {
- r += " right";
- } else if(s.substr(2,1) == "C") {
- r += " center";
- } else {
- SG_LOG(SG_ATC, SG_WARN, "WARNING: Unknown suffix " << s.substr(2,1) << " from runway ID " << s << " in ConvertRwyNumToSpokenString(...)");
- }
- return(r);
- }
+string ConvertRwyNumToSpokenString(const string &rwy) {
+ string rslt;
+ for (int ii = 0; ii < rwy.length(); ii++){
+ if (rslt.length()) rslt += " ";
+ string ch = rwy.substr(ii,1);
+ if (isdigit(ch[0])) rslt += ConvertNumToSpokenDigits(atoi(ch.c_str()));
+ else if (ch == "R") rslt += "right";
+ else if (ch == "C") rslt += "center";
+ else if (ch == "L") rslt += "left";
+ else {
+ rslt += GetPhoneticLetter(ch[0]);
+ SG_LOG(SG_ATC, SG_WARN, "WARNING: Unknown suffix '" << ch
+ << "' in runway " << rwy << " in ConvertRwyNumToSpokenString(...)");
+ }
+ }
+ return rslt;
}
// Return the phonetic letter of a letter represented as an integer 1->26
-string GetPhoneticIdent(int i) {
- // TODO - Check i is between 1 and 26 and wrap if necessary
- return(GetPhoneticIdent(char('a' + (i-1))));
+string GetPhoneticLetter(const int i) {
+ return(letters[i % LTRS]);
}
// Return the phonetic letter of a character in the range a-z or A-Z.
// Currently always returns prefixed by lowercase.
-string GetPhoneticIdent(char c) {
- c = tolower(c);
- // 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");
+string GetPhoneticLetter(const char c) {
+ return GetPhoneticLetter(int(tolower(c) - 'a'));
}
// Get the compass direction associated with a heading in degrees
string ConvertNumToSpokenDigits(const string &n);
// Convert an integer to spoken digits
-string ConvertNumToSpokenDigits(int n);
-
-// Convert a 2 digit rwy number to a spoken-style string
-string ConvertRwyNumToSpokenString(int n);
+string ConvertNumToSpokenDigits(const int& n);
+string decimalNumeral(const int& n);
// Convert rwy number string to a spoken-style string
-// eg "05L" to "zero five left"
-// Assumes we get a two-digit string optionally appended with R, L, or C
-// eg 01 07L 29R 36
-// Anything else is not guaranteed to be handled correctly!
+// eg "15L" to "one five left"
+// Assumes we get a string of digits optionally appended with R, L, or C
+// eg 1 7L 29R 36
string ConvertRwyNumToSpokenString(const string &s);
-// Return the phonetic letter of a letter represented as an integer 1->26
-string GetPhoneticIdent(int i);
+const int LTRS(26);
+// 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.
// Currently always returns prefixed by lowercase.
-string GetPhoneticIdent(char c);
+string GetPhoneticLetter(char c);
// Get the compass direction associated with a heading in degrees
// Currently returns 8 direction resolution (N, NE, E etc...)
void FGApproach::calc_wp( const int &i ) {
int j;
- double course, d, cd, a1;
+ double course, d, cd, a1, az2;
int wpn = planes[i].wpn;
// waypoint 0: Threshold of active runway
- course = SGGeoc::courseRad(SGGeoc::fromDegM(lon, lat, 6e6), SGGeoc::fromDegM(active_rw_lon, active_rw_lat, 6e6));
- d = SGGeoc::distanceM(SGGeoc::fromDegM(lon, lat, 6e6), SGGeoc::fromDegM(active_rw_lon, active_rw_lat, 6e6));
+ SGGeod activeRunway(SGGeod::fromDeg(active_rw_lon, active_rw_lat));
+ SGGeodesy::inverse(_geod, activeRunway, course, az2, d);
+
double d1 = active_rw_hdg+180.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,
&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][5] = 0.0;
wpn += 1;
// ====================
// vertical navigation
// ====================
- double alt = elev+3000.0;
+ double alt = _geod.getElevationM()+3000.0;
planes[i].wpts[1][2] = round_alt( true, alt );
for ( j=2; j<wpn-1; ++j ) {
double dalt = planes[i].alt - planes[i].wpts[j-1][2];
planes[i].hdg = hdg_node->getDoubleValue();
planes[i].spd = speed_node->getDoubleValue();
- double course, distance;
- course = SGGeoc::courseRad(SGGeoc::fromDegM(lon, lat, 6e6), SGGeoc::fromDegM(planes[i].lon, active_rw_lat, 6e6));
- distance = SGGeoc::distanceM(SGGeoc::fromDegM(lon, lat, 6e6), SGGeoc::fromDegM(planes[i].lon, active_rw_lat, 6e6));
- planes[i].dist = distance/SG_NM_TO_METER;
- planes[i].brg = 360.0-course*SGD_RADIANS_TO_DEGREES;
+ double course, distance, az2;
+ SGGeod plane(SGGeod::fromDeg(planes[1].lon, active_rw_lat));
+ SGGeodesy::inverse(_geod, plane, course, az2, distance);
+ planes[i].dist = distance * SG_METER_TO_NM;
+ planes[i].brg = 360.0-course;
//cout << "Plane Id: " << planes[i].ident << " Distance to " << ident
// << " is " << planes[i].dist << " miles " << "Bearing " << planes[i].brg << endl;
// along with this program; if not, write to the Free Software
// 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
# include <config.h>
#endif
+#include "atis.hxx"
+
#include <simgear/compiler.h>
-#include <stdlib.h> // atoi()
-#include <stdio.h> // sprintf
+#include <stdlib.h> // atoi()
+#include <stdio.h> // sprintf
#include <string>
-using std::string;
-
#include <iostream>
-using std::cout;
+
+
+#include <boost/tuple/tuple.hpp>
#include <simgear/misc/sg_path.hxx>
#include <Environment/environment_mgr.hxx>
#include <Environment/environment.hxx>
+#include <Environment/atmosphere.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <Airports/runways.hxx>
-#include "atis.hxx"
+
#include "commlist.hxx"
#include "ATCutils.hxx"
#include "ATCmgr.hxx"
+using std::cout;
+using std::cout;
+using boost::ref;
+using boost::make_tuple;
+
FGATIS::FGATIS() :
- transmission(""),
- trans_ident(""),
- atis_failed(false),
- refname("atis")
- //type(ATIS)
+ transmission(""),
+ trans_ident(""),
+ old_volume(0),
+ atis_failed(false),
+ attention(0),
+ _prev_display(0),
+ refname("atis")
{
- _vPtr = globals->get_ATC_mgr()->GetVoicePointer(ATIS);
- _voiceOK = (_vPtr == NULL ? false : true);
- _type = ATIS;
+ _vPtr = globals->get_ATC_mgr()->GetVoicePointer(ATIS);
+ _voiceOK = (_vPtr == NULL ? false : true);
+ 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() {
+ 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.
void FGATIS::Update(double dt) {
- if(_display) {
- if(_displaying) {
- // Check if we need to update the message
- // - basically every hour and if the weather changes significantly at the station
- } else {
- // We need to get and display the message
- UpdateTransmission();
- //cout << "ATIS.CXX - calling ATCMgr to render transmission..." << endl;
- Render(transmission, refname, true);
- _displaying = true;
- }
- } else {
- // We shouldn't be displaying
- if(_displaying) {
- //cout << "ATIS.CXX - calling NoRender()..." << endl;
- NoRender(refname);
- _displaying = false;
- }
- }
+ cur_time = globals->get_time_params()->get_cur_time();
+ msg_OK = (msg_time < cur_time);
+#if 0
+ if (msg_OK || _display != _prev_display) {
+ cout << "ATIS Update: " << _display << " " << _prev_display
+ << " len: " << transmission.length()
+ << " oldvol: " << old_volume
+ << " dt: " << dt << endl;
+ msg_time = cur_time;
+ }
+#endif
+ if(_display) {
+ double volume(0);
+ for (map<string,int>::iterator act = active_on.begin();
+ act != active_on.end(); act++) {
+ string prop = "/instrumentation/" + act->first + "/volume";
+ 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.
-void FGATIS::UpdateTransmission() {
- double visibility;
- char buf[10];
- int phonetic_id;
- string phonetic_id_string;
- string time_str = fgGetString("sim/time/gmt-string");
- int hours;
- // int minutes;
-
- FGEnvironment stationweather =
- ((FGEnvironmentMgr *)globals->get_subsystem("environment"))
- ->getEnvironment(lat, lon, 0.0);
-
- transmission = "";
-
- // UK CAA radiotelephony manual indicated ATIS transmissions start with "This is"
- // Not sure about rest of the world though.
- transmission += "This_is ";
- // transmitted station name.
- transmission += name;
- // Add "Information"
- transmission += " information";
-
- //cout << "In atis.cxx, time_str = " << time_str << '\n';
- // Add the recording identifier
- // For now we will assume we only transmit every hour
- hours = atoi((time_str.substr(1,2)).c_str()); //Warning - this is fragile if the
- //time string format changes
- //cout << "In atis.cxx, hours = " << hours << endl;
- phonetic_id = current_commlist->GetCallSign(ident, hours, 0);
- phonetic_id_string = GetPhoneticIdent(phonetic_id);
- transmission += " ";
- transmission += phonetic_id_string;
-
- // Output the recording time. - we'll just output the last whole hour for now.
- // FIXME - this only gets GMT time but that appears to be all the clock outputs for now
- //cout << "in atis.cxx, time = " << time_str << endl;
- transmission = transmission + " / Weather " + ConvertNumToSpokenDigits((time_str.substr(0,3) + "00")) + " hours zulu";
-
- // Get the temperature
- int temp;
- temp = (int)stationweather.get_temperature_degc();
-
- // HACK ALERT - at the moment the new environment subsystem returns bogus temperatures
- // FIXME - take out this hack when this gets fixed upstream
- if((temp < -50) || (temp > 60)) {
- temp = 15;
- }
-
- sprintf(buf, "%i", abs(temp));
- transmission += " / Temperature ";
- if(temp < 0) {
- transmission += "minus ";
- }
- string tempstr1 = buf;
- string tempstr2;
- transmission += ConvertNumToSpokenDigits(tempstr1);
- transmission += " degrees_Celsius";
-
- // Get the visibility
- visibility = stationweather.get_visibility_m();
- sprintf(buf, "%i", int(visibility/1600));
- transmission += " / Visibility ";
- tempstr1 = buf;
- transmission += ConvertNumToSpokenDigits(tempstr1);
- transmission += " miles";
-
- // Get the cloudbase
- // FIXME: kludge for now
- if (strcmp(fgGetString("/environment/clouds/layer[0]/type"), "clear")) {
- double cloudbase =
- fgGetDouble("/environment/clouds/layer[0]/elevation-ft");
- // For some reason the altitude returned doesn't seem to correspond to the actual cloud altitude.
- char buf3[10];
- char buf4[10];
- // cout << "cloudbase = " << cloudbase << endl;
- sprintf(buf3, "%i", int(cloudbase)/1000);
- sprintf(buf4, "%i", ((int)cloudbase % 1000)/100);
- transmission += " / Cloudbase";
- if(int(cloudbase)/1000) {
- tempstr1 = buf3;
- transmission = transmission + " " + ConvertNumToSpokenDigits(tempstr1) + " thousand";
- }
- if(((int)cloudbase % 1000)/100) {
- tempstr1 = buf4;
- transmission = transmission + " " + ConvertNumToSpokenDigits(tempstr1) + " hundred";
- }
- transmission += " feet";
- }
-
- // Get the pressure / altimeter
- double P = fgGetDouble("/environment/pressure-sea-level-inhg");
- if(ident.substr(0,2) == "EG" && fgGetBool("/sim/atc/use-millibars") == true) {
- // Convert to millibars for the UK!
- P *= 33.864;
- sprintf(buf, "%.0f", P);
- } else {
- sprintf(buf, "%.2f", P);
- }
- transmission += " / Altimeter ";
- tempstr1 = buf;
- transmission += ConvertNumToSpokenDigits(tempstr1);
-
- // Based on the airport-id and wind get the active runway
- //FGRunway *r;
- double speed = stationweather.get_wind_speed_kt();
- double hdg = stationweather.get_wind_from_heading_deg();
- if (speed == 0) {
- hdg = 270; // This forces West-facing rwys to be used in no-wind situations
- // which is consistent with Flightgear's initial setup.
- transmission += " / Winds_light_and_variable";
- } else {
- // FIXME: get gust factor in somehow
- char buf5[10];
- char buf6[10];
- sprintf(buf5, "%i", int(speed));
- sprintf(buf6, "%i", int(hdg));
- tempstr1 = buf5;
- tempstr2 = buf6;
- transmission = transmission + " / Winds " + ConvertNumToSpokenDigits(tempstr1) + " knots from "
- + ConvertNumToSpokenDigits(tempstr2) + " degrees";
- }
-
- const FGAirport* apt = fgFindAirportID(ident);
- assert(apt);
- string rwy_no = apt->getActiveRunwayForUsage()->ident();
- if(rwy_no != "NN") {
- transmission += " / Landing_and_departing_runway ";
- transmission += ConvertRwyNumToSpokenString(atoi(rwy_no.c_str()));
- //cout << "in atis.cxx, r.rwy_no = " << rwy_no << " r.id = " << r->id << " r.heading = " << r->heading << endl;
- }
-
- // Anything else?
-
- transmission += " / Advise_controller_on_initial_contact_you_have ";
- transmission += phonetic_id_string;
- transmission += " /// ";
+string uppercase(const string &s) {
+ string rslt(s);
+ for(string::iterator p = rslt.begin(); p != rslt.end(); p++){
+ *p = toupper(*p);
+ }
+ return rslt;
+}
+
+// Replace all occurrences of a given word.
+// Words in the original string must be separated by hyphens (not spaces).
+// We check for the word as given, and for the all-caps version thereof.
+string replace_word(const string _orig, const string _www, const string _nnn){
+// The following are so we can match words at the beginning
+// and end of the string.
+ string orig = "-" + _orig + "-";
+ string www = "-" + _www + "-";
+ string nnn = "-" + _nnn + "-";
+
+ size_t where(0);
+ for ( ; (where = orig.find(www, where)) != string::npos ; ) {
+ orig.replace(where, www.length(), nnn);
+ where += nnn.length();
+ }
+
+ www = uppercase(www);
+ for ( ; (where = orig.find(www, where)) != string::npos ; ) {
+ orig.replace(where, www.length(), nnn);
+ where += nnn.length();
+ }
+ where = orig.length();
+ return orig.substr(1, where-2);
+}
+
+// Normally the interval is 1 hour,
+// but you can shorten it for testing.
+const int minute(60); // measured in seconds
+#ifdef ATIS_TEST
+ const int ATIS_interval(2*minute);
+#else
+ const int ATIS_interval(60*minute);
+#endif
+
+// Generate the actual broadcast ATIS transmission.
+// Regen means regenerate the /current/ transmission.
+// Special means generate a new transmission, with a new sequence.
+// Returns 1 if we actually generated something.
+int FGATIS::GenTransmission(const int regen, const int special) {
+ using namespace atmodel;
+
+ string BRK = ".\n";
+
+ double tstamp = atof(fgGetString("sim/time/elapsed-sec"));
+ int interval = ATIS ? ATIS_interval : 2*minute; // AWOS updated frequently
+ int sequence = current_commlist->GetAtisSequence(ident,
+ tstamp, interval, special);
+ if (!regen && sequence > LTRS) {
+//xx if (msg_OK) cout << "ATIS: no change: " << sequence << endl;
+//xx msg_time = cur_time;
+ return 0; // no change since last time
+ }
+
+ const int bs(100);
+ char buf[bs];
+ string time_str = fgGetString("sim/time/gmt-string");
+ string hours, mins;
+ string phonetic_seq_string;
+
+ transmission = "";
+
+// UK CAA radiotelephony manual indicated ATIS transmissions start
+// with "This is ..."
+// In the US they just start with the airport name.
+// transmission += "This_is ";
+
+ // SG_LOG(SG_ATC, SG_ALERT, "ATIS: facility name: " << name);
+
+// Note that at this point, multi-word facility names
+// will sometimes contain hyphens, not spaces.
+// Force the issue, just to be safe:
+
+ string name2 = name;
+ for(string::iterator p = name2.begin(); p != name2.end(); p++){
+ if (*p == ' ') *p = '-';
+ }
+
+///////////////
+// FIXME: This would be more flexible and more extensible
+// if the mappings were taken from an XML file, not hard-coded.
+///////////////
+
+// Remap some abbreviations that occur in apt.dat, to
+// make things nicer for the text-to-speech system:
+ name2 = replace_word(name2, "Intl", "International");
+ name2 = replace_word(name2, "Rgnl", "Regional");
+ name2 = replace_word(name2, "Co", "County");
+ name2 = replace_word(name2, "Muni", "Municipal");
+ name2 = replace_word(name2, "Mem", "Memorial");
+ name2 = replace_word(name2, "Fld", "Field");
+ name2 = replace_word(name2, "AFB", "Air-Force-Base");
+ name2 = replace_word(name2, "AAF", "Army-Air-Field");
+ name2 = replace_word(name2, "MCAS", "Marine-Corps-Air-Station");
+ transmission += name2 + " ";
+ if (_type == ATIS /* as opposed to AWOS */) {
+ transmission += "airport_information ";
+ phonetic_seq_string = GetPhoneticLetter(sequence); // Add the sequence letter
+ transmission += phonetic_seq_string + BRK;
+ }
+ transmission += "Automated_weather_observation ";
+// Warning - this is fragile if the time string format changes
+ hours = time_str.substr(0,2).c_str();
+ mins = time_str.substr(3,2).c_str();
+// speak each digit separately:
+ transmission += ConvertNumToSpokenDigits(hours + mins);
+ transmission += " zulu weather" + BRK;
+
+ transmission += "Wind: ";
+
+ double wind_speed = fgGetDouble("/environment/config/boundary/entry[0]/wind-speed-kt");
+ double wind_dir = fgGetDouble("/environment/config/boundary/entry[0]/wind-from-heading-deg");
+ while (wind_dir <= 0) wind_dir += 360;
+// The following isn't as bad a kludge as it might seem.
+// 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
+// be receiving the ATIS signal, this should be a good-enough
+// approximation. For more-distant aircraft, the wind_dir
+// shouldn't be corrected anyway.
+// The less-kludgy approach would be to use the magvar associated
+// with the station, but that is not tabulated in the stationweather
+// structure as it stands, and computing it would be expensive.
+// 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.
+ wind_dir -= fgGetDouble("/environment/magnetic-variation-deg"); // wind_dir now magnetic
+ if (wind_speed == 0) {
+// Force west-facing rwys to be used in no-wind situations
+// which is consistent with Flightgear's initial setup:
+ 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;
}
#ifndef _FG_ATIS_HXX
#define _FG_ATIS_HXX
-#include <stdio.h>
#include <string>
+#include <iosfwd>
#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 <iosfwd>
-
#include "ATC.hxx"
//DCL - a complete guess for now.
#define FG_ATIS_DEFAULT_RANGE 30
+
class FGATIS : public FGATC {
//atc_type type;
// for failure modeling
std::string trans_ident; // transmitted ident
+ double old_volume;
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
// 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
FGATIS(void);
~FGATIS(void);
-
+ virtual void Init();
+ void attend (int);
+
//run the ATIS instance
void Update(double dt);
std::string refname; // Holds the refname of a transmission in progress
- //Update the transmission string
- void UpdateTransmission(void);
+ int GenTransmission(const int regen,
+ const int special); // Generate the transmission string
friend std::istream& operator>> ( std::istream&, FGATIS& );
};
+typedef int (FGATIS::*int_getter)() const;
+
#endif // _FG_ATIS_HXX
# include <config.h>
#endif
+#include "commlist.hxx"
+
#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/bucket/newbucket.hxx>
#include <Airports/simple.hxx>
-#include "commlist.hxx"
#include "ATCutils.hxx"
// Constructor
FGCommList::FGCommList( void ) {
+ sg_srandom_time();
}
// load the navaids and build the map
bool FGCommList::init( const SGPath& path ) {
- SGPath temp = path;
+ SGPath temp = path;
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" );
- LoadComms(temp);
- temp = path;
- temp.append( "ATC/default.tower" );
- LoadComms(temp);
- temp = path;
- temp.append( "ATC/default.ground" );
- LoadComms(temp);
- temp = path;
- temp.append( "ATC/default.approach" );
- LoadComms(temp);
- return true;
+ LoadComms(temp);
+ temp = path;
+ temp.append( "ATC/default.tower" );
+ LoadComms(temp);
+ temp = path;
+ temp.append( "ATC/default.ground" );
+ LoadComms(temp);
+ temp = path;
+ temp.append( "ATC/default.approach" );
+ LoadComms(temp);
+ return true;
}
-
+
bool FGCommList::LoadComms(const SGPath& path) {
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
exit(-1);
}
-
+
// read in each line of the file
fin >> skipcomment;
while ( !fin.eof() ) {
ATCData a;
- fin >> a;
- if(a.type == INVALID) {
- SG_LOG(SG_GENERAL, SG_DEBUG, "WARNING - INVALID type found in " << path.str() << '\n');
- } else {
- // Push all stations onto frequency map
- commlist_freq[a.freq].push_back(a);
-
- // Push non-atis stations onto bucket map as well
- // In fact, push all stations onto bucket map for now so FGATCMgr::GetFrequency() works.
- //if(a.type != ATIS) {
- // get bucket number
- SGBucket bucket(a.lon, a.lat);
- int bucknum = bucket.gen_index();
- commlist_bck[bucknum].push_back(a);
- //}
- }
-
- fin >> skipcomment;
- }
-
- fin.close();
- return true;
+ fin >> a;
+ if(a.type == INVALID) {
+ SG_LOG(SG_GENERAL, SG_DEBUG, "WARNING - INVALID type found in " << path.str() << '\n');
+ } else {
+ // Push all stations onto frequency map
+ commlist_freq[a.freq].push_back(a);
+
+ // Push non-atis stations onto bucket map as well
+ // In fact, push all stations onto bucket map for now so FGATCMgr::GetFrequency() works.
+ //if(a.type != ATIS and/or AWOS?) {
+ // get bucket number
+ SGBucket bucket(a.geod);
+ int bucknum = bucket.gen_index();
+ commlist_bck[bucknum].push_back(a);
+ //}
+ }
+
+ fin >> skipcomment;
+ }
+
+ fin.close();
+ return true;
}
// degrees, elev is in meters
// 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
-bool FGCommList::FindByFreq( double lon, double lat, double elev, double freq,
+bool FGCommList::FindByFreq(const SGGeod& aPos, double freq,
ATCData* ad, atc_type tp )
{
- // HACK - if freq > 1000 assume it's in KHz, otherwise assume MHz.
- // A bit ugly but it works for now!!!!
- comm_list_type stations;
- if(freq > 1000.0) {
- stations = commlist_freq[(int)freq];
- } else {
- stations = commlist_freq[(int)(freq*100.0 + 0.5)];
- }
- comm_list_iterator current = stations.begin();
- comm_list_iterator last = stations.end();
-
- // double az1, az2, s;
- SGVec3d aircraft = SGVec3d::fromGeod(SGGeod::fromDegM(lon, lat, elev));
- const double orig_max_d = 1e100;
- double max_d = orig_max_d;
- // TODO - at the moment this loop returns the first match found in range
- // We want to return the closest match in the event of a frequency conflict
- for ( ; current != last ; ++current ) {
- //cout << "testing " << current->get_ident() << endl;
- SGVec3d station(current->x, current->y, current->z);
- //cout << "aircraft = " << aircraft << endl;
- //cout << "station = " << station << endl;
-
- double d = distSqr(aircraft, station);
-
- //cout << " dist = " << sqrt(d)
- // << " range = " << current->range * SG_NM_TO_METER << endl;
-
- // 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((tp == INVALID) || (tp == (*current).type)) {
- if(d < max_d) {
- max_d = d;
- *ad = *current;
- }
- }
- }
- }
-
- if(max_d < orig_max_d) {
- return true;
- } else {
- return false;
- }
+ comm_list_type stations;
+ stations = commlist_freq[kHz10(freq)];
+ comm_list_iterator current = stations.begin();
+ comm_list_iterator last = stations.end();
+
+ // double az1, az2, s;
+ SGVec3d aircraft = SGVec3d::fromGeod(aPos);
+ const double orig_max_d = 1e100;
+ double max_d = orig_max_d;
+ double d;
+ // TODO - at the moment this loop returns the first match found in range
+ // We want to return the closest match in the event of a frequency conflict
+ for ( ; current != last ; ++current ) {
+ d = distSqr(aircraft, current->cart);
+
+ //cout << " dist = " << sqrt(d)
+ // << " range = " << current->range * SG_NM_TO_METER << endl;
+
+ // 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((tp == INVALID) || (tp == (*current).type)) {
+ if(d < max_d) {
+ max_d = d;
+ *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
- int found = 0;
- stations->erase(stations->begin(), stations->end());
-
- // get bucket number for plane position
- SGBucket buck(lon, lat);
-
- // get neigboring buckets
- 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 );
-
- // loop over bucket range
- for ( int i=-bx; i<=bx; i++) {
- for ( int j=-by; j<=by; j++) {
- buck = sgBucketOffset(lon, lat, i, j);
- long int bucket = buck.gen_index();
- comm_list_type Fstations = commlist_bck[bucket];
- comm_list_iterator current = Fstations.begin();
- comm_list_iterator last = Fstations.end();
-
- // double az1, az2, s;
- SGVec3d aircraft = SGVec3d::fromGeod(SGGeod::fromDegM(lon, lat, elev));
- for(; current != last; ++current) {
- if((current->type == tp) || (tp == INVALID)) {
- SGVec3d station(current->x, current->y, current->z);
- double d = distSqr(aircraft, station);
- // 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 ) ) {
- stations->push_back(*current);
- ++found;
- }
- }
- }
- }
- }
- return found;
+ // number of relevant stations found within range
+ int found = 0;
+ stations->erase(stations->begin(), stations->end());
+
+ // get bucket number for plane position
+ SGBucket buck(aPos);
+
+ // get neigboring buckets
+ 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 );
+
+ // loop over bucket range
+ for ( int i=-bx; i<=bx; i++) {
+ for ( int j=-by; j<=by; j++) {
+ buck = sgBucketOffset(aPos.getLongitudeDeg(), aPos.getLatitudeDeg(), i, j);
+ long int bucket = buck.gen_index();
+ comm_list_type Fstations = commlist_bck[bucket];
+ comm_list_iterator current = Fstations.begin();
+ comm_list_iterator last = Fstations.end();
+
+
+ // double az1, az2, s;
+ SGVec3d aircraft = SGVec3d::fromGeod(aPos);
+ double d;
+ for(; current != last; ++current) {
+ if((current->type == tp) || (tp == INVALID)) {
+ d = distSqr(aircraft, current->cart);
+ // 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 ) ) {
+ stations->push_back(*current);
+ ++found;
+ }
+ }
+ }
+ }
+ }
+ return found;
}
// 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
// and giving up after 1000.
-double FGCommList::FindClosest( double lon, double lat, double elev, ATCData& ad, atc_type tp, double max_range) {
- int num_stations = 0;
- int range = 10;
- comm_list_type stations;
- comm_list_iterator itr;
- double distance = -9999.0;
-
- while(num_stations == 0) {
- num_stations = FindByPos(lon, lat, elev, range, &stations, tp);
- if(num_stations) {
- double closest = max_range * SG_NM_TO_METER;
- double tmp;
- for(itr = stations.begin(); itr != stations.end(); ++itr) {
- ATCData ad2 = *itr;
- SGGeod p1 = SGGeod::fromDegM(ad2.lon, ad2.lat, ad2.elev);
- const FGAirport *a = fgFindAirportID(ad2.ident);
- if (a) {
- SGGeod p2 = SGGeod::fromDegM(lon, lat, elev);
- tmp = dclGetHorizontalSeparation(p1, p2);
- if(tmp <= closest) {
- closest = tmp;
- distance = tmp;
- ad = *itr;
- }
- }
- }
- //cout << "Closest station is " << ad.ident << " at a range of " << distance << " meters\n";
- return(distance);
- }
- if(range > max_range) {
- break;
- }
- range *= 10;
- }
- return(-9999.0);
+double FGCommList::FindClosest(const SGGeod& aPos, ATCData& ad, atc_type tp, double max_range) {
+ int num_stations = 0;
+ int range = 10;
+ comm_list_type stations;
+ comm_list_iterator itr;
+ double distance = -9999.0;
+
+ while(num_stations == 0) {
+ num_stations = FindByPos(aPos, range, &stations, tp);
+ if(num_stations) {
+ double closest = max_range * SG_NM_TO_METER;
+ double tmp;
+ for(itr = stations.begin(); itr != stations.end(); ++itr) {
+ ATCData ad2 = *itr;
+ const FGAirport *a = fgFindAirportID(ad2.ident);
+ if (a) {
+ tmp = dclGetHorizontalSeparation(ad2.geod, aPos);
+ if(tmp <= closest) {
+ closest = tmp;
+ distance = tmp;
+ ad = *itr;
+ }
+ }
+ }
+ //cout << "Closest station is " << ad.ident << " at a range of " << distance << " meters\n";
+ return(distance);
+ }
+ if(range > max_range) {
+ break;
+ }
+ range *= 10;
+ }
+ return(-9999.0);
}
bool FGCommList::FindByCode( const string& ICAO, ATCData& ad, atc_type tp ) {
const FGAirport *a = fgFindAirportID( ICAO);
if ( a) {
- comm_list_type stations;
- int found = FindByPos(a->getLongitude(), a->getLatitude(), a->getElevation(), 10.0, &stations, tp);
- if(found) {
- comm_list_iterator itr = stations.begin();
- while(itr != stations.end()) {
- if(((*itr).ident == ICAO) && ((*itr).type == tp)) {
- ad = *itr;
- return true;
- }
- ++itr;
- }
- }
- } else {
- return false;
+ comm_list_type stations;
+ int found = FindByPos(a->geod(), 10.0, &stations, tp);
+ if(found) {
+ comm_list_iterator itr = stations.begin();
+ while(itr != stations.end()) {
+ if(((*itr).ident == ICAO) && ((*itr).type == tp)) {
+ ad = *itr;
+ //cout << "FindByCode returns " << ICAO
+ // << " type: " << tp
+ // << " freq: " << itr->freq
+ // << endl;
+ return true;
+ }
+ ++itr;
+ }
+ }
}
- return false;
+ return false;
}
-
// TODO - this function should move somewhere else eventually!
-// Return an appropriate call-sign for an ATIS transmission.
-int FGCommList::GetCallSign( const string& apt_id, int hours, int mins )
+// Return an appropriate sequence number for an ATIS transmission.
+// 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;
-
- if(atislog.find(apt_id) == atislog.end()) {
- // This station has not transmitted yet - return a random identifier
- // and add the transmission to the log
- tran.hours = hours;
- tran.mins = mins;
- sg_srandom_time();
- tran.callsign = int(sg_random() * 25) + 1; // This *should* give a random int between 1 and 26
- //atislog[apt_id].push_back(tran);
- atislog[apt_id] = tran;
- } else {
- // This station has transmitted - calculate the appropriate identifier
- // and add the transmission to the log if it has changed
- tran = atislog[apt_id];
- // This next bit assumes that no-one comes back to the same ATIS station
- // after running FlightGear for more than 24 hours !!
- if((tran.hours == hours) && (tran.mins == mins)) {
- return(tran.callsign);
- } else {
- if(tran.hours == hours) {
- // The minutes must have changed
- 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);
+ atis_transmission_type tran;
+
+ if(atislog.find(apt_id) == atislog.end()) { // New station
+ tran.tstamp = tstamp - interval;
+// Random number between 0 and 25 inclusive, i.e. 26 equiprobable outcomes:
+ tran.sequence = int(sg_random() * LTRS);
+ atislog[apt_id] = tran;
+ //cout << "New ATIS station: " << apt_id << " seq-1: "
+ // << tran.sequence << endl;
+ }
+
+// calculate the appropriate identifier and update the log
+ tran = atislog[apt_id];
+
+ int delta = int((tstamp - tran.tstamp) / interval);
+ tran.tstamp += delta * interval;
+ if (special && !delta) delta++; // a "special" ATIS update is required
+ tran.sequence = (tran.sequence + delta) % LTRS;
+ atislog[apt_id] = tran;
+ //if (delta) cout << "New ATIS sequence: " << tran.sequence
+ // << " Delta: " << delta << endl;
+ return(tran.sequence + (delta ? 0 : LTRS*1000));
}
#include <simgear/compiler.h>
-#include <simgear/misc/sg_path.hxx>
#include <map>
#include <list>
#include "ATC.hxx"
#include "atis.hxx"
-using std::list;
-using std::map;
-using std::vector;
-using std::string;
+class SGPath;
// 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::const_iterator comm_list_const_iterator;
// 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::const_iterator comm_map_const_iterator;
class FGCommList {
-
+
public:
FGCommList();
bool init( const SGPath& path );
// 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 atc_type is specifed, returns true only if the specified type is found.
- // Returns the station closest to the supplied position.
- // The data found is written into the passed-in ATCData structure.
- bool FindByFreq( double lon, double lat, double elev, double freq, ATCData* ad, atc_type tp = INVALID );
-
+ // 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.
+ // Returns the station closest to the supplied position.
+ // The data found is written into the passed-in ATCData structure.
+ bool FindByFreq(const SGGeod& aPos, 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.
- // 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.
- // If no atc_type is specifed, returns the number of all stations in range, and pushes them into stations
- // ** stations is erased before use **
- int FindByPos( double lon, double lat, double elev, double range, comm_list_type* stations, atc_type tp = INVALID );
-
- // 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
- // 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).
- // 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
- // and giving up after 1000.
- // !!!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).
- double FindClosest( double lon, double lat, double elev, ATCData& ad, atc_type tp = INVALID, double max_range = 100.0 );
-
- // Find by Airport code.
- bool FindByCode( const string& ICAO, ATCData& ad, atc_type tp = INVALID );
-
- // Return the callsign for an ATIS transmission given transmission time and airport id
- // This maybe should get moved somewhere else!!
- int GetCallSign( const string& apt_id, int hours, int mins );
-
+ // 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.
+ // If no atc_type is specifed, returns the number of all stations in range, and pushes them into stations
+ // ** stations is erased before use **
+ 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,
+ // with the details written into ATCData& ad. If no type is specifed simply
+ // 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).
+ // 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
+ // and giving up after 1000.
+ // !!!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).
+ double FindClosest(const SGGeod& aPos, ATCData& ad, atc_type tp = INVALID, double max_range = 100.0 );
+
+ // Find by Airport code.
+ bool FindByCode( const std::string& ICAO, ATCData& ad, atc_type tp = INVALID );
+
+ // Return the sequence letter for an ATIS transmission given transmission time and airport id
+ // This maybe should get moved somewhere else!!
+ 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:
-
- // 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
- // Add structure and map for storing a log of atis transmissions
- // made in this session of FlightGear. This allows the callsign
- // to be allocated correctly wrt time.
- typedef struct {
- int hours;
- int mins;
- int callsign;
- } atis_transmission_type;
-
- typedef map < string, atis_transmission_type > atis_log_type;
+ // Add structure and map for storing a log of atis transmissions
+ // made in this session of FlightGear. This allows the callsign
+ // to be allocated correctly wrt time.
+ typedef struct {
+ double tstamp;
+ int sequence;
+ } atis_transmission_type;
+
+ typedef std::map < std::string, atis_transmission_type > atis_log_type;
typedef atis_log_type::iterator atis_log_iterator;
typedef atis_log_type::const_iterator atis_log_const_iterator;
extern FGCommList *current_commlist;
-
#endif // _FG_COMMLIST_HXX
+
+
//cout << "Dep list, checking " << t->plane.callsign;
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()));
- else distout = dclGetHorizontalSeparation(SGGeod::fromDegM(lon, lat, elev), t->planePtr->getPos());
+ if(t->isUser) distout = dclGetHorizontalSeparation(_geod,
+ 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';
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)) {
// Only call this at startup - reading the runways database is expensive and needs to be fixed!
bool FGTower::OnAnyRunway(const SGGeod& pt, bool onGround) {
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) {
return(false);
}
else if ( strcmp ( tag, "@MI" ) == 0 ) {
char buf[10];
//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);
strcat( &dum[0], &buf[0] );
}
}
}
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 > 360.0) h -= 360.0;
if(h < 22.5 || h > 337.5) {
}
string FGTower::GetATISID() {
- int hours = fgGetInt("/sim/time/utc/hour");
- int phonetic_id = current_commlist->GetCallSign(ident, hours, 0);
- return GetPhoneticIdent(phonetic_id);
+ double tstamp = atof(fgGetString("sim/time/elapsed-sec"));
+ const int minute(60); // in SI units
+ 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) {
#include <iosfwd>
#include <string>
+#include <list>
#include "ATC.hxx"
#include "ATCProjection.hxx"
# include <config.h>
#endif
+#include "apt_loader.hxx"
+
#include <simgear/compiler.h>
#include <stdlib.h> // atof(), atoi()
#include "simple.hxx"
#include "runways.hxx"
#include "pavement.hxx"
+#include <ATCDCL/commlist.hxx>
-#include "apt_loader.hxx"
+#include <iostream>
+
+using namespace std;
static FGPositioned::Type fptypeFromRobinType(int aType)
{
class APTLoader
{
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 );
+
if ( !in.is_open() ) {
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << aptdb_file );
exit(-1);
// custom startup locations (ignore)
} else if ( line_id == 0 ) {
// ??
- } else if ( line_id >= 50 && line_id <= 56 ) {
- // frequency entries (ignore)
+ } else if ( line_id == 50 ) {
+ parseATISLine(comm_list, simgear::strutils::split(line));
+ } else if ( line_id >= 51 && line_id <= 56 ) {
+ // other frequency entries (ignore)
} else if ( line_id == 110 ) {
pavement = true;
parsePavementLine850(simgear::strutils::split(line, 0, 4));
}
private:
+ vector<string> token;
double rwy_lat_accum;
double rwy_lon_accum;
double last_rwy_heading;
string last_apt_name;
double last_apt_elev;
SGGeod tower;
- int last_apt_type;
+ string last_apt_info;
+ string last_apt_type;
string pavement_ident;
bool pavement;
SGGeod pos(SGGeod::fromDegFt(lon, lat, last_apt_elev));
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);
}
last_apt_name += " ";
}
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
rwy_lon_accum = 0.0;
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
// metar file is used to mark the airports as having metar available
// 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;
- ld.parseAPT(aptdb_file);
+
+ ld.parseAPT(aptdb_file, comm_list);
//
// Load the metar.dat file and update apt db with stations that
#include <string>
-#include "simple.hxx"
-
+// forward decls
+class FGCommList;
// Load the airport data base from the specified aptdb file. The
// metar file is used to mark the airports as having metar available
// 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
<< " and " << temperature_sea_level_degc
<< " :: " << this
<< " # " << count);
- ///////////////////////////////////raise(SIGUSR1);
}
}
#endif
#include "renderer.hxx"
#include "viewmgr.hxx"
#include "main.hxx"
+#include "ATCDCL/commlist.hxx"
#ifdef __APPLE__
# include <CoreFoundation/CoreFoundation.h>
SGPath p_metar( globals->get_fg_root() );
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 *loclist = new FGNavList;