+ // dew point
+ double dpsl = fgGetDouble("/environment/dewpoint-sea-level-degc");
+ _report.dewpoint = int(SGMiscd::round(FGAtmo().fake_dp_vs_a_us(dpsl, _geod.getElevationFt())));
+
+ // precipitation
+ _report.rain_norm = fgGetDouble("environment/rain-norm");
+ _report.snow_norm = fgGetDouble("environment/snow-norm");
+
+ // NOTAMs
+ _report.notam = 0;
+ if (fgGetBool("/sim/atis/random-notams", true))
+ {
+ _report.notam = fgGetInt("/sim/atis/notam-id", 0); // fixed NOTAM for testing/debugging only
+ if (!_report.notam)
+ {
+ // select pseudo-random NOTAM (changes every hour, differs for each airport)
+ char cksum = 0;
+ string name = apt->getName();
+ for(string::iterator p = name.begin(); p != name.end(); p++)
+ {
+ cksum += *p;
+ }
+ cksum ^= atoi(_report.hours.c_str());
+ _report.notam = cksum % 12; // 12 intentionally higher than number of available NOTAMs, so they don't appear too often
+ // debugging
+ //fgSetInt("/sim/atis/selected-notam", _report.notam);
+ }
+ }
+}
+
+void FGATIS::genPrecipitationInfo(void)
+{
+ using namespace lex;
+
+ double rain_norm = _report.rain_norm;
+ double snow_norm = _report.snow_norm;
+
+ // report rain or snow - which ever is worse
+ if (rain_norm > 0.7)
+ transmission += heavy + " " + rain + BRK;
+ else
+ if (snow_norm > 0.7)
+ transmission += heavy + " " + snow + BRK;
+ else
+ if (rain_norm > 0.4)
+ transmission += moderate + " " + rain + BRK;
+ else
+ if (snow_norm > 0.4)
+ transmission += moderate + " " + snow + BRK;
+ else
+ if (rain_norm > 0.2)
+ transmission += light + " " + rain + BRK;
+ else
+ if (snow_norm > 0.05)
+ transmission += light + " " + snow + BRK;
+ else
+ if (rain_norm > 0.05)
+ transmission += light + " " + drizzle + BRK;
+}
+
+void FGATIS::genTimeInfo(void)
+{
+ using namespace lex;
+
+ if (!_report.concise)
+ transmission += Time + " ";
+
+ // speak each digit separately:
+ transmission += ConvertNumToSpokenDigits(_report.hours + _report.mins);
+ transmission += " " + zulu + BRK;
+}
+
+bool FGATIS::genVisibilityInfo(string& vis_info)
+{
+ using namespace lex;
+
+ double visibility = fgGetDouble("/environment/config/boundary/entry[0]/visibility-m");
+ bool IsMax = false;
+ bool USE_KM = !_report.US_CA;
+
+ vis_info += Visibility + ": ";
+ if (USE_KM)
+ {
+ visibility /= 1000.0; // convert to statute miles
+ // integer kilometers
+ if (visibility >= 9.5)
+ {
+ visibility = 10;
+ IsMax = true;
+ }
+ snprintf(buf, sizeof(buf), "%i", int(.5 + visibility));
+ // "kelometers" instead of "kilometers" since the festival language generator doesn't get it right otherwise
+ vis_info += ConvertNumToSpokenDigits(buf) + " " + kelometers;
+ }
+ else
+ {
+ visibility /= atmodel::sm; // convert to statute miles
+ if (visibility < 0.25) {
+ vis_info += less_than_one_quarter;
+ } else if (visibility < 0.5) {
+ vis_info += one_quarter;
+ } else if (visibility < 0.75) {
+ vis_info += one_half;
+ } else if (visibility < 1.0) {
+ vis_info += three_quarters;
+ } else if (visibility >= 1.5 && visibility < 2.0) {
+ vis_info += one_and_one_half;
+ } else {
+ // integer miles
+ if (visibility > 9.5)
+ {
+ visibility = 10;
+ IsMax = true;
+ }
+ snprintf(buf, sizeof(buf), "%i", int(.5 + visibility));
+ vis_info += ConvertNumToSpokenDigits(buf);
+ }
+ }
+ if (IsMax)
+ {
+ vis_info += " " + or_more;
+ }
+ vis_info += BRK;
+ return !IsMax;
+}
+
+void FGATIS::addTemperature(int Temp)
+{
+ if (Temp < 0)
+ transmission += lex::minus + " ";
+ else
+ if (Temp > 0)
+ {
+ transmission += lex::plus + " ";
+ }
+ snprintf(buf, sizeof(buf), "%i", abs(Temp));
+ transmission += ConvertNumToSpokenDigits(buf);
+ if (_report.US_CA)
+ transmission += " " + lex::Celsius;
+}
+
+void FGATIS::genTemperatureInfo()
+{
+ // temperature
+ transmission += lex::Temperature + ": ";
+ addTemperature(_report.temp);
+
+ // dewpoint
+ transmission += BRK + lex::Dewpoint + ": ";
+ addTemperature(_report.dewpoint);
+
+ transmission += BRK;
+}
+
+bool FGATIS::genCloudInfo(string& cloud_info)
+{
+ using namespace lex;
+
+ bool did_some = false;
+ bool did_ceiling = false;
+
+ for (int layer = 0; layer <= 4; layer++) {
+ snprintf(buf, sizeof(buf), "/environment/clouds/layer[%i]/coverage", layer);
+ string coverage = fgGetString(buf);
+ if (coverage == clear)
+ continue;
+ snprintf(buf, sizeof(buf), "/environment/clouds/layer[%i]/thickness-ft", layer);
+ if (fgGetDouble(buf) == 0)
+ continue;
+ snprintf(buf, sizeof(buf), "/environment/clouds/layer[%i]/elevation-ft", layer);
+ double ceiling = int(fgGetDouble(buf) - _geod.getElevationFt());
+ if (ceiling > 12000)
+ continue;
+
+ // BEWARE: At the present time, the environment system has no
+ // way (so far as I know) to represent a "thin broken" or
+ // "thin overcast" layer. If/when such things are implemented
+ // in the environment system, code will have to be written here
+ // to handle them.
+
+ // First, do the prefix if any:
+ if (coverage == scattered || coverage == few) {
+ if (!did_some) {
+ if (_report.concise)
+ cloud_info += Clouds + ": ";
+ else
+ cloud_info += Sky_condition + ": ";
+ did_some = true;
+ }
+ } else /* must be a ceiling */ if (!did_ceiling) {
+ cloud_info += " " + Ceiling + ": ";
+ did_ceiling = true;
+ did_some = true;
+ } else {
+ cloud_info += " "; // no prefix required
+ }
+ int cig00 = int(SGMiscd::round(ceiling/100)); // hundreds of feet
+ if (cig00) {
+ int cig000 = cig00/10;
+ cig00 -= cig000*10; // just the hundreds digit
+ if (cig000) {
+ snprintf(buf, sizeof(buf), "%i", cig000);
+ cloud_info += ConvertNumToSpokenDigits(buf);
+ cloud_info += " " + thousand + " ";
+ }
+ if (cig00) {
+ snprintf(buf, sizeof(buf), "%i", cig00);
+ cloud_info += ConvertNumToSpokenDigits(buf);
+ cloud_info += " " + hundred + " ";
+ }
+ } else {
+ // Should this be "sky obscured?"
+ cloud_info += " " + zero + " "; // not "zero hundred"
+ }
+ cloud_info += coverage + BRK;
+ }
+ if (!did_some)
+ cloud_info += " " + Sky + " " + clear + BRK;
+ return did_some;
+}
+
+void FGATIS::genFacilityInfo(void)
+{
+ if ((!_report.US_CA)&&(!_report.concise))
+ {
+ // UK CAA radiotelephony manual indicates ATIS transmissions start
+ // with "This is ...", while US just starts with airport name.
+ transmission += lex::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.
+ 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:
+ for (MSS::const_iterator replace = _remap.begin();
+ replace != _remap.end(); replace++) {
+ // Due to inconsistent capitalisation in the apt.dat file, we need
+ // to do a case-insensitive comparison here.
+ string tmp1 = word, tmp2 = replace->first;
+ boost::algorithm::to_lower(tmp1);
+ boost::algorithm::to_lower(tmp2);
+ if (tmp1 == tmp2) {
+ word = replace->second;
+ break;
+ }