From 9a987a3709271eceb56bdd3c92e62750ed58a4a9 Mon Sep 17 00:00:00 2001
From: Dave Luff <daveluff@ntlworld.com>
Date: Thu, 30 Dec 2010 13:27:57 +0000
Subject: [PATCH] ATIS fixes/refactoring from John Denker

Some fixes, and refactoring of the lexicon and abbreviation remapping
into headers where it can be picked up by the scripts that generate
the voice files.
---
 src/ATCDCL/atis.cxx         | 116 ++++++++++++++++++++----------------
 src/ATCDCL/atis.hxx         |   2 +
 src/ATCDCL/atis_lexicon.hxx |  59 ++++++++++++++++++
 src/ATCDCL/atis_remap.hxx   |  15 +++++
 4 files changed, 142 insertions(+), 50 deletions(-)
 create mode 100644 src/ATCDCL/atis_lexicon.hxx
 create mode 100644 src/ATCDCL/atis_remap.hxx

diff --git a/src/ATCDCL/atis.cxx b/src/ATCDCL/atis.cxx
index e5bda8b3d..357ff18a2 100644
--- a/src/ATCDCL/atis.cxx
+++ b/src/ATCDCL/atis.cxx
@@ -31,6 +31,7 @@
 #endif
 
 #include "atis.hxx"
+#include "atis_lexicon.hxx"
 
 #include <simgear/compiler.h>
 
@@ -40,6 +41,7 @@
 #include <iostream>
 
 #include <boost/tuple/tuple.hpp>
+#include <boost/algorithm/string.hpp>
 
 #include <simgear/misc/sg_path.hxx>
 
@@ -77,6 +79,23 @@ FGATIS::FGATIS() :
        SG_LOG(SG_ATC, SG_ALERT, "ERROR - _type not ATIS or AWOS in atis.cxx");
   }
   fgTie("/environment/attention", this, (int_getter)0, &FGATIS::attend);
+
+///////////////
+// FIXME:  This would be more flexible and more extensible
+// if the mappings were taken from an XML file, not hard-coded ...
+// ... although having it in a .hxx file is better than nothing.
+//
+// Load the remap list from the .hxx file:
+  using namespace lex;
+# define NIL ""
+# define REMAP(from,to) _remap[#from] = to;
+# include <atis_remap.hxx>
+# undef REMAP
+# undef NIL
+
+#ifdef ATIS_TEST
+  SG_LOG(SG_ATC, SG_ALERT, "ATIS initialized");
+#endif
 }
 
 // Hint:
@@ -192,6 +211,7 @@ const int minute(60);		// measured in seconds
 // Returns 1 if we actually generated something.
 int FGATIS::GenTransmission(const int regen, const int special) {
   using namespace atmodel;
+  using namespace lex;
 
   string BRK = ".\n";
 
@@ -216,7 +236,7 @@ int FGATIS::GenTransmission(const int regen, const int special) {
   if (ident.substr(0,2) == "EG") {
 // UK CAA radiotelephony manual indicates ATIS transmissions start
 // with "This is ..." 
-    transmission += "This_is + ";
+    transmission += This_is + " ";
   } else {
     // In the US they just start with the airport name.
   }
@@ -225,34 +245,29 @@ int FGATIS::GenTransmission(const int regen, const int special) {
 
 // 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.
-///////////////
+  
+  vector<string> name_words;
+  boost::split(name_words, name, boost::is_any_of(" -"));
 
+  for (vector<string>::const_iterator wordp = name_words.begin();
+                wordp != name_words.end(); wordp++) {
+    string word(*wordp);
 // 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 + " ";
+    for (MSS::const_iterator replace = _remap.begin();
+          replace != _remap.end(); replace++) {
+      if (word == replace->first) {
+        word = replace->second;
+        break;
+      }
+    }
+    transmission += word + " ";
+  }
+
   if (_type == ATIS /* as opposed to AWOS */) {
-    transmission += "airport_information ";
+    transmission += airport_information + " ";
   } else {
-    transmission += "Automated_weather_observation ";
+    transmission += Automated_weather_observation + " ";
   }
 
   phonetic_seq_string = GetPhoneticLetter(sequence);  // Add the sequence letter
@@ -263,9 +278,9 @@ int FGATIS::GenTransmission(const int regen, const int special) {
   mins  = time_str.substr(3,2).c_str();
 // speak each digit separately:
   transmission += ConvertNumToSpokenDigits(hours + mins);
-  transmission += " zulu weather" + BRK;
+  transmission += " " + zulu_weather + BRK;
 
-  transmission += "Wind: ";
+  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");
@@ -288,14 +303,14 @@ int FGATIS::GenTransmission(const int regen, const int special) {
 // 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";
+      transmission += " " + light_and_variable;
   } else {
       // FIXME: get gust factor in somehow
       snprintf(buf, bs, "%03.0f", 5*SGMiscd::round(wind_dir/5));
       transmission += ConvertNumToSpokenDigits(buf);
 
       snprintf(buf, bs, "%1.0f", wind_speed);
-      transmission += " at " + ConvertNumToSpokenDigits(buf) + BRK;
+      transmission += " " + at + " " + ConvertNumToSpokenDigits(buf) + BRK;
   }
 
   int did_some(0);
@@ -304,17 +319,17 @@ int FGATIS::GenTransmission(const int regen, const int special) {
   for (int layer = 0; layer <= 4; layer++) {
     snprintf(buf, bs, "/environment/clouds/layer[%i]/coverage", layer);
     string coverage = fgGetString(buf);
-    if (coverage == "clear") continue;
+    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: ";
+    if (coverage == scattered) {
+      if (!did_some) transmission += "   " + Sky_condition + ": ";
       did_some++;
     } else /* must be a ceiling */  if (!did_ceiling) {
-      transmission += "   Ceiling: ";
+      transmission += "   " + Ceiling + ": ";
       did_ceiling++;
       did_some++;
     } else {
@@ -327,51 +342,52 @@ int FGATIS::GenTransmission(const int regen, const int special) {
       if (cig000) {
     snprintf(buf, bs, "%i", cig000);
     transmission += ConvertNumToSpokenDigits(buf);
-    transmission += " thousand ";
+    transmission += " " + thousand + " ";
       }
       if (cig00) {
     snprintf(buf, bs, "%i", cig00);
     transmission += ConvertNumToSpokenDigits(buf);
-    transmission += " hundred ";
+    transmission += " " + hundred + " ";
       }
     } else {
       // Should this be "sky obscured?"
-      transmission += " zero ";     // not "zero hundred"
+      transmission += " " + zero + " ";     // not "zero hundred"
     }
     transmission += coverage + BRK;
   }
 
-  transmission += "Temperature: ";
+  transmission += Temperature + ": ";
   double Tsl = fgGetDouble("/environment/temperature-sea-level-degc");
   int temp = int(SGMiscd::round(FGAtmo().fake_T_vs_a_us(_geod.getElevationFt(), Tsl)));
   if(temp < 0) {
-      transmission += "minus ";
+      transmission += lex::minus + " ";
   }
   snprintf(buf, bs, "%i", abs(temp));
   transmission += ConvertNumToSpokenDigits(buf);
-  transmission += " dewpoint ";
+  transmission += " " + Celsius;
+  transmission += " " + dewpoint + " ";
   double dpsl = fgGetDouble("/environment/dewpoint-sea-level-degc");
   temp = int(SGMiscd::round(FGAtmo().fake_dp_vs_a_us(dpsl, _geod.getElevationFt())));
   if(temp < 0) {
-      transmission += "minus ";
+      transmission += lex::minus + " ";
   }
   snprintf(buf, bs, "%i", abs(temp));
-  transmission += ConvertNumToSpokenDigits(buf) + BRK;
+  transmission += ConvertNumToSpokenDigits(buf);
+  transmission += " " + Celsius + BRK;
 
-
-  transmission += "Visibility: ";
+  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";
+    transmission += less_than_one_quarter;
   } else if (visibility < 0.5) {
-    transmission += "one quarter";
+    transmission += one_quarter;
   } else if (visibility < 0.75) {
-    transmission += "one half";
+    transmission += one_half;
   } else if (visibility < 1.0) {
-    transmission += "three quarters";
+    transmission += three_quarters;
   } else if (visibility >= 1.5 && visibility < 2.0) {
-    transmission += "one and one half";
+    transmission += one_and_one_half;
   } else {
     // integer miles
     if (visibility > 10) visibility = 10;
@@ -380,7 +396,7 @@ int FGATIS::GenTransmission(const int regen, const int special) {
   }
   transmission += BRK;
 
-  transmission += "Altimeter: ";
+  transmission += Altimeter + ": ";
   double myQNH;
   double Psl = fgGetDouble("/environment/pressure-sea-level-inhg");
   {
@@ -412,7 +428,7 @@ int FGATIS::GenTransmission(const int regen, const int special) {
       assert(apt);
         string rwy_no = apt->getActiveRunwayForUsage()->ident();
       if(rwy_no != "NN") {
-        transmission += "Landing_and_departing_runway ";
+        transmission += Landing_and_departing_runway + " ";
         transmission += ConvertRwyNumToSpokenString(rwy_no) + BRK;
         if (msg_OK) {
           msg_time = cur_time;
@@ -420,7 +436,7 @@ int FGATIS::GenTransmission(const int regen, const int special) {
           //   << " wind_dir: " << wind_dir << endl;
         }
     }
-    transmission += "On_initial_contact_advise_you_have_information ";
+    transmission += On_initial_contact_advise_you_have_information + " ";
     transmission += phonetic_seq_string;
     transmission += "... " + BRK;
   }
diff --git a/src/ATCDCL/atis.hxx b/src/ATCDCL/atis.hxx
index aeafe33fb..cd35ea476 100644
--- a/src/ATCDCL/atis.hxx
+++ b/src/ATCDCL/atis.hxx
@@ -34,6 +34,7 @@
 //DCL - a complete guess for now.
 #define FG_ATIS_DEFAULT_RANGE 30
 	
+typedef std::map<std::string,std::string> MSS;
 
 class FGATIS : public FGATC {
 	
@@ -52,6 +53,7 @@ class FGATIS : public FGATC {
 	int attention;
 	
 	bool _prev_display;		// Previous value of _display flag
+	MSS _remap;                     // abbreviations to be expanded
 
 	// Aircraft position
 	// ATIS is actually a special case in that unlike other ATC eg.tower it doesn't actually know about
diff --git a/src/ATCDCL/atis_lexicon.hxx b/src/ATCDCL/atis_lexicon.hxx
new file mode 100644
index 000000000..9d4300d0b
--- /dev/null
+++ b/src/ATCDCL/atis_lexicon.hxx
@@ -0,0 +1,59 @@
+#ifndef _FG_ATIS_LEXICON_HXX
+#define _FG_ATIS_LEXICON_HXX
+
+#include <string>
+
+
+#define Q(word) const std::string word(#word);
+
+namespace lex {
+Q(Airport)
+Q(Airfield)
+Q(Airbase)
+Q(Junior)
+Q(Celsius)
+Q(wind)
+Q(zulu_weather)
+Q(Automated_weather_observation)
+Q(weather)
+Q(airport_information)
+Q(International)
+Q(Regional)
+Q(County)
+Q(Municipal)
+Q(Memorial)
+Q(Field)
+Q(Air_Force_Base)
+Q(Army_Air_Field)
+Q(Marine_Corps_Air_Station)
+Q(light_and_variable)
+Q(at)
+Q(thousand)
+Q(hundred)
+Q(zero)
+Q(Temperature)
+Q(clear)
+Q(scattered)
+Q(broken)
+Q(overcast)
+Q(Sky_condition)
+Q(Ceiling)
+Q(minus)
+Q(dewpoint)
+Q(Visibility)
+Q(less_than_one_quarter)
+Q(one_quarter)
+Q(one_half)
+Q(three_quarters)
+Q(one_and_one_half)
+Q(Altimeter)
+Q(Landing_and_departing_runway)
+Q(On_initial_contact_advise_you_have_information)
+Q(This_is)
+Q(left)
+Q(right)
+Q(center)
+}
+
+#undef Q
+#endif // _FG_ATIS_LEXICON_HXX
diff --git a/src/ATCDCL/atis_remap.hxx b/src/ATCDCL/atis_remap.hxx
new file mode 100644
index 000000000..f679b70b9
--- /dev/null
+++ b/src/ATCDCL/atis_remap.hxx
@@ -0,0 +1,15 @@
+REMAP(Intl,  International)
+REMAP(Rgnl,  Regional)
+REMAP(Co,    County)
+REMAP(Muni,  Municipal)
+REMAP(Mem,   Memorial)
+REMAP(Apt,   Airport)
+REMAP(Arpt,  Airport)
+REMAP(Fld,   Field)
+REMAP(AFLD,  Airfield)
+REMAP(AFB,   Air_Force_Base)
+REMAP(AB,    Airbase)
+REMAP(AAF,   Army_Air_Field)
+REMAP(MCAS,  Marine_Corps_Air_Station)
+REMAP(JR,    Junior)
+REMAP(GKI,   NIL)