]> git.mxchange.org Git - flightgear.git/commitdiff
ATIS overhaul by John Denker, adapted to trunk by me.
authorjmt <jmt>
Fri, 18 Sep 2009 15:27:19 +0000 (15:27 +0000)
committerTim Moore <timoore@redhat.com>
Fri, 18 Sep 2009 18:18:54 +0000 (20:18 +0200)
8:: AWOS is available at AWOS locations.  (Previously only ATIS was
   implemented.)

 9:: ATIS phraseology now more nearly conforms to international
   standard METAR pattern, and therefore to usual FAA practice.(*)

Items marked with a (*) are fully implemented in the /text/ of the
ATIS message, but the voiced version of the message is degraded by
limitations of the FGFS built-in text-to-speech system.

 10:: ATIS now reports sky condition.(*)

 11:: ATIS now reports multiple layers of clouds, not just the lowest
 layer.(*)

 12:: ATIS now takes field elevation into account when calculating
   sky condition and ceiling.

 13:: ATIS now reports dewpoint.(*)

 14:: ATIS now can handle negative quantities (temperature and dewpoint).(*)

 15:: ATIS can now report report fractional-mile visibility.(*)

 16:: ATIS now uses magnetic (not true) wind directions, as it should.

 17:: ATIS generates correct runway number and suffix (nine right,
   one one left).

 18:: ATIS can be received on nav frequencies, not just comm.

 19:: Nothing bad happens if the same ATIS is tuned up on more than
   one receiver.

 20:: ATIS can be updated at times other than at the top of the hour.

 21:: ATIS listens for an "attention" signal, and responds to changes
   in the weather by issuing a new ATIS message (somewhat like a
   "special observation").

 22:: ATIS volume now responds to radio volume setting.

 23:: Area-related services (i.e. approach radar) are handled
   more-nearly consistently with radio-frequency related services.

 24:: ATIS sequence-letter generation has been fixed.

 25:: ATIS messages are now in the property tree, so they can be read
   e.g. via the http interface.

26 files changed:
src/ATCDCL/AILocalTraffic.cxx
src/ATCDCL/AIMgr.cxx
src/ATCDCL/AIMgr.hxx
src/ATCDCL/AIPlane.cxx
src/ATCDCL/AIPlane.hxx
src/ATCDCL/ATC.cxx
src/ATCDCL/ATC.hxx
src/ATCDCL/ATCDialog.cxx
src/ATCDCL/ATCDialog.hxx
src/ATCDCL/ATCVoice.cxx
src/ATCDCL/ATCVoice.hxx
src/ATCDCL/ATCmgr.cxx
src/ATCDCL/ATCmgr.hxx
src/ATCDCL/ATCutils.cxx
src/ATCDCL/ATCutils.hxx
src/ATCDCL/approach.cxx
src/ATCDCL/atis.cxx
src/ATCDCL/atis.hxx
src/ATCDCL/commlist.cxx
src/ATCDCL/commlist.hxx
src/ATCDCL/tower.cxx
src/ATCDCL/tower.hxx
src/Airports/apt_loader.cxx
src/Airports/apt_loader.hxx
src/Environment/environment.cxx
src/Main/fg_init.cxx

index e38e4314970c35a6d0847674625b9ee5665aa6bb..2acf7d10dd612f9c027b3be771431d8ca50fc794 100644 (file)
@@ -546,6 +546,7 @@ void FGAILocalTraffic::Update(double dt) {
                // And to avoid compiler warnings...
                case APPROACH:  break;
                case ATIS:      break;
+    case AWOS:      break;
                case ENROUTE:   break;
                case DEPARTURE: break;
                case INVALID:   break;
index 073acd7e77a4c89fd8030f8b53df5c4313dca537..8b6c448bc8d63720d59e64509a01bd174018815e 100644 (file)
@@ -79,10 +79,6 @@ void FGAIMgr::init() {
        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!!
@@ -167,7 +163,7 @@ void FGAIMgr::update(double dt) {
        
        //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;
@@ -475,10 +471,9 @@ void FGAIMgr::SearchByPos(double range) {
        //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);
@@ -492,7 +487,7 @@ void FGAIMgr::SearchByPos(double range) {
                //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()) {
@@ -530,14 +525,14 @@ void FGAIMgr::SearchByPos(double range) {
        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;
@@ -601,7 +596,7 @@ string FGAIMgr::GenerateShortForm(const string& callsign, const string& plane_st
                //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 += '-';
        }
index d08d6f56662794c2f5516efd05fdfea8f6fcd96d..8d87c5c981482bebb1c247d9a8e61649b0c849ff 100644 (file)
@@ -81,15 +81,12 @@ private:
        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();
index f3bf43b8c2e8a6618ccead66178b330455844642..1c394d638a8c2c1b0373a42183a6a4c477ca4a85 100644 (file)
@@ -105,7 +105,9 @@ void FGAIPlane::Update(double dt) {
                        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.
@@ -165,7 +167,9 @@ void FGAIPlane::ConditionalTransmit(double timeout, int callback_code) {
 }
 
 void FGAIPlane::ImmediateTransmit(int callback_code) {
-       Render(plane.callsign, false);
+      // TODO - at the moment the volume is always set off comm1 
+        double volume = fgGetDouble("/instrumentation/comm[0]/volume");
+       Render(plane.callsign, volume, false);
        if(callback_code) {
                ProcessCallback(callback_code);
        }
@@ -179,26 +183,25 @@ void FGAIPlane::ProcessCallback(int code) {
 // Outputs the transmission either on screen or as audio depending on user preference
 // 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) {
index 489ec1793de2d6e39a9232d38f8bc8f9bac35785..e1f581c6c7e25eeb6fb1c7d0044310606dd1da4c 100644 (file)
@@ -140,7 +140,7 @@ private:
        // 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 "".
index 2bc321805619481bed8f0698fdeb42cdfd700693..193b824572c102dd0e9cb6ac111ebe3cb9c9f7bc 100644 (file)
 #  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';
@@ -200,12 +203,9 @@ int FGATC::RemovePlane() {
 }
 
 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;
@@ -216,7 +216,8 @@ void FGATC::SetData(ATCData* d) {
 // 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
@@ -225,26 +226,29 @@ void FGATC::Render(string& msg, const string& refname, bool repeating) {
        #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) {
@@ -279,13 +283,73 @@ string FGATC::GenText(const string& m, int c) {
 
 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;
+}
+
index 0a6d55a8b3e7e9c189954433a0812252fcab5d6e..adb62264ec896dad68dad2932a3633e504af758b 100644 (file)
 #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,
@@ -61,28 +69,24 @@ struct PlaneRec {
 // 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;
@@ -104,12 +108,14 @@ struct RunwayDetails {
 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.
@@ -154,19 +160,7 @@ public:
        
        // 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; }
@@ -182,7 +176,8 @@ protected:
        // 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 "".
@@ -199,13 +194,15 @@ protected:
        
        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
@@ -213,28 +210,32 @@ protected:
        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
@@ -243,67 +244,6 @@ private:
        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
index e356cee3f47aa10002c81747c563e582e10b4ee4..f094b1170cc82893a419028af4a11e132cd890ce 100644 (file)
@@ -66,7 +66,7 @@ ATCMenuEntry::ATCMenuEntry() {
 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]);
        }
@@ -318,21 +318,19 @@ void FGATCDialog::FreqDialog() {
        // 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>)
@@ -382,7 +380,7 @@ void FGATCDialog::FreqDisplay(string& ident) {
        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();
index ea393c907546223645a089e7cf5d1a2705e2657d..b60fa599a1c4dfa20a6550902894c7a18857020a 100644 (file)
@@ -55,8 +55,9 @@ typedef atcmentry_vec_type::iterator atcmentry_vec_iterator;
 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 {
index e4b05b75c0ff93cdd47f0989e47d08494ca18119..05f24daac4934b828bb3db79221b377d45f8451e 100644 (file)
 #  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;
@@ -47,20 +52,24 @@ FGATCVoice::~FGATCVoice() {
 // Load the two voice files - one containing the raw sound data (.wav) and one containing the word positions (.vce).
 // 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);
@@ -93,6 +102,13 @@ bool FGATCVoice::LoadVoice(const string& voice) {
                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';
        }
@@ -105,8 +121,9 @@ bool FGATCVoice::LoadVoice(const string& voice) {
 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.
@@ -117,28 +134,36 @@ unsigned char* FGATCVoice::WriteMessage(char* message, int& len, bool& dataOK) {
 
        // TODO - at the moment we're effectively taking 3 passes through the data.
        // 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;
@@ -151,13 +176,10 @@ unsigned char* FGATCVoice::WriteMessage(char* message, int& len, bool& dataOK) {
        // 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) {
                /*
@@ -170,11 +192,9 @@ unsigned char* FGATCVoice::WriteMessage(char* message, int& len, bool& dataOK) {
                        SG_LOG(SG_ATC, SG_ALERT, "ERROR - mismatch between ATC .wav and .vce file in ATCVoice.cxx\n");
                        SG_LOG(SG_ATC, SG_ALERT, "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;
@@ -183,12 +203,10 @@ unsigned char* FGATCVoice::WriteMessage(char* message, int& len, bool& dataOK) {
        // tmpbuf now contains the message starting at the beginning - but we want it to start at a random position.
        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;
 }
index bdd2b0a491002a6dd5b46893f57818cc2ef3a42f..5da9768481fdf5a9b0f2d7502422abc03980614f 100644 (file)
 
 #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
@@ -57,8 +50,7 @@ public:
        
        // 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:
 
index bbf99dfa620f2965f2796213d4b67ddbca58e6ff..0d78c458bb3c73742cb0ed8d86bfa4069ceb3fd4 100644 (file)
@@ -24,6 +24,7 @@
 
 #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),
@@ -53,34 +51,20 @@ AirportATC::AirportATC() :
     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() {
@@ -90,313 +74,243 @@ void FGATCMgr::unbind() {
 }
 
 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
+    }
+  }
 }
 
 
@@ -404,28 +318,19 @@ void FGATCMgr::RemoveFromList(const string& id, const atc_type& tp) {
 // 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);
+    }
 }
 
 
@@ -434,73 +339,73 @@ bool FGATCMgr::GetAirportATCDetails(const string& icao, AirportATC* a) {
 // 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
@@ -511,213 +416,148 @@ FGATC* FGATCMgr::GetATCPointer(const string& icao, const atc_type& type) {
 // 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
index 9ac6a75d97558d30ef88c173f3759162d4cccf05..13349490db63ed1286f507b0f832347dbd9b0570 100644 (file)
@@ -45,9 +45,7 @@ using std::map;
 struct AirportATC {
        AirportATC();
        
-    float lon;
-    float lat;
-    float elev;
+    SGGeod geod;
     float atis_freq;
     bool atis_active;
     float tower_freq;
@@ -65,7 +63,7 @@ struct AirportATC {
     // 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
@@ -84,47 +82,26 @@ private:
     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;
 
@@ -170,10 +147,10 @@ public:
        // 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
@@ -189,11 +166,7 @@ private:
 
     // 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) 
@@ -201,11 +174,13 @@ private:
        // - *** 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
 
 };
 
index 94eaff246e4d5615f3c45794c116892a5bf47f1c..fe484f25674ca285017f8ef05a0cba989207b755 100644 (file)
 #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) {
@@ -59,101 +68,50 @@ 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
index b26a55cd0b0b01c22ed9be353e060a55e25ab0ce..01f2e9e9d93ea0fade3c04a7ed86d33128c38b4e 100644 (file)
@@ -41,24 +41,22 @@ using std::string;
 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...)
index ed396b0d986966e0c8886138c1e5882fd6818441..e1ff8226e212b2000314f790ee1efba05ff3cb6e 100644 (file)
@@ -408,18 +408,19 @@ double FGApproach::angle_diff_deg( const double &a1, const double &a2) {
 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;
@@ -506,7 +507,7 @@ void FGApproach::calc_wp( const int &i ) {
        // ====================
        // 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];
@@ -585,11 +586,11 @@ void FGApproach::update_plane_dat() {
     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;
index d3be4ea23bfbe8fb9b42aa937fa2eec05ffe4b3a..e0555697722173e18ab45bc6217083df913bf737 100644 (file)
 // 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;
 }
index 60335e18561d5d810bfcf14a01a5f8a360bae4f2..aeafe33fb4cd2758e53c19f39ee2fd250090ca03 100644 (file)
 #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;
@@ -48,8 +44,15 @@ class FGATIS : public FGATC {
        
        // 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
@@ -63,7 +66,9 @@ class FGATIS : public FGATC {
        
        FGATIS(void);
        ~FGATIS(void);
-       
+       virtual void Init();
+       void attend (int);
+
        //run the ATIS instance
        void Update(double dt);
        
@@ -75,10 +80,12 @@ class FGATIS : public FGATC {
        
        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
index 140b3f2da0f487e9b589cbd491b39479ef41631d..baed76f373bea946e0e8d81858a2699dcf3dcd1a 100644 (file)
 #  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"
 
 
@@ -40,6 +42,7 @@ FGCommList *current_commlist;
 
 // Constructor
 FGCommList::FGCommList( void ) {
+      sg_srandom_time();
 }
 
 
@@ -51,23 +54,23 @@ FGCommList::~FGCommList( void ) {
 // 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) {
 
@@ -76,34 +79,34 @@ 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;    
 }
 
 
@@ -111,98 +114,89 @@ bool FGCommList::LoadComms(const SGPath& path) {
 // 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;
 }
 
 
@@ -213,41 +207,39 @@ int FGCommList::FindByPos(double lon, double lat, double elev, double range, com
 // Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if
 // 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);
 }
 
 
@@ -257,72 +249,53 @@ double FGCommList::FindClosest( double lon, double lat, double elev, ATCData& ad
 bool FGCommList::FindByCode( const string& ICAO, ATCData& ad, atc_type tp ) {
     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));
 }
index e253e6e98c60feea16ff59c32e72a8440de89eb2..c5066f3e9f0f24ff1ead49afbcf448d5fcac5be9 100644 (file)
@@ -35,7 +35,6 @@
 
 
 #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();
@@ -71,60 +67,59 @@ public:
     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;
 
@@ -136,5 +131,6 @@ private:
 
 extern FGCommList *current_commlist;
 
-
 #endif // _FG_COMMLIST_HXX
+
+
index 1b92eaa1614fb3b868ced1d00c58f30d0ef2e3ff..d1886c3389691a8ca6226f9df68ab6db253d115b 100644 (file)
@@ -1409,8 +1409,9 @@ void FGTower::CheckDepartureList(double dt) {
                //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)) {
@@ -1563,7 +1564,7 @@ bool FGTower::OnActiveRunway(const SGGeod& pt) {
 // Only call this at startup - reading the runways database is expensive and needs to be fixed!
 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);
        }
@@ -2540,7 +2541,7 @@ string FGTower::GenText(const string& m, int c) {
                                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] );
                                }
@@ -2560,7 +2561,7 @@ string FGTower::GenText(const string& m, int c) {
                                        }
                                }
                                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) {
@@ -2625,9 +2626,13 @@ string FGTower::GetWeather() {
 }
 
 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) {
index 6a08f27c83d45d11cf881190332f5ba4d89f1ea9..a09536993f1df57c16ef2b6e6a50ef79da89ed56 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <iosfwd>
 #include <string>
+#include <list>
 
 #include "ATC.hxx"
 #include "ATCProjection.hxx"
index a8af8ca4792d50e720006081f2985762173b31e1..0cb01f3bb4d6c32945f568a1b452d4973f6cedb8 100644 (file)
@@ -26,6 +26,8 @@
 #  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)
 {
@@ -61,9 +66,19 @@ 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);
@@ -139,8 +154,10 @@ public:
           // 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));
@@ -166,6 +183,7 @@ public:
   }
   
 private:
+  vector<string> token;
   double rwy_lat_accum;
   double rwy_lon_accum;
   double last_rwy_heading;
@@ -175,7 +193,8 @@ private:
   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;
   
@@ -209,7 +228,7 @@ private:
 
     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);
   }
@@ -232,7 +251,7 @@ private:
         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;
@@ -433,15 +452,62 @@ private:
       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
index af2d46aeba47c0547e9817da4b3b6295e77b2e31..54596e8d85bf37e07a36547ecc09acc68686a721 100644 (file)
 
 #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
index f915e526194fc088905ba444263fa8ce6b3c94fb..f6238c415e91cac1245d3cdfa5fea29231004078 100644 (file)
@@ -708,7 +708,6 @@ FGEnvironment::_recalc_alt_pt ()
         << "  and "  << temperature_sea_level_degc
         << " :: " << this
         << "  # " << count);
-        ///////////////////////////////////raise(SIGUSR1);
     }
   }
 #endif
index 953e4a254a24c29c8cc0385b7cb49f81d22c6588..ab5742ac407e225eac966bae6feda0a433af78dd 100644 (file)
 #include "renderer.hxx"
 #include "viewmgr.hxx"
 #include "main.hxx"
+#include "ATCDCL/commlist.hxx"
 
 #ifdef __APPLE__
 #  include <CoreFoundation/CoreFoundation.h>
@@ -968,7 +969,11 @@ fgInitNav ()
     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;