+ if (_type == ATIS) // as opposed to AWOS
+ genRunwayInfo(apt);
+
+ // some warnings may appear after runway info
+ genWarnings(0);
+
+ // transition level
+ genTransitionLevel(apt);
+
+ // weather
+ if (!_report.concise)
+ transmission += Weather + BRK;
+
+ genWindInfo();
+
+ // clouds and visibility
+ {
+ string vis_info, cloud_info;
+ bool v = genVisibilityInfo(vis_info);
+ bool c = genCloudInfo(cloud_info);
+ _report.cavok = !(v || c);
+ if (!_report.cavok)
+ {
+ // there is some visibility or cloud restriction
+ transmission += vis_info + cloud_info;
+ }
+ else
+ {
+ // Abbreviation CAVOK vs full "clouds and visibility..." does not really depend on
+ // US vs rest of the world, it really seems to depend on the airport. Just use
+ // it as a heuristic.
+ if ((_report.US_CA)||(_report.concise))
+ transmission += cav_ok + BRK;
+ else
+ transmission += clouds_and_visibility_OK + BRK;
+ }
+ }
+
+ // precipitation
+ genPrecipitationInfo();
+
+ // temperature
+ genTemperatureInfo();
+
+ // pressure
+ genPressureInfo();
+
+ // TODO check whether "no significant change" applies - somehow...
+ transmission += No_sig + BRK; // sounds better with festival than "nosig"
+
+ // some warnings may appear at the very end
+ genWarnings(1);
+
+ if ((!_report.concise)|| _report.US_CA)
+ transmission += Advise_on_initial_contact_you_have_information;
+ else
+ transmission += information;
+ transmission += " " + phonetic_seq_string + ".";
+
+ if (!_report.US_CA)
+ {
+ // non-US ATIS ends with "out!"
+ transmission += " " + out;
+ }
+
+ // Pause in between two messages must be 3-5 seconds
+ transmission += " / / / / / / / / ";
+
+ /////////////////////////////////////////////////////////
+ // post-processing
+ /////////////////////////////////////////////////////////
+ transmission_readable = transmission;
+
+ // Take the previous readable string and munge it to
+ // be relatively-more acceptable to the primitive tts system.
+ // Note that : ; and . are among the token-delimiters recognized
+ // by the tts system.
+ for (size_t where;;) {
+ where = transmission.find_first_of(":.");
+ if (where == string::npos) break;
+ transmission.replace(where, 1, PAUSE);
+ }
+
+ return true;
+}
+
+/** Collect (most of) the data and create report.
+ */
+void FGATIS::createReport(const FGAirport* apt)
+{
+ // check country
+ _report.US_CA = Apt_US_CA(ident);
+
+ // switch to enable brief ATIS message (really depends on the airport)
+ _report.concise = fgGetBool("/sim/atis/concise-reports", false);
+
+ _report.ils = false;
+
+ // time information
+ string time_str = fgGetString("sim/time/gmt-string");
+ // Warning - this is fragile if the time string format changes
+ _report.hours = time_str.substr(0,2).c_str();
+ _report.mins = time_str.substr(3,2).c_str();
+
+ // pressure/temperature
+ {
+ double press, temp;
+ double Tsl = fgGetDouble("/environment/temperature-sea-level-degc");
+ tie(press, temp) = PT_vs_hpt(_geod.getElevationM(), _report.psl*atmodel::inHg, Tsl + atmodel::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
+ _report.qnh = FGAtmo().QNH(_geod.getElevationM(), press);
+ _report.temp = int(SGMiscd::round(FGAtmo().fake_T_vs_a_us(_geod.getElevationFt(), Tsl)));
+ }
+
+ // 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));