ATIS upgrade
Add missing ATIS elements (transition level, expected approach, precipitation, runway surface warnings). Minor phraseology fixes for US/Europe. Adds "/sim/atis/concise-report" option to use abbreviations (CAVOK etc), and omit "obvious" units (depends on airport). Trigger ATIS updates for significant pressure changes.
This commit is contained in:
parent
3bf09215b0
commit
32b9c61528
4 changed files with 651 additions and 275 deletions
|
@ -2,6 +2,7 @@
|
||||||
// This is the implementation of the FGATIS class
|
// This is the implementation of the FGATIS class
|
||||||
//
|
//
|
||||||
// Written by David Luff, started October 2001.
|
// Written by David Luff, started October 2001.
|
||||||
|
// Extended by Thorsten Brehm, October 2012.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2001 David C Luff - david.luff@nottingham.ac.uk
|
// Copyright (C) 2001 David C Luff - david.luff@nottingham.ac.uk
|
||||||
//
|
//
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
#include <simgear/math/sg_random.h>
|
#include <simgear/math/sg_random.h>
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
|
#include <simgear/misc/strutils.hxx>
|
||||||
|
|
||||||
#include <stdlib.h> // atoi()
|
#include <stdlib.h> // atoi()
|
||||||
#include <stdio.h> // sprintf
|
#include <stdio.h> // sprintf
|
||||||
|
@ -56,6 +58,7 @@
|
||||||
#include <Airports/dynamics.hxx>
|
#include <Airports/dynamics.hxx>
|
||||||
|
|
||||||
#include <ATC/CommStation.hxx>
|
#include <ATC/CommStation.hxx>
|
||||||
|
#include <Navaids/navrecord.hxx>
|
||||||
|
|
||||||
#include "ATCutils.hxx"
|
#include "ATCutils.hxx"
|
||||||
#include "ATISmgr.hxx"
|
#include "ATISmgr.hxx"
|
||||||
|
@ -80,6 +83,7 @@ FGATIS::FGATIS(const std::string& name, int num) :
|
||||||
cur_time(0),
|
cur_time(0),
|
||||||
msg_OK(0),
|
msg_OK(0),
|
||||||
_attention(false),
|
_attention(false),
|
||||||
|
_check_transmission(true),
|
||||||
_prev_display(0),
|
_prev_display(0),
|
||||||
_time_before_search_sec(0),
|
_time_before_search_sec(0),
|
||||||
_last_frequency(0)
|
_last_frequency(0)
|
||||||
|
@ -117,15 +121,18 @@ FGATIS::FGATIS(const std::string& name, int num) :
|
||||||
//
|
//
|
||||||
// Load the remap list from the .hxx file:
|
// Load the remap list from the .hxx file:
|
||||||
using namespace lex;
|
using namespace lex;
|
||||||
# define NIL ""
|
|
||||||
# define REMAP(from,to) _remap[#from] = to;
|
|
||||||
# include "atis_remap.hxx"
|
|
||||||
# undef REMAP
|
|
||||||
# undef NIL
|
|
||||||
|
|
||||||
#ifdef ATIS_TEST
|
# 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");
|
SG_LOG(SG_ATC, SG_ALERT, "ATIS initialized");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
_report.psl = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hint:
|
// Hint:
|
||||||
|
@ -143,14 +150,22 @@ FGATCVoice* FGATIS::GetVoicePointer()
|
||||||
return pAtisMgr->GetVoicePointer(ATIS);
|
return pAtisMgr->GetVoicePointer(ATIS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGATIS::init() {
|
void FGATIS::init()
|
||||||
|
{
|
||||||
// Nothing to see here. Move along.
|
// Nothing to see here. Move along.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FGATIS::reinit()
|
||||||
|
{
|
||||||
|
_time_before_search_sec = 0;
|
||||||
|
_check_transmission = true;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
FGATIS::attend(SGPropertyNode* node)
|
FGATIS::attend(SGPropertyNode* node)
|
||||||
{
|
{
|
||||||
_attention = node->getBoolValue();
|
if (node->getBoolValue())
|
||||||
|
_attention = true;
|
||||||
#ifdef ATMO_TEST
|
#ifdef ATMO_TEST
|
||||||
int flag = fgGetInt("/sim/logging/atmo");
|
int flag = fgGetInt("/sim/logging/atmo");
|
||||||
if (flag) {
|
if (flag) {
|
||||||
|
@ -179,13 +194,11 @@ void FGATIS::update(double dt) {
|
||||||
double volume = 0;
|
double volume = 0;
|
||||||
if ((_electrical->getDoubleValue() > 8) && _serviceable->getBoolValue())
|
if ((_electrical->getDoubleValue() > 8) && _serviceable->getBoolValue())
|
||||||
{
|
{
|
||||||
_time_before_search_sec -= dt;
|
|
||||||
// radio is switched on and OK
|
// radio is switched on and OK
|
||||||
if (_operable.valid())
|
if (_operable.valid())
|
||||||
_operable->setBoolValue(true);
|
_operable->setBoolValue(true);
|
||||||
|
|
||||||
// Search the tuned frequencies
|
_check_transmission |= search(dt);
|
||||||
search();
|
|
||||||
|
|
||||||
if (_display)
|
if (_display)
|
||||||
{
|
{
|
||||||
|
@ -202,14 +215,21 @@ void FGATIS::update(double dt) {
|
||||||
|
|
||||||
if (volume > 0.05)
|
if (volume > 0.05)
|
||||||
{
|
{
|
||||||
|
bool changed = false;
|
||||||
|
if (_check_transmission)
|
||||||
|
{
|
||||||
|
_check_transmission = false;
|
||||||
// Check if we need to update the message
|
// Check if we need to update the message
|
||||||
// - basically every hour and if the weather changes significantly at the station
|
// - 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
|
// If !_prev_display, the radio had been detuned for a while and our
|
||||||
// "transmission" variable was lost when we were de-instantiated.
|
// "transmission" variable was lost when we were de-instantiated.
|
||||||
int changed = genTransmission(!_prev_display, _attention);
|
if (genTransmission(!_prev_display, _attention))
|
||||||
|
{
|
||||||
// update output property
|
// update output property
|
||||||
TreeOut(msg_OK);
|
treeOut(msg_OK);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (changed || volume != old_volume) {
|
if (changed || volume != old_volume) {
|
||||||
// audio output enabled
|
// audio output enabled
|
||||||
|
@ -227,14 +247,6 @@ void FGATIS::update(double dt) {
|
||||||
FGATC::update(dt);
|
FGATC::update(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
// Replace all occurrences of a given word.
|
||||||
// Words in the original string must be separated by hyphens (not spaces).
|
// 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.
|
// We check for the word as given, and for the all-caps version thereof.
|
||||||
|
@ -251,7 +263,7 @@ string replace_word(const string _orig, const string _www, const string _nnn){
|
||||||
where += nnn.length();
|
where += nnn.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
www = uppercase(www);
|
www = simgear::strutils::uppercase(www);
|
||||||
for ( ; (where = orig.find(www, where)) != string::npos ; ) {
|
for ( ; (where = orig.find(www, where)) != string::npos ; ) {
|
||||||
orig.replace(where, www.length(), nnn);
|
orig.replace(where, www.length(), nnn);
|
||||||
where += nnn.length();
|
where += nnn.length();
|
||||||
|
@ -278,9 +290,10 @@ const int minute(60); // measured in seconds
|
||||||
// ascertaining which airports are in the US, let alone
|
// ascertaining which airports are in the US, let alone
|
||||||
// (b) ascertaining which other places use inches.
|
// (b) ascertaining which other places use inches.
|
||||||
//
|
//
|
||||||
bool Apt_US_CA(const string id) {
|
bool Apt_US_CA(const string id)
|
||||||
// Assume all IDs have length 3 or 4.
|
{
|
||||||
// No counterexamples have been seen.
|
// Assume all IDs have length 3 or 4.
|
||||||
|
// No counterexamples have been seen.
|
||||||
if (id.length() == 4) {
|
if (id.length() == 4) {
|
||||||
if (id.substr(0,1) == "K") return true;
|
if (id.substr(0,1) == "K") return true;
|
||||||
if (id.substr(0,2) == "CY") return true;
|
if (id.substr(0,2) == "CY") return true;
|
||||||
|
@ -291,37 +304,40 @@ bool Apt_US_CA(const string id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static string BRK = ".\n";
|
// voice spacers
|
||||||
static string PAUSE = " / ";
|
static const string BRK = ".\n";
|
||||||
|
static const string PAUSE = " / ";
|
||||||
|
|
||||||
/** Generate the actual broadcast ATIS transmission.
|
/** Generate the actual broadcast ATIS transmission.
|
||||||
* 'regen' triggers a regeneration of the /current/ transmission.
|
* 'regen' triggers a regeneration of the /current/ transmission.
|
||||||
* 'special' generates a new transmission, with a new sequence.
|
* 'forceUpdate' generates a new transmission, with a new sequence.
|
||||||
* Returns 1 if we actually generated something.
|
* Returns 1 if we actually generated something.
|
||||||
*/
|
*/
|
||||||
int FGATIS::genTransmission(const int regen, const bool special) {
|
bool FGATIS::genTransmission(const int regen, bool forceUpdate)
|
||||||
using namespace atmodel;
|
{
|
||||||
using namespace lex;
|
using namespace lex;
|
||||||
|
|
||||||
// ATIS updated hourly, AWOS updated more frequently
|
// ATIS updated hourly, AWOS updated more frequently
|
||||||
int interval = _type == ATIS ? ATIS_interval : 2*minute;
|
int interval = _type == ATIS ? ATIS_interval : 2*minute;
|
||||||
|
|
||||||
|
// check if pressure has changed significantly and we need to update ATIS
|
||||||
|
double Psl = fgGetDouble("/environment/pressure-sea-level-inhg");
|
||||||
|
if (fabs(Psl-_report.psl) >= 0.15)
|
||||||
|
forceUpdate = true;
|
||||||
|
|
||||||
FGAirport* apt = FGAirport::findByIdent(ident);
|
FGAirport* apt = FGAirport::findByIdent(ident);
|
||||||
int sequence = apt->getDynamics()->updateAtisSequence(interval, special);
|
int sequence = apt->getDynamics()->updateAtisSequence(interval, forceUpdate);
|
||||||
if (!regen && sequence > LTRS) {
|
if (!regen && sequence > LTRS) {
|
||||||
//xx if (msg_OK) cout << "ATIS: no change: " << sequence << endl;
|
//xx if (msg_OK) cout << "ATIS: no change: " << sequence << endl;
|
||||||
//xx msg_time = cur_time;
|
//xx msg_time = cur_time;
|
||||||
return 0; // no change since last time
|
return false; // no change since last time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_report.psl = Psl;
|
||||||
transmission = "";
|
transmission = "";
|
||||||
|
|
||||||
bool US_CA = Apt_US_CA(ident);
|
// collect data and create report
|
||||||
if (!US_CA) {
|
createReport(apt);
|
||||||
// UK CAA radiotelephony manual indicates ATIS transmissions start
|
|
||||||
// with "This is ...", while US just starts with airport name.
|
|
||||||
transmission += This_is + " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
// add facility name
|
// add facility name
|
||||||
genFacilityInfo();
|
genFacilityInfo();
|
||||||
|
@ -339,29 +355,79 @@ int FGATIS::genTransmission(const int regen, const bool special) {
|
||||||
|
|
||||||
genTimeInfo();
|
genTimeInfo();
|
||||||
|
|
||||||
|
// some warnings may appear at the beginning
|
||||||
|
genWarnings(-1);
|
||||||
|
|
||||||
|
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();
|
genWindInfo();
|
||||||
|
|
||||||
// Sounds better with a pause in there:
|
// clouds and visibility
|
||||||
transmission += PAUSE;
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
genCloudInfo();
|
// precipitation
|
||||||
|
genPrecipitationInfo();
|
||||||
|
|
||||||
double Tsl;
|
// temperature
|
||||||
genTemperatureInfo(Tsl, US_CA);
|
genTemperatureInfo();
|
||||||
|
|
||||||
genVisibilityInfo();
|
// pressure
|
||||||
|
genPressureInfo();
|
||||||
|
|
||||||
genPressureInfo(US_CA, Tsl);
|
// TODO check whether "no significant change" applies - somehow...
|
||||||
|
transmission += No_sig + BRK; // sounds better with festival than "nosig"
|
||||||
|
|
||||||
if (_type == ATIS /* as opposed to AWOS */) {
|
// some warnings may appear at the very end
|
||||||
genRunwayInfo(phonetic_seq_string);
|
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
|
// Pause in between two messages must be 3-5 seconds
|
||||||
transmission += PAUSE + PAUSE + PAUSE + PAUSE;
|
transmission += " / / / / / / / / ";
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
// postprocessing
|
// post-processing
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
transmission_readable = transmission;
|
transmission_readable = transmission;
|
||||||
|
|
||||||
|
@ -375,103 +441,215 @@ int FGATIS::genTransmission(const int regen, const bool special) {
|
||||||
transmission.replace(where, 1, PAUSE);
|
transmission.replace(where, 1, PAUSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
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)
|
void FGATIS::genTimeInfo(void)
|
||||||
{
|
{
|
||||||
using namespace atmodel;
|
|
||||||
using namespace lex;
|
using namespace lex;
|
||||||
|
|
||||||
string hours, mins;
|
if (!_report.concise)
|
||||||
string time_str = fgGetString("sim/time/gmt-string");
|
transmission += Time + " ";
|
||||||
|
|
||||||
// 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:
|
// speak each digit separately:
|
||||||
transmission += ConvertNumToSpokenDigits(hours + mins);
|
transmission += ConvertNumToSpokenDigits(_report.hours + _report.mins);
|
||||||
transmission += " " + zulu + " " + weather + BRK;
|
transmission += " " + zulu + BRK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGATIS::genVisibilityInfo(void)
|
bool FGATIS::genVisibilityInfo(string& vis_info)
|
||||||
{
|
{
|
||||||
using namespace atmodel;
|
|
||||||
using namespace lex;
|
using namespace lex;
|
||||||
const int bs(100);
|
|
||||||
char buf[bs];
|
|
||||||
|
|
||||||
transmission += Visibility + ": ";
|
|
||||||
double visibility = fgGetDouble("/environment/config/boundary/entry[0]/visibility-m");
|
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
|
visibility /= atmodel::sm; // convert to statute miles
|
||||||
if (visibility < 0.25) {
|
if (visibility < 0.25) {
|
||||||
transmission += less_than_one_quarter;
|
vis_info += less_than_one_quarter;
|
||||||
} else if (visibility < 0.5) {
|
} else if (visibility < 0.5) {
|
||||||
transmission += one_quarter;
|
vis_info += one_quarter;
|
||||||
} else if (visibility < 0.75) {
|
} else if (visibility < 0.75) {
|
||||||
transmission += one_half;
|
vis_info += one_half;
|
||||||
} else if (visibility < 1.0) {
|
} else if (visibility < 1.0) {
|
||||||
transmission += three_quarters;
|
vis_info += three_quarters;
|
||||||
} else if (visibility >= 1.5 && visibility < 2.0) {
|
} else if (visibility >= 1.5 && visibility < 2.0) {
|
||||||
transmission += one_and_one_half;
|
vis_info += one_and_one_half;
|
||||||
} else {
|
} else {
|
||||||
// integer miles
|
// integer miles
|
||||||
if (visibility > 10) visibility = 10;
|
if (visibility > 9.5)
|
||||||
sprintf(buf, "%i", int(.5 + visibility));
|
{
|
||||||
transmission += ConvertNumToSpokenDigits(buf);
|
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;
|
transmission += BRK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGATIS::genTemperatureInfo(double& Tsl, bool US_CA)
|
bool FGATIS::genCloudInfo(string& cloud_info)
|
||||||
{
|
{
|
||||||
using namespace atmodel;
|
|
||||||
using namespace lex;
|
using namespace lex;
|
||||||
const int bs(100);
|
|
||||||
char buf[bs];
|
|
||||||
|
|
||||||
transmission += Temperature + ": ";
|
|
||||||
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 += lex::minus + " ";
|
|
||||||
}
|
|
||||||
snprintf(buf, bs, "%i", abs(temp));
|
|
||||||
transmission += ConvertNumToSpokenDigits(buf);
|
|
||||||
if (US_CA) 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 += lex::minus + " ";
|
|
||||||
}
|
|
||||||
snprintf(buf, bs, "%i", abs(temp));
|
|
||||||
transmission += ConvertNumToSpokenDigits(buf);
|
|
||||||
if (US_CA) transmission += " " + Celsius;
|
|
||||||
transmission += BRK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FGATIS::genCloudInfo(void)
|
|
||||||
{
|
|
||||||
using namespace atmodel;
|
|
||||||
using namespace lex;
|
|
||||||
|
|
||||||
const int bs(100);
|
|
||||||
char buf[bs];
|
|
||||||
|
|
||||||
bool did_some = false;
|
bool did_some = false;
|
||||||
bool did_ceiling = false;
|
bool did_ceiling = false;
|
||||||
|
|
||||||
for (int layer = 0; layer <= 4; layer++) {
|
for (int layer = 0; layer <= 4; layer++) {
|
||||||
snprintf(buf, bs, "/environment/clouds/layer[%i]/coverage", layer);
|
snprintf(buf, sizeof(buf), "/environment/clouds/layer[%i]/coverage", layer);
|
||||||
string coverage = fgGetString(buf);
|
string coverage = fgGetString(buf);
|
||||||
if (coverage == clear) continue;
|
if (coverage == clear)
|
||||||
snprintf(buf, bs, "/environment/clouds/layer[%i]/thickness-ft", layer);
|
continue;
|
||||||
if (fgGetDouble(buf) == 0) continue;
|
snprintf(buf, sizeof(buf), "/environment/clouds/layer[%i]/thickness-ft", layer);
|
||||||
snprintf(buf, bs, "/environment/clouds/layer[%i]/elevation-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());
|
double ceiling = int(fgGetDouble(buf) - _geod.getElevationFt());
|
||||||
if (ceiling > 12000) continue;
|
if (ceiling > 12000)
|
||||||
|
continue;
|
||||||
|
|
||||||
// BEWARE: At the present time, the environment system has no
|
// BEWARE: At the present time, the environment system has no
|
||||||
// way (so far as I know) to represent a "thin broken" or
|
// way (so far as I know) to represent a "thin broken" or
|
||||||
|
@ -482,41 +660,53 @@ void FGATIS::genCloudInfo(void)
|
||||||
// First, do the prefix if any:
|
// First, do the prefix if any:
|
||||||
if (coverage == scattered || coverage == few) {
|
if (coverage == scattered || coverage == few) {
|
||||||
if (!did_some) {
|
if (!did_some) {
|
||||||
transmission += " " + Sky_condition + ": ";
|
if (_report.concise)
|
||||||
did_some++;
|
cloud_info += Clouds + ": ";
|
||||||
|
else
|
||||||
|
cloud_info += Sky_condition + ": ";
|
||||||
|
did_some = true;
|
||||||
}
|
}
|
||||||
} else /* must be a ceiling */ if (!did_ceiling) {
|
} else /* must be a ceiling */ if (!did_ceiling) {
|
||||||
transmission += " " + Ceiling + ": ";
|
cloud_info += " " + Ceiling + ": ";
|
||||||
did_ceiling++;
|
did_ceiling = true;
|
||||||
did_some++;
|
did_some = true;
|
||||||
} else {
|
} else {
|
||||||
transmission += " "; // no prefix required
|
cloud_info += " "; // no prefix required
|
||||||
}
|
}
|
||||||
int cig00 = int(SGMiscd::round(ceiling/100)); // hundreds of feet
|
int cig00 = int(SGMiscd::round(ceiling/100)); // hundreds of feet
|
||||||
if (cig00) {
|
if (cig00) {
|
||||||
int cig000 = cig00/10;
|
int cig000 = cig00/10;
|
||||||
cig00 -= cig000*10; // just the hundreds digit
|
cig00 -= cig000*10; // just the hundreds digit
|
||||||
if (cig000) {
|
if (cig000) {
|
||||||
snprintf(buf, bs, "%i", cig000);
|
snprintf(buf, sizeof(buf), "%i", cig000);
|
||||||
transmission += ConvertNumToSpokenDigits(buf);
|
cloud_info += ConvertNumToSpokenDigits(buf);
|
||||||
transmission += " " + thousand + " ";
|
cloud_info += " " + thousand + " ";
|
||||||
}
|
}
|
||||||
if (cig00) {
|
if (cig00) {
|
||||||
snprintf(buf, bs, "%i", cig00);
|
snprintf(buf, sizeof(buf), "%i", cig00);
|
||||||
transmission += ConvertNumToSpokenDigits(buf);
|
cloud_info += ConvertNumToSpokenDigits(buf);
|
||||||
transmission += " " + hundred + " ";
|
cloud_info += " " + hundred + " ";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Should this be "sky obscured?"
|
// Should this be "sky obscured?"
|
||||||
transmission += " " + zero + " "; // not "zero hundred"
|
cloud_info += " " + zero + " "; // not "zero hundred"
|
||||||
}
|
}
|
||||||
transmission += coverage + BRK;
|
cloud_info += coverage + BRK;
|
||||||
}
|
}
|
||||||
if (!did_some) transmission += " " + Sky + " " + clear + BRK;
|
if (!did_some)
|
||||||
|
cloud_info += " " + Sky + " " + clear + BRK;
|
||||||
|
return did_some;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGATIS::genFacilityInfo(void)
|
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);
|
// SG_LOG(SG_ATC, SG_ALERT, "ATIS: facility name: " << name);
|
||||||
|
|
||||||
// Note that at this point, multi-word facility names
|
// Note that at this point, multi-word facility names
|
||||||
|
@ -547,10 +737,9 @@ void FGATIS::genFacilityInfo(void)
|
||||||
|
|
||||||
void FGATIS::genWindInfo(void)
|
void FGATIS::genWindInfo(void)
|
||||||
{
|
{
|
||||||
using namespace atmodel;
|
|
||||||
using namespace lex;
|
using namespace lex;
|
||||||
|
|
||||||
transmission += wind + ": ";
|
transmission += Wind + ": ";
|
||||||
|
|
||||||
double wind_speed = fgGetDouble("/environment/config/boundary/entry[0]/wind-speed-kt");
|
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");
|
double wind_dir = fgGetDouble("/environment/config/boundary/entry[0]/wind-from-heading-deg");
|
||||||
|
@ -575,71 +764,123 @@ void FGATIS::genWindInfo(void)
|
||||||
wind_dir = 270;
|
wind_dir = 270;
|
||||||
transmission += " " + light_and_variable;
|
transmission += " " + light_and_variable;
|
||||||
} else {
|
} else {
|
||||||
const int bs(100);
|
|
||||||
char buf[bs];
|
|
||||||
|
|
||||||
// FIXME: get gust factor in somehow
|
// FIXME: get gust factor in somehow
|
||||||
snprintf(buf, bs, "%03.0f", 5*SGMiscd::round(wind_dir/5));
|
snprintf(buf, sizeof(buf), "%03.0f", 5*SGMiscd::round(wind_dir/5));
|
||||||
transmission += ConvertNumToSpokenDigits(buf);
|
transmission += ConvertNumToSpokenDigits(buf);
|
||||||
|
if (!_report.concise)
|
||||||
snprintf(buf, bs, "%1.0f", wind_speed);
|
transmission += " " + degrees;
|
||||||
transmission += " " + at + " " + ConvertNumToSpokenDigits(buf) + BRK;
|
transmission += " ";
|
||||||
|
snprintf(buf, sizeof(buf), "%1.0f", wind_speed);
|
||||||
|
transmission += at + " " + ConvertNumToSpokenDigits(buf);
|
||||||
|
if (!_report.concise)
|
||||||
|
transmission += " " + knots;
|
||||||
}
|
}
|
||||||
|
transmission += BRK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGATIS::genPressureInfo(bool US_CA, double Tsl)
|
void FGATIS::genTransitionLevel(const FGAirport* apt)
|
||||||
{
|
{
|
||||||
using namespace atmodel;
|
double hPa = _report.qnh/atmodel::mbar;
|
||||||
using namespace lex;
|
|
||||||
double myQNH;
|
|
||||||
double Psl = fgGetDouble("/environment/pressure-sea-level-inhg");
|
|
||||||
const int bs(100);
|
|
||||||
char buf[bs];
|
|
||||||
|
|
||||||
|
/* Transition level is the flight level above which aircraft must use standard pressure and below
|
||||||
|
* which airport pressure settings must be used.
|
||||||
|
* Following definitions are taken from German ATIS:
|
||||||
|
* QNH <= 977 hPa: TRL 80
|
||||||
|
* QNH <= 1013 hPa: TRL 70
|
||||||
|
* QNH > 1013 hPa: TRL 60
|
||||||
|
* (maybe differs slightly for other countries...)
|
||||||
|
*/
|
||||||
|
int tl = 60;
|
||||||
|
if (hPa <= 977)
|
||||||
|
tl = 80;
|
||||||
|
else
|
||||||
|
if (hPa <= 1013)
|
||||||
|
tl = 70;
|
||||||
|
|
||||||
|
// add an offset to the transition level for high altitude airports (just guessing here,
|
||||||
|
// seems reasonable)
|
||||||
|
double elevationFt = apt->getElevation();
|
||||||
|
int e = int(elevationFt / 1000.0);
|
||||||
|
if (e >= 3)
|
||||||
{
|
{
|
||||||
double press, temp;
|
// TL steps in 10(00)ft
|
||||||
|
tl += (e-2)*10;
|
||||||
tie(press, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to millibars for most of the world (not US, not CA)
|
snprintf(buf, sizeof(buf), "%02i", tl);
|
||||||
if((!US_CA) && fgGetBool("/sim/atc/use-millibars")) {
|
transmission += lex::Transition_level + ": " + ConvertNumToSpokenDigits(buf) + BRK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGATIS::genPressureInfo(void)
|
||||||
|
{
|
||||||
|
using namespace lex;
|
||||||
|
|
||||||
|
// hectopascal for most of the world (not US, not CA)
|
||||||
|
if(!_report.US_CA) {
|
||||||
|
double hPa = _report.qnh/atmodel::mbar;
|
||||||
transmission += QNH + ": ";
|
transmission += QNH + ": ";
|
||||||
myQNH /= mbar;
|
snprintf(buf, sizeof(buf), "%03.0f", _report.qnh / atmodel::mbar);
|
||||||
snprintf(buf, bs, "%03.0f", myQNH);
|
|
||||||
transmission += ConvertNumToSpokenDigits(buf);
|
transmission += ConvertNumToSpokenDigits(buf);
|
||||||
// TODO Extend voice samples so we can replace "millibars" with "hectopascal" (new ATIS standard since 2011)
|
// "hectopascal" replaced "millibars" in new ATIS standard since 2011
|
||||||
if (myQNH < 1000)
|
if ((!_report.concise)||(hPa < 1000))
|
||||||
transmission += " " + millibars; // "hectopascal" (millibars) spoken for values below 1000 only (to avoid confusion with inHg)
|
transmission += " " + hectopascal; // "hectopascal" must be provided for values below 1000 (to avoid confusion with inHg)
|
||||||
|
|
||||||
|
// Many (European) airports (with lots of US traffic?) provide both, hPa and inHg announcements.
|
||||||
|
// Europeans keep the "decimal" in inHg readings to make the distinction to hPa even more apparent.
|
||||||
|
// hPa/inHg separated by "equals" or "or" with some airports
|
||||||
|
if (_report.concise)
|
||||||
|
transmission += " " + equals + " ";
|
||||||
|
else
|
||||||
|
transmission += " " + Or + " ";
|
||||||
|
snprintf(buf, sizeof(buf), "%04.2f", _report.qnh / atmodel::inHg);
|
||||||
|
transmission += ConvertNumToSpokenDigits(buf);
|
||||||
|
if (!_report.concise)
|
||||||
|
transmission += " " + inches;
|
||||||
transmission += BRK;
|
transmission += BRK;
|
||||||
} else {
|
} else {
|
||||||
|
// use inches of mercury for US/CA
|
||||||
transmission += Altimeter + ": ";
|
transmission += Altimeter + ": ";
|
||||||
double asetting = myQNH / inHg; // use inches of mercury
|
double asetting = _report.qnh / atmodel::inHg;
|
||||||
asetting *= 100.; // shift two decimal places
|
// shift two decimal places, US/CA airports omit the "decimal" in inHg settings
|
||||||
snprintf(buf, bs, "%04.0f", asetting);
|
asetting *= 100.;
|
||||||
transmission += ConvertNumToSpokenDigits(buf) + BRK;
|
snprintf(buf, sizeof(buf), "%04.0f", asetting);
|
||||||
|
transmission += ConvertNumToSpokenDigits(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transmission += BRK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGATIS::genRunwayInfo(const string& phonetic_seq_string)
|
void FGATIS::genRunwayInfo(const FGAirport* apt)
|
||||||
{
|
{
|
||||||
using namespace atmodel;
|
|
||||||
using namespace lex;
|
using namespace lex;
|
||||||
|
|
||||||
const FGAirport* apt = fgFindAirportID(ident);
|
if (!apt)
|
||||||
if (apt) {
|
return;
|
||||||
|
|
||||||
FGRunway* rwy = apt->getActiveRunwayForUsage();
|
FGRunway* rwy = apt->getActiveRunwayForUsage();
|
||||||
if (rwy)
|
if (!rwy)
|
||||||
{
|
return;
|
||||||
|
|
||||||
string rwy_no = rwy->ident();
|
string rwy_no = rwy->ident();
|
||||||
if(rwy_no != "NN") {
|
if(rwy_no != "NN")
|
||||||
|
{
|
||||||
|
FGNavRecord* ils = rwy->ILS();
|
||||||
|
if (ils)
|
||||||
|
{
|
||||||
|
_report.ils = true;
|
||||||
|
transmission += Expect_I_L_S_approach + " "+ runway + " "+ConvertRwyNumToSpokenString(rwy_no) + BRK;
|
||||||
|
if (fgGetBool("/sim/atis/announce-ils-frequency", false))
|
||||||
|
{
|
||||||
|
// this is handy - but really non-standard (so disabled by default)
|
||||||
|
snprintf(buf, sizeof(buf), "%5.2f", ils->get_freq()/100.0);
|
||||||
|
transmission += I_L_S + " " + ConvertNumToSpokenDigits(buf) + BRK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
transmission += Expect_visual_approach + " "+ runway + " "+ConvertRwyNumToSpokenString(rwy_no) + BRK;
|
||||||
|
}
|
||||||
|
|
||||||
transmission += Landing_and_departing_runway + " ";
|
transmission += Landing_and_departing_runway + " ";
|
||||||
transmission += ConvertRwyNumToSpokenString(rwy_no) + BRK;
|
transmission += ConvertRwyNumToSpokenString(rwy_no) + BRK;
|
||||||
#ifdef ATIS_TEST
|
#ifdef ATIS_TEST
|
||||||
|
@ -650,11 +891,57 @@ void FGATIS::genRunwayInfo(const string& phonetic_seq_string)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGATIS::genWarnings(int position)
|
||||||
|
{
|
||||||
|
using namespace lex;
|
||||||
|
bool dayTime = (fgGetDouble("/sim/time/sun-angle-rad") < 1.57);
|
||||||
|
|
||||||
|
if (position == -1) // warnings at beginning of ATIS
|
||||||
|
{
|
||||||
|
// bird related warnings at day-time only (birds are VFR-only! ;-) )
|
||||||
|
if (dayTime)
|
||||||
|
{
|
||||||
|
if (_report.notam == 1)
|
||||||
|
transmission += Attention + ": " + flock_of_birds + " " + in_the_vicinity_of_the_airport + BRK;
|
||||||
|
else
|
||||||
|
if (_report.notam == 2)
|
||||||
|
transmission += Attention + ": " + bird_activity + " " + in_the_vicinity_of_the_airport + BRK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (position == 0) // warnings after runway messages
|
||||||
|
{
|
||||||
|
if ((_report.notam == 3)&&(_report.ils))
|
||||||
|
{
|
||||||
|
// "__I_LS_" necessary to trick the language generator into pronouncing it properly
|
||||||
|
transmission += Attention + ": " + short_time__I_LS_interference_possible_by_taxiing_aircraft + BRK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (position == 1) // warnings at the end of the report
|
||||||
|
{
|
||||||
|
// "runway wet-wet-wet" warning in heavy rain
|
||||||
|
if (_report.rain_norm > 0.6)
|
||||||
|
{
|
||||||
|
// "wet" is repeated 3 times in ATIS warnings, since the word is difficult
|
||||||
|
// to understand over radio - but the message is important.
|
||||||
|
transmission += runway_wet + " " + wet + " " + wet + BRK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_report.notam == 4)
|
||||||
|
{
|
||||||
|
// intentional: "reed" instead of "read" since festival gets it wrong otherwise
|
||||||
|
transmission += reed_back_all_runway_hold_instructions + BRK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if ((_report.notam == 5)&& _report.cavok && dayTime &&
|
||||||
|
(_report.rain_norm == 0) && (_report.snow_norm == 0)) // ;-)
|
||||||
|
{
|
||||||
|
transmission += Attention + ": " + glider_operation_in_sector + BRK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transmission += On_initial_contact_advise_you_have_information + " ";
|
|
||||||
transmission += phonetic_seq_string;
|
|
||||||
transmission += "... " + BRK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put the transmission into the property tree.
|
// Put the transmission into the property tree.
|
||||||
|
@ -663,7 +950,7 @@ void FGATIS::genRunwayInfo(const string& phonetic_seq_string)
|
||||||
// http://localhost:5400/instrumentation/comm[1]
|
// http://localhost:5400/instrumentation/comm[1]
|
||||||
//
|
//
|
||||||
// (Also, if in debug mode, dump it to the console.)
|
// (Also, if in debug mode, dump it to the console.)
|
||||||
void FGATIS::TreeOut(int msg_OK)
|
void FGATIS::treeOut(int msg_OK)
|
||||||
{
|
{
|
||||||
_atis->setStringValue("<pre>\n" + transmission_readable + "</pre>\n");
|
_atis->setStringValue("<pre>\n" + transmission_readable + "</pre>\n");
|
||||||
SG_LOG(SG_ATC, SG_DEBUG, "**** ATIS active on: " << _name <<
|
SG_LOG(SG_ATC, SG_DEBUG, "**** ATIS active on: " << _name <<
|
||||||
|
@ -671,7 +958,6 @@ void FGATIS::TreeOut(int msg_OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RangeFilter : public CommStation::Filter
|
class RangeFilter : public CommStation::Filter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -713,7 +999,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
// Search for ATC stations by frequency
|
// Search for ATC stations by frequency
|
||||||
void FGATIS::search(void)
|
bool FGATIS::search(double dt)
|
||||||
{
|
{
|
||||||
double frequency = _freq->getDoubleValue();
|
double frequency = _freq->getDoubleValue();
|
||||||
|
|
||||||
|
@ -721,9 +1007,12 @@ void FGATIS::search(void)
|
||||||
// in order to be consistent with apt.dat et cetera.
|
// in order to be consistent with apt.dat et cetera.
|
||||||
int freqKhz = 10 * static_cast<int>(frequency * 100 + 0.25);
|
int freqKhz = 10 * static_cast<int>(frequency * 100 + 0.25);
|
||||||
|
|
||||||
|
// only search tuned frequencies when necessary
|
||||||
|
_time_before_search_sec -= dt;
|
||||||
|
|
||||||
// throttle frequency searches
|
// throttle frequency searches
|
||||||
if ((freqKhz == _last_frequency)&&(_time_before_search_sec > 0))
|
if ((freqKhz == _last_frequency)&&(_time_before_search_sec > 0))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
_last_frequency = freqKhz;
|
_last_frequency = freqKhz;
|
||||||
_time_before_search_sec = 4.0;
|
_time_before_search_sec = 4.0;
|
||||||
|
@ -744,4 +1033,6 @@ void FGATIS::search(void)
|
||||||
{
|
{
|
||||||
SG_LOG(SG_ATC, SG_DEBUG, "FGATIS " << _name << ": no station.");
|
SG_LOG(SG_ATC, SG_DEBUG, "FGATIS " << _name << ": no station.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
// along with this program; if not, write to the Free Software
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
|
||||||
#ifndef _FG_ATIS_HXX
|
#ifndef _FG_ATIS_HXX
|
||||||
#define _FG_ATIS_HXX
|
#define _FG_ATIS_HXX
|
||||||
|
|
||||||
|
@ -32,6 +31,8 @@
|
||||||
|
|
||||||
#include "ATC.hxx"
|
#include "ATC.hxx"
|
||||||
|
|
||||||
|
class FGAirport;
|
||||||
|
|
||||||
typedef std::map<std::string,std::string> MSS;
|
typedef std::map<std::string,std::string> MSS;
|
||||||
|
|
||||||
class FGATIS : public FGATC {
|
class FGATIS : public FGATC {
|
||||||
|
@ -70,6 +71,7 @@ class FGATIS : public FGATC {
|
||||||
time_t cur_time;
|
time_t cur_time;
|
||||||
int msg_OK;
|
int msg_OK;
|
||||||
bool _attention;
|
bool _attention;
|
||||||
|
bool _check_transmission;
|
||||||
|
|
||||||
bool _prev_display; // Previous value of _display flag
|
bool _prev_display; // Previous value of _display flag
|
||||||
MSS _remap; // abbreviations to be expanded
|
MSS _remap; // abbreviations to be expanded
|
||||||
|
@ -78,11 +80,33 @@ class FGATIS : public FGATC {
|
||||||
double _time_before_search_sec;
|
double _time_before_search_sec;
|
||||||
int _last_frequency;
|
int _last_frequency;
|
||||||
|
|
||||||
|
// temporary buffer for string conversions
|
||||||
|
char buf[100];
|
||||||
|
|
||||||
|
// data for the current ATIS report
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
std::string phonetic_seq_string;
|
||||||
|
bool US_CA;
|
||||||
|
bool cavok;
|
||||||
|
bool concise;
|
||||||
|
bool ils;
|
||||||
|
int temp;
|
||||||
|
int dewpoint;
|
||||||
|
double psl;
|
||||||
|
double qnh;
|
||||||
|
double rain_norm, snow_norm;
|
||||||
|
int notam;
|
||||||
|
std::string hours,mins;
|
||||||
|
} _report;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FGATIS(const std::string& name, int num);
|
FGATIS(const std::string& name, int num);
|
||||||
|
|
||||||
virtual void init();
|
void init();
|
||||||
|
void reinit();
|
||||||
|
|
||||||
void attend(SGPropertyNode* node);
|
void attend(SGPropertyNode* node);
|
||||||
|
|
||||||
//run the ATIS instance
|
//run the ATIS instance
|
||||||
|
@ -96,27 +120,32 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/** generate the ATIS transmission text */
|
void createReport (const FGAirport* apt);
|
||||||
int genTransmission (const int regen, const bool special);
|
|
||||||
|
|
||||||
|
/** generate the ATIS transmission text */
|
||||||
|
bool genTransmission (const int regen, bool forceUpdate);
|
||||||
void genTimeInfo (void);
|
void genTimeInfo (void);
|
||||||
void genFacilityInfo (void);
|
void genFacilityInfo (void);
|
||||||
void genVisibilityInfo (void);
|
void genPrecipitationInfo(void);
|
||||||
void genCloudInfo (void);
|
bool genVisibilityInfo (std::string& vis_info);
|
||||||
|
bool genCloudInfo (std::string& cloud_info);
|
||||||
void genWindInfo (void);
|
void genWindInfo (void);
|
||||||
void genTemperatureInfo (double& Tsl, bool US_CA);
|
void genTemperatureInfo (void);
|
||||||
void genPressureInfo (bool US_CA, double Tsl);
|
void genTransitionLevel (const FGAirport* apt);
|
||||||
void genRunwayInfo (const std::string& phonetic_seq_string);
|
void genPressureInfo (void);
|
||||||
|
void genRunwayInfo (const FGAirport* apt);
|
||||||
|
void genWarnings (int position);
|
||||||
|
|
||||||
|
void addTemperature (int Temp);
|
||||||
|
|
||||||
// Put the text into the property tree
|
// Put the text into the property tree
|
||||||
// (and in debug mode, print it on the console):
|
// (and in debug mode, print it on the console):
|
||||||
void TreeOut(int msgOK);
|
void treeOut(int msgOK);
|
||||||
|
|
||||||
// Search the specified radio for stations on the same frequency and in range.
|
// Search the specified radio for stations on the same frequency and in range.
|
||||||
void search(void);
|
bool search(double dt);
|
||||||
|
|
||||||
friend std::istream& operator>> ( std::istream&, FGATIS& );
|
friend std::istream& operator>> ( std::istream&, FGATIS& );
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int (FGATIS::*int_getter)() const;
|
typedef int (FGATIS::*int_getter)() const;
|
||||||
|
|
|
@ -15,11 +15,11 @@ Q(Airfield)
|
||||||
Q(Airbase)
|
Q(Airbase)
|
||||||
Q(Junior)
|
Q(Junior)
|
||||||
Q(Celsius)
|
Q(Celsius)
|
||||||
Q(wind)
|
Q(Wind)
|
||||||
Q(zulu)
|
Q(zulu)
|
||||||
Q(zulu_weather)
|
Q(zulu_weather)
|
||||||
Q(Automated_weather_observation)
|
Q(Automated_weather_observation)
|
||||||
Q(weather)
|
Q(Weather)
|
||||||
Q(airport_information)
|
Q(airport_information)
|
||||||
Q(International)
|
Q(International)
|
||||||
Q(Regional)
|
Q(Regional)
|
||||||
|
@ -45,9 +45,10 @@ Q(overcast)
|
||||||
Q(thin)
|
Q(thin)
|
||||||
Q(Sky_condition)
|
Q(Sky_condition)
|
||||||
Q(Sky)
|
Q(Sky)
|
||||||
|
Q(Clouds)
|
||||||
Q(Ceiling)
|
Q(Ceiling)
|
||||||
Q(minus)
|
Q(minus)
|
||||||
Q(dewpoint)
|
Q(Dewpoint)
|
||||||
Q(Visibility)
|
Q(Visibility)
|
||||||
Q(less_than_one_quarter)
|
Q(less_than_one_quarter)
|
||||||
Q(one_quarter)
|
Q(one_quarter)
|
||||||
|
@ -56,13 +57,66 @@ Q(three_quarters)
|
||||||
Q(one_and_one_half)
|
Q(one_and_one_half)
|
||||||
Q(Altimeter)
|
Q(Altimeter)
|
||||||
Q(QNH)
|
Q(QNH)
|
||||||
Q(millibars)
|
|
||||||
Q(Landing_and_departing_runway)
|
Q(Landing_and_departing_runway)
|
||||||
Q(On_initial_contact_advise_you_have_information)
|
Q(Advise_on_initial_contact_you_have_information)
|
||||||
Q(This_is)
|
Q(This_is)
|
||||||
|
Q(information)
|
||||||
|
Q(millibars)
|
||||||
|
Q(hectopascal)
|
||||||
|
Q(inches)
|
||||||
|
Q(I_L_S)
|
||||||
|
Q(visual)
|
||||||
|
Q(cav_ok)
|
||||||
|
Q(clouds_and_visibility_OK)
|
||||||
|
Q(out)
|
||||||
|
Q(equals)
|
||||||
|
Q(Expect_I_L_S_approach)
|
||||||
|
Q(Expect_visual_approach)
|
||||||
|
Q(Transition_level)
|
||||||
|
Q(No_sig)
|
||||||
|
Q(Time)
|
||||||
|
Q(kelometers)
|
||||||
|
Q(Attention)
|
||||||
|
Q(flock_of_birds)
|
||||||
|
Q(bird_activity)
|
||||||
|
Q(in_the_vicinity_of_the_airport)
|
||||||
|
Q(short_time__I_LS_interference_possible_by_taxiing_aircraft)
|
||||||
|
Q(reed_back_all_runway_hold_instructions)
|
||||||
|
Q(glider_operation_in_sector)
|
||||||
|
Q(airport)
|
||||||
|
Q(runway_wet)
|
||||||
|
Q(runway_in_use)
|
||||||
|
Q(arrivals)
|
||||||
|
Q(runway)
|
||||||
|
Q(runways)
|
||||||
|
Q(expect)
|
||||||
|
Q(approach)
|
||||||
|
Q(departures)
|
||||||
|
Q(wet)
|
||||||
|
Q(ice)
|
||||||
|
Q(closed)
|
||||||
|
Q(light)
|
||||||
|
Q(moderate)
|
||||||
|
Q(heavy)
|
||||||
|
Q(rain)
|
||||||
|
Q(drizzle)
|
||||||
|
Q(snow)
|
||||||
|
Q(fog)
|
||||||
|
Q(plus)
|
||||||
|
Q(hours)
|
||||||
|
Q(variable)
|
||||||
|
Q(from)
|
||||||
|
Q(Or)
|
||||||
|
Q(And)
|
||||||
|
Q(to)
|
||||||
|
Q(maximum)
|
||||||
|
Q(between)
|
||||||
|
Q(degrees)
|
||||||
|
Q(or_more)
|
||||||
Q(left)
|
Q(left)
|
||||||
Q(right)
|
Q(right)
|
||||||
Q(center)
|
Q(center)
|
||||||
|
Q(knots)
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef Q
|
#undef Q
|
||||||
|
|
|
@ -841,6 +841,8 @@ void fgReInitSubsystems()
|
||||||
globals->get_subsystem("systems")->reinit();
|
globals->get_subsystem("systems")->reinit();
|
||||||
globals->get_subsystem("instrumentation")->reinit();
|
globals->get_subsystem("instrumentation")->reinit();
|
||||||
|
|
||||||
|
globals->get_subsystem("ATIS")->reinit();
|
||||||
|
|
||||||
// setup state to end re-init
|
// setup state to end re-init
|
||||||
fgSetBool("/sim/signals/reinit", false);
|
fgSetBool("/sim/signals/reinit", false);
|
||||||
if ( !freeze ) {
|
if ( !freeze ) {
|
||||||
|
|
Loading…
Reference in a new issue