A new comm radio and atis implementation
This commit is contained in:
parent
c519b9262d
commit
dd6cccdda1
23 changed files with 1319 additions and 100 deletions
src
ATC
ATISEncoder.cxxATISEncoder.hxxCMakeLists.txtMetarPropertiesATISInformationProvider.cxxMetarPropertiesATISInformationProvider.hxx
ATCDCL
Airports
Environment
Instrumentation
Main
473
src/ATC/ATISEncoder.cxx
Normal file
473
src/ATC/ATISEncoder.cxx
Normal file
|
@ -0,0 +1,473 @@
|
||||||
|
/*
|
||||||
|
Encode an ATIS into spoken words
|
||||||
|
Copyright (C) 2014 Torsten Dreyer
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ATISEncoder.hxx"
|
||||||
|
#include <Airports/dynamics.hxx>
|
||||||
|
#include <Main/globals.hxx>
|
||||||
|
#include <Main/locale.hxx>
|
||||||
|
#include <simgear/structure/exception.hxx>
|
||||||
|
#include <simgear/props/props_io.hxx>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
using simgear::PropertyList;
|
||||||
|
|
||||||
|
static string NO_ATIS("nil");
|
||||||
|
static string EMPTY("");
|
||||||
|
#define SPACE append(1,' ')
|
||||||
|
|
||||||
|
const char * ATCSpeech::getSpokenDigit( int i )
|
||||||
|
{
|
||||||
|
string key = "n" + boost::lexical_cast<std::string>( i );
|
||||||
|
return globals->get_locale()->getLocalizedString(key.c_str(), "atc", "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATCSpeech::getSpokenNumber( string number )
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
for( string::iterator it = number.begin(); it != number.end(); ++it ) {
|
||||||
|
result.append( getSpokenDigit( (*it) - '0' )).SPACE;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATCSpeech::getSpokenNumber( int number, bool leadingZero, int digits )
|
||||||
|
{
|
||||||
|
vector<const char *> spokenDigits;
|
||||||
|
int n = 0;
|
||||||
|
while( number > 0 ) {
|
||||||
|
spokenDigits.push_back( getSpokenDigit(number%10) );
|
||||||
|
number /= 10;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( digits > 0 ) {
|
||||||
|
while( n++ < digits ) {
|
||||||
|
spokenDigits.push_back( getSpokenDigit(0) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string result;
|
||||||
|
while( false == spokenDigits.empty() ) {
|
||||||
|
if( false == spokenDigits.empty() )
|
||||||
|
result.SPACE;
|
||||||
|
|
||||||
|
result.append( spokenDigits.back() );
|
||||||
|
spokenDigits.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATCSpeech::getSpokenAltitude( int altitude )
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
int thousands = altitude / 1000;
|
||||||
|
int hundrets = (altitude % 1000) / 100;
|
||||||
|
|
||||||
|
if( thousands > 0 ) {
|
||||||
|
result.append( getSpokenNumber(thousands) );
|
||||||
|
result.SPACE;
|
||||||
|
result.append( getSpokenDigit(1000) );
|
||||||
|
result.SPACE;
|
||||||
|
}
|
||||||
|
if( hundrets > 0 )
|
||||||
|
result.append( getSpokenNumber(hundrets) )
|
||||||
|
.SPACE
|
||||||
|
.append( getSpokenDigit(100) );
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ATISEncoder::ATISEncoder()
|
||||||
|
{
|
||||||
|
handlerMap.insert( std::make_pair( "text", &ATISEncoder::processTextToken ));
|
||||||
|
handlerMap.insert( std::make_pair( "token", &ATISEncoder::processTokenToken ));
|
||||||
|
handlerMap.insert( std::make_pair( "if", &ATISEncoder::processIfToken ));
|
||||||
|
|
||||||
|
handlerMap.insert( std::make_pair( "id", &ATISEncoder::getAtisId ));
|
||||||
|
handlerMap.insert( std::make_pair( "airport-name", &ATISEncoder::getAirportName ));
|
||||||
|
handlerMap.insert( std::make_pair( "time", &ATISEncoder::getTime ));
|
||||||
|
handlerMap.insert( std::make_pair( "approach-type", &ATISEncoder::getApproachType ));
|
||||||
|
handlerMap.insert( std::make_pair( "rwy-land", &ATISEncoder::getLandingRunway ));
|
||||||
|
handlerMap.insert( std::make_pair( "rwy-to", &ATISEncoder::getTakeoffRunway ));
|
||||||
|
handlerMap.insert( std::make_pair( "transition-level", &ATISEncoder::getTransitionLevel ));
|
||||||
|
handlerMap.insert( std::make_pair( "wind-dir", &ATISEncoder::getWindDirection ));
|
||||||
|
handlerMap.insert( std::make_pair( "wind-speed-kn", &ATISEncoder::getWindspeedKnots ));
|
||||||
|
handlerMap.insert( std::make_pair( "gusts", &ATISEncoder::getGustsKnots ));
|
||||||
|
handlerMap.insert( std::make_pair( "visibility-metric", &ATISEncoder::getVisibilityMetric ));
|
||||||
|
handlerMap.insert( std::make_pair( "phenomena", &ATISEncoder::getPhenomena ));
|
||||||
|
handlerMap.insert( std::make_pair( "clouds", &ATISEncoder::getClouds ));
|
||||||
|
handlerMap.insert( std::make_pair( "cavok", &ATISEncoder::getCavok ));
|
||||||
|
handlerMap.insert( std::make_pair( "temperature-deg", &ATISEncoder::getTemperatureDeg ));
|
||||||
|
handlerMap.insert( std::make_pair( "dewpoint-deg", &ATISEncoder::getDewpointDeg ));
|
||||||
|
handlerMap.insert( std::make_pair( "qnh", &ATISEncoder::getQnh ));
|
||||||
|
handlerMap.insert( std::make_pair( "inhg", &ATISEncoder::getInhg ));
|
||||||
|
handlerMap.insert( std::make_pair( "trend", &ATISEncoder::getTrend ));
|
||||||
|
}
|
||||||
|
|
||||||
|
ATISEncoder::~ATISEncoder()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SGPropertyNode_ptr findAtisTemplate( const std::string & stationId, SGPropertyNode_ptr atisSchemaNode )
|
||||||
|
{
|
||||||
|
using simgear::strutils::starts_with;
|
||||||
|
SGPropertyNode_ptr atisTemplate;
|
||||||
|
|
||||||
|
PropertyList schemaNodes = atisSchemaNode->getChildren("atis-schema");
|
||||||
|
for( PropertyList::iterator asit = schemaNodes.begin(); asit != schemaNodes.end(); ++asit ) {
|
||||||
|
SGPropertyNode_ptr ppp = (*asit)->getNode("station-starts-with", false );
|
||||||
|
atisTemplate = (*asit)->getNode("atis", false );
|
||||||
|
if( false == atisTemplate.valid() ) continue; // no <atis> node - ignore entry
|
||||||
|
|
||||||
|
PropertyList startsWithNodes = (*asit)->getChildren("station-starts-with");
|
||||||
|
for( PropertyList::iterator swit = startsWithNodes.begin(); swit != startsWithNodes.end(); ++swit ) {
|
||||||
|
|
||||||
|
if( starts_with( stationId, (*swit)->getStringValue() ) ) {
|
||||||
|
return atisTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return atisTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::encodeATIS( ATISInformationProvider * atisInformation )
|
||||||
|
{
|
||||||
|
using simgear::strutils::lowercase;
|
||||||
|
|
||||||
|
if( false == atisInformation->isValid() ) return NO_ATIS;
|
||||||
|
|
||||||
|
airport = FGAirport::getByIdent( atisInformation->airportId() );
|
||||||
|
if( false == airport.valid() ) {
|
||||||
|
SG_LOG( SG_ATC, SG_WARN, "ATISEncoder: unknown airport id " << atisInformation->airportId() );
|
||||||
|
return NO_ATIS;
|
||||||
|
}
|
||||||
|
|
||||||
|
_atis = atisInformation;
|
||||||
|
|
||||||
|
// lazily load the schema file on the first call
|
||||||
|
if( false == atisSchemaNode.valid() ) {
|
||||||
|
atisSchemaNode = new SGPropertyNode();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SGPath path = globals->resolve_maybe_aircraft_path("ATC/atis.xml");
|
||||||
|
readProperties( path.str(), atisSchemaNode );
|
||||||
|
}
|
||||||
|
catch (const sg_exception& e)
|
||||||
|
{
|
||||||
|
SG_LOG( SG_ATC, SG_ALERT, "ATISEncoder: Failed to load atis schema definition: " << e.getMessage());
|
||||||
|
return NO_ATIS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string stationId = lowercase( airport->ident() );
|
||||||
|
|
||||||
|
SGPropertyNode_ptr atisTemplate = findAtisTemplate( stationId, atisSchemaNode );;
|
||||||
|
if( false == atisTemplate.valid() ) {
|
||||||
|
SG_LOG(SG_ATC, SG_WARN, "no matching atis template for station " << stationId );
|
||||||
|
return NO_ATIS; // no template for this station!?
|
||||||
|
}
|
||||||
|
|
||||||
|
return processTokens( atisTemplate );
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::processTokens( SGPropertyNode_ptr node )
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
if( node.valid() ) {
|
||||||
|
for( int i = 0; i < node->nChildren(); i++ ) {
|
||||||
|
result.append(processToken( node->getChild(i) ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::processToken( SGPropertyNode_ptr token )
|
||||||
|
{
|
||||||
|
HandlerMap::iterator it = handlerMap.find( token->getName());
|
||||||
|
if( it == handlerMap.end() ) {
|
||||||
|
SG_LOG(SG_ATC, SG_WARN, "ATISEncoder: unknown token: " << token->getName() );
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
handler_t h = it->second;
|
||||||
|
return (this->*h)( token );
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::processTextToken( SGPropertyNode_ptr token )
|
||||||
|
{
|
||||||
|
return token->getStringValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::processTokenToken( SGPropertyNode_ptr token )
|
||||||
|
{
|
||||||
|
HandlerMap::iterator it = handlerMap.find( token->getStringValue());
|
||||||
|
if( it == handlerMap.end() ) {
|
||||||
|
SG_LOG(SG_ATC, SG_WARN, "ATISEncoder: unknown token: " << token->getStringValue() );
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
handler_t h = it->second;
|
||||||
|
return (this->*h)( token );
|
||||||
|
|
||||||
|
token->getStringValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::processIfToken( SGPropertyNode_ptr token )
|
||||||
|
{
|
||||||
|
SGPropertyNode_ptr n;
|
||||||
|
|
||||||
|
if( (n = token->getChild("empty", false )).valid() ) {
|
||||||
|
return checkEmptyCondition( n, true) ?
|
||||||
|
processTokens(token->getChild("then",false)) :
|
||||||
|
processTokens(token->getChild("else",false));
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (n = token->getChild("not-empty", false )).valid() ) {
|
||||||
|
return checkEmptyCondition( n, false) ?
|
||||||
|
processTokens(token->getChild("then",false)) :
|
||||||
|
processTokens(token->getChild("else",false));
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (n = token->getChild("equals", false )).valid() ) {
|
||||||
|
return checkEqualsCondition( n, true) ?
|
||||||
|
processTokens(token->getChild("then",false)) :
|
||||||
|
processTokens(token->getChild("else",false));
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (n = token->getChild("not-equals", false )).valid() ) {
|
||||||
|
return checkEqualsCondition( n, false) ?
|
||||||
|
processTokens(token->getChild("then",false)) :
|
||||||
|
processTokens(token->getChild("else",false));
|
||||||
|
}
|
||||||
|
|
||||||
|
SG_LOG(SG_ATC, SG_WARN, "ATISEncoder: no valid token found for <if> element" );
|
||||||
|
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ATISEncoder::checkEmptyCondition( SGPropertyNode_ptr node, bool isEmpty )
|
||||||
|
{
|
||||||
|
SGPropertyNode_ptr n1 = node->getNode( "token", false );
|
||||||
|
if( false == n1.valid() ) {
|
||||||
|
SG_LOG(SG_ATC, SG_WARN, "missing <token> node for (not)-empty" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return processToken( n1 ).empty() == isEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ATISEncoder::checkEqualsCondition( SGPropertyNode_ptr node, bool isEqual )
|
||||||
|
{
|
||||||
|
SGPropertyNode_ptr n1 = node->getNode( "token", 0, false );
|
||||||
|
SGPropertyNode_ptr n2 = node->getNode( "token", 1, false );
|
||||||
|
if( false == n1.valid() || false == n2.valid()) {
|
||||||
|
SG_LOG(SG_ATC, SG_WARN, "missing <token> node for (not)-equals" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool comp = processToken( n1 ).compare( processToken( n2 ) ) == 0;
|
||||||
|
return comp == isEqual;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getAtisId( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
FGAirportDynamics * dynamics = airport->getDynamics();
|
||||||
|
if( NULL != dynamics ) {
|
||||||
|
dynamics->updateAtisSequence( 30*60, false );
|
||||||
|
return dynamics->getAtisSequence();
|
||||||
|
}
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getAirportName( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
return airport->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getTime( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
return getSpokenNumber( _atis->getTime() % (100*100), true, 4 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline FGRunwayRef findBestRunwayForWind( FGAirportRef airport, int windDeg, int windKt )
|
||||||
|
{
|
||||||
|
struct FGAirport::FindBestRunwayForHeadingParams p;
|
||||||
|
//TODO: ramp down the heading weight with wind speed
|
||||||
|
p.ilsWeight = 4;
|
||||||
|
return airport->findBestRunwayForHeading( windDeg, &p );
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getApproachType( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
FGRunwayRef runway = findBestRunwayForWind( airport, _atis->getWindDeg(), _atis->getWindSpeedKt() );
|
||||||
|
if( runway.valid() ) {
|
||||||
|
if( NULL != runway->ILS() ) return globals->get_locale()->getLocalizedString("ils", "atc", "ils" );
|
||||||
|
//TODO: any chance to find other approach types? localizer-dme, vor-dme, vor, ndb?
|
||||||
|
}
|
||||||
|
|
||||||
|
return globals->get_locale()->getLocalizedString("visual", "atc", "visual" );
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getLandingRunway( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
FGRunwayRef runway = findBestRunwayForWind( airport, _atis->getWindDeg(), _atis->getWindSpeedKt() );
|
||||||
|
if( runway.valid() ) {
|
||||||
|
string runwayIdent = runway->ident();
|
||||||
|
if(runwayIdent != "NN") {
|
||||||
|
return getSpokenNumber(runwayIdent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getTakeoffRunway( SGPropertyNode_ptr p )
|
||||||
|
{
|
||||||
|
//TODO: if the airport has more than one runway, probably pick another one?
|
||||||
|
return getLandingRunway( p );
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getTransitionLevel( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
double hPa = _atis->getQnh();
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
if (hPa <= 978) {
|
||||||
|
tl = 80;
|
||||||
|
} else if( hPa > 978 && hPa <= 1013 ) {
|
||||||
|
tl = 70;
|
||||||
|
} else if( hPa > 1013 && hPa <= 1046 ) {
|
||||||
|
tl = 60;
|
||||||
|
} else {
|
||||||
|
tl = 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add an offset to the transition level for high altitude airports (just guessing here,
|
||||||
|
// seems reasonable)
|
||||||
|
int e = int(airport->getElevation() / 1000.0);
|
||||||
|
if (e >= 3) {
|
||||||
|
// TL steps in 10(00)ft
|
||||||
|
tl += (e-2)*10;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSpokenNumber(tl);
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getWindDirection( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
return getSpokenNumber( _atis->getWindDeg() );
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getWindspeedKnots( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
return getSpokenNumber( _atis->getWindSpeedKt() );
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getGustsKnots( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
int g = _atis->getGustsKt();
|
||||||
|
return g > 0 ? getSpokenNumber( g ) : EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getCavok( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
string CAVOK = globals->get_locale()->getLocalizedString("cavok", "atc", "cavok" );
|
||||||
|
|
||||||
|
return _atis->isCavok() ? CAVOK : EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getVisibilityMetric( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
string m = globals->get_locale()->getLocalizedString("meters", "atc", "meters" );
|
||||||
|
string km = globals->get_locale()->getLocalizedString("kilometersmeters", "atc", "kilometersmeters" );
|
||||||
|
string or_more = globals->get_locale()->getLocalizedString("ormore", "atc", "or more" );
|
||||||
|
|
||||||
|
int v = _atis->getVisibilityMeters();
|
||||||
|
string reply;
|
||||||
|
if( v < 5000 ) return reply.append( getSpokenAltitude( v ) ).SPACE.append( m );
|
||||||
|
if( v >= 10000 ) return reply.append( getSpokenNumber(10) ).SPACE.append( km ).SPACE.append(or_more);
|
||||||
|
return reply.append( getSpokenNumber( v/1000 ).append( km ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getPhenomena( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
return _atis->getPhenomena();
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getClouds( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
string FEET = globals->get_locale()->getLocalizedString("feet", "atc", "feet" );
|
||||||
|
string reply;
|
||||||
|
|
||||||
|
ATISInformationProvider::CloudEntries cloudEntries = _atis->getClouds();
|
||||||
|
|
||||||
|
for( ATISInformationProvider::CloudEntries::iterator it = cloudEntries.begin(); it != cloudEntries.end(); it++ ) {
|
||||||
|
if( false == reply.empty() ) reply.SPACE;
|
||||||
|
reply.append( it->second ).SPACE.append( getSpokenAltitude(it->first).SPACE.append( FEET ) );
|
||||||
|
}
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getTemperatureDeg( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
return getSpokenNumber( _atis->getTemperatureDeg() );
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getDewpointDeg( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
return getSpokenNumber( _atis->getDewpointDeg() );
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getQnh( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
return getSpokenNumber( _atis->getQnh() );
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getInhg( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
string DECIMAL = globals->get_locale()->getLocalizedString("dp", "atc", "decimal" );
|
||||||
|
double intpart = .0;
|
||||||
|
int fractpart = 1000 * ::modf( _atis->getQnh() * 100.0 / SG_INHG_TO_PA, &intpart );
|
||||||
|
fractpart += 5;
|
||||||
|
fractpart /= 10;
|
||||||
|
|
||||||
|
string reply;
|
||||||
|
reply.append( getSpokenNumber( (int)intpart ) )
|
||||||
|
.append( DECIMAL ).SPACE
|
||||||
|
.append( getSpokenNumber( fractpart ) );
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ATISEncoder::getTrend( SGPropertyNode_ptr )
|
||||||
|
{
|
||||||
|
return _atis->getTrend();
|
||||||
|
}
|
||||||
|
|
111
src/ATC/ATISEncoder.hxx
Normal file
111
src/ATC/ATISEncoder.hxx
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
Encode an ATIS into spoken words
|
||||||
|
Copyright (C) 2014 Torsten Dreyer
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ATIS_ENCODER_HXX
|
||||||
|
#define __ATIS_ENCODER_HXX
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <Airports/airport.hxx>
|
||||||
|
#include <simgear/props/props.hxx>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
class ATCSpeech {
|
||||||
|
public:
|
||||||
|
static const char * getSpokenDigit( int i );
|
||||||
|
static std::string getSpokenNumber( std::string number );
|
||||||
|
static std::string getSpokenNumber( int number, bool leadingZero = false, int digits = 1 );
|
||||||
|
static std::string getSpokenAltitude( int altitude );
|
||||||
|
};
|
||||||
|
|
||||||
|
class ATISInformationProvider {
|
||||||
|
public:
|
||||||
|
virtual ~ATISInformationProvider() {}
|
||||||
|
virtual bool isValid() = 0;
|
||||||
|
virtual std::string airportId() = 0;
|
||||||
|
|
||||||
|
static long makeAtisTime( int day, int hour, int minute ) {
|
||||||
|
return 100*100l* day + 100l * hour + minute;
|
||||||
|
}
|
||||||
|
inline int getAtisTimeDay( long atisTime ) { return atisTime / (100l*100l); }
|
||||||
|
inline int getAtisTimeHour( long atisTime ) { return (atisTime % (100l*100l)) / 100l; }
|
||||||
|
inline int getAtisTimeMinute( long atisTime ) { return atisTime % 100l; }
|
||||||
|
virtual long getTime() = 0; // see makeAtisTime
|
||||||
|
|
||||||
|
virtual int getWindDeg() = 0;
|
||||||
|
virtual int getWindSpeedKt() = 0;
|
||||||
|
virtual int getGustsKt() = 0;
|
||||||
|
virtual int getQnh() = 0;
|
||||||
|
virtual bool isCavok() = 0;
|
||||||
|
virtual int getVisibilityMeters() = 0;
|
||||||
|
virtual std::string getPhenomena() = 0;
|
||||||
|
|
||||||
|
typedef std::map<int,std::string> CloudEntries;
|
||||||
|
virtual CloudEntries getClouds() = 0;
|
||||||
|
virtual int getTemperatureDeg() = 0;
|
||||||
|
virtual int getDewpointDeg() = 0;
|
||||||
|
virtual std::string getTrend() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ATISEncoder : public ATCSpeech {
|
||||||
|
public:
|
||||||
|
ATISEncoder();
|
||||||
|
virtual ~ATISEncoder();
|
||||||
|
virtual std::string encodeATIS( ATISInformationProvider * atisInformationProvider );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual std::string getAtisId( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getAirportName( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getTime( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getApproachType( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getLandingRunway( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getTakeoffRunway( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getTransitionLevel( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getWindDirection( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getWindspeedKnots( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getGustsKnots( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getCavok( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getVisibilityMetric( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getPhenomena( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getClouds( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getTemperatureDeg( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getDewpointDeg( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getQnh( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getInhg( SGPropertyNode_ptr );
|
||||||
|
virtual std::string getTrend( SGPropertyNode_ptr );
|
||||||
|
|
||||||
|
typedef std::string (ATISEncoder::*handler_t)( SGPropertyNode_ptr baseNode );
|
||||||
|
typedef std::map<std::string, handler_t > HandlerMap;
|
||||||
|
HandlerMap handlerMap;
|
||||||
|
|
||||||
|
SGPropertyNode_ptr atisSchemaNode;
|
||||||
|
|
||||||
|
std::string processTokens( SGPropertyNode_ptr baseNode );
|
||||||
|
std::string processToken( SGPropertyNode_ptr baseNode );
|
||||||
|
|
||||||
|
std::string processTextToken( SGPropertyNode_ptr baseNode );
|
||||||
|
std::string processTokenToken( SGPropertyNode_ptr baseNode );
|
||||||
|
std::string processIfToken( SGPropertyNode_ptr baseNode );
|
||||||
|
bool checkEmptyCondition( SGPropertyNode_ptr node, bool isEmpty );
|
||||||
|
bool checkEqualsCondition( SGPropertyNode_ptr node, bool isEmpty );
|
||||||
|
|
||||||
|
FGAirportRef airport;
|
||||||
|
ATISInformationProvider * _atis;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -5,6 +5,8 @@ set(SOURCES
|
||||||
atcdialog.cxx
|
atcdialog.cxx
|
||||||
trafficcontrol.cxx
|
trafficcontrol.cxx
|
||||||
CommStation.cxx
|
CommStation.cxx
|
||||||
|
ATISEncoder.cxx
|
||||||
|
MetarPropertiesATISInformationProvider.cxx
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
|
@ -12,6 +14,8 @@ set(HEADERS
|
||||||
atcdialog.hxx
|
atcdialog.hxx
|
||||||
trafficcontrol.hxx
|
trafficcontrol.hxx
|
||||||
CommStation.hxx
|
CommStation.hxx
|
||||||
|
ATISEncoder.hxx
|
||||||
|
MetarPropertiesATISInformationProvider.hxx
|
||||||
)
|
)
|
||||||
|
|
||||||
flightgear_component(ATC "${SOURCES}" "${HEADERS}")
|
flightgear_component(ATC "${SOURCES}" "${HEADERS}")
|
||||||
|
|
120
src/ATC/MetarPropertiesATISInformationProvider.cxx
Normal file
120
src/ATC/MetarPropertiesATISInformationProvider.cxx
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
Provide Data for the ATIS Encoder from metarproperties
|
||||||
|
Copyright (C) 2014 Torsten Dreyer
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MetarPropertiesATISInformationProvider.hxx"
|
||||||
|
#include <Main/globals.hxx>
|
||||||
|
#include <simgear/constants.h>
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
static string EMPTY("");
|
||||||
|
static string CAVOK("cavok");
|
||||||
|
MetarPropertiesATISInformationProvider::MetarPropertiesATISInformationProvider( SGPropertyNode_ptr metar ) :
|
||||||
|
_metar( metar )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MetarPropertiesATISInformationProvider::~MetarPropertiesATISInformationProvider()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MetarPropertiesATISInformationProvider::isValid()
|
||||||
|
{
|
||||||
|
return _metar->getBoolValue( "valid", false );
|
||||||
|
}
|
||||||
|
|
||||||
|
string MetarPropertiesATISInformationProvider::airportId()
|
||||||
|
{
|
||||||
|
return _metar->getStringValue( "station-id", "xxxx" );
|
||||||
|
}
|
||||||
|
|
||||||
|
long MetarPropertiesATISInformationProvider::getTime()
|
||||||
|
{
|
||||||
|
return makeAtisTime( 0,
|
||||||
|
_metar->getIntValue( "hour" ) % 24,
|
||||||
|
_metar->getIntValue( "minute" ) % 60 );
|
||||||
|
}
|
||||||
|
|
||||||
|
int MetarPropertiesATISInformationProvider::getWindDeg()
|
||||||
|
{
|
||||||
|
return _metar->getIntValue( "base-wind-dir-deg" );
|
||||||
|
}
|
||||||
|
|
||||||
|
int MetarPropertiesATISInformationProvider::getWindSpeedKt()
|
||||||
|
{
|
||||||
|
return _metar->getIntValue( "base-wind-speed-kt" );
|
||||||
|
}
|
||||||
|
|
||||||
|
int MetarPropertiesATISInformationProvider::getGustsKt()
|
||||||
|
{
|
||||||
|
return _metar->getIntValue( "gust-wind-speed-kt" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MetarPropertiesATISInformationProvider::getQnh()
|
||||||
|
{
|
||||||
|
return _metar->getDoubleValue("pressure-sea-level-inhg") * SG_INHG_TO_PA / 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MetarPropertiesATISInformationProvider::isCavok()
|
||||||
|
{
|
||||||
|
return _metar->getBoolValue( "cavok" );
|
||||||
|
}
|
||||||
|
|
||||||
|
int MetarPropertiesATISInformationProvider::getVisibilityMeters()
|
||||||
|
{
|
||||||
|
return _metar->getIntValue( "min-visibility-m" );
|
||||||
|
}
|
||||||
|
|
||||||
|
string MetarPropertiesATISInformationProvider::getPhenomena()
|
||||||
|
{
|
||||||
|
return _metar->getStringValue( "decoded" );
|
||||||
|
}
|
||||||
|
|
||||||
|
ATISInformationProvider::CloudEntries MetarPropertiesATISInformationProvider::getClouds()
|
||||||
|
{
|
||||||
|
CloudEntries reply;
|
||||||
|
|
||||||
|
using simgear::PropertyList;
|
||||||
|
PropertyList layers = _metar->getNode("clouds", true )->getChildren("layer");
|
||||||
|
for( PropertyList::iterator it = layers.begin(); it != layers.end(); ++it ) {
|
||||||
|
const char * coverage = (*it)->getStringValue("coverage","clear");
|
||||||
|
double elevation = (*it)->getDoubleValue("elevation-ft", -9999 );
|
||||||
|
if( elevation > 0 ) {
|
||||||
|
reply[elevation] = coverage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MetarPropertiesATISInformationProvider::getTemperatureDeg()
|
||||||
|
{
|
||||||
|
return _metar->getIntValue( "temperature-degc" );
|
||||||
|
}
|
||||||
|
|
||||||
|
int MetarPropertiesATISInformationProvider::getDewpointDeg()
|
||||||
|
{
|
||||||
|
return _metar->getIntValue( "dewpoint-degc" );
|
||||||
|
}
|
||||||
|
|
||||||
|
string MetarPropertiesATISInformationProvider::getTrend()
|
||||||
|
{
|
||||||
|
return "nosig";
|
||||||
|
}
|
||||||
|
|
75
src/ATC/MetarPropertiesATISInformationProvider.hxx
Normal file
75
src/ATC/MetarPropertiesATISInformationProvider.hxx
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
Provide Data for the ATIS Encoder from metarproperties
|
||||||
|
Copyright (C) 2014 Torsten Dreyer
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __METARPROPERTIES_ATIS_ENCODER_HXX
|
||||||
|
#define __METARPROPERTIES_ATIS_ENCODER_HXX
|
||||||
|
|
||||||
|
/* ATIS encoder from metarproperties */
|
||||||
|
|
||||||
|
#include <simgear/props/props.hxx>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "ATISEncoder.hxx"
|
||||||
|
|
||||||
|
class MetarPropertiesATISInformationProvider : public ATISInformationProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MetarPropertiesATISInformationProvider( SGPropertyNode_ptr metar );
|
||||||
|
virtual ~MetarPropertiesATISInformationProvider();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool isValid();
|
||||||
|
virtual std::string airportId();
|
||||||
|
virtual long getTime();
|
||||||
|
virtual int getWindDeg();
|
||||||
|
virtual int getWindSpeedKt();
|
||||||
|
virtual int getGustsKt();
|
||||||
|
virtual int getQnh();
|
||||||
|
virtual bool isCavok();
|
||||||
|
virtual int getVisibilityMeters();
|
||||||
|
virtual std::string getPhenomena();
|
||||||
|
virtual CloudEntries getClouds();
|
||||||
|
virtual int getTemperatureDeg();
|
||||||
|
virtual int getDewpointDeg();
|
||||||
|
virtual std::string getTrend();
|
||||||
|
#if 0
|
||||||
|
virtual std::string getStationId();
|
||||||
|
virtual std::string getAtisId();
|
||||||
|
virtual std::string getTime();
|
||||||
|
virtual std::string getApproachType();
|
||||||
|
virtual std::string getLandingRunway();
|
||||||
|
virtual std::string getTakeoffRunway();
|
||||||
|
virtual std::string getTransitionLevel();
|
||||||
|
virtual std::string getWindDirection();
|
||||||
|
virtual std::string getWindspeedKnots();
|
||||||
|
virtual std::string getGustsKnots();
|
||||||
|
virtual std::string getVisibilityMetric();
|
||||||
|
virtual std::string getPhenomena();
|
||||||
|
virtual std::string getClouds();
|
||||||
|
virtual std::string getCavok();
|
||||||
|
virtual std::string getTemperatureDeg();
|
||||||
|
virtual std::string getDewpointDeg();
|
||||||
|
virtual std::string getQnh();
|
||||||
|
virtual std::string getTrend();
|
||||||
|
#endif
|
||||||
|
private:
|
||||||
|
SGPropertyNode_ptr _metar;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,23 +1,11 @@
|
||||||
include(FlightGearComponent)
|
include(FlightGearComponent)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
ATC.cxx
|
|
||||||
atis.cxx
|
|
||||||
ATCVoice.cxx
|
|
||||||
ATISmgr.cxx
|
|
||||||
ATCutils.cxx
|
|
||||||
ATCProjection.cxx
|
ATCProjection.cxx
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
ATC.hxx
|
|
||||||
atis.hxx
|
|
||||||
ATCVoice.hxx
|
|
||||||
ATISmgr.hxx
|
|
||||||
ATCutils.hxx
|
|
||||||
ATCProjection.hxx
|
ATCProjection.hxx
|
||||||
atis_lexicon.hxx
|
|
||||||
atis_remap.hxx
|
|
||||||
)
|
)
|
||||||
|
|
||||||
flightgear_component(ATCDCL "${SOURCES}" "${HEADERS}")
|
flightgear_component(ATCDCL "${SOURCES}" "${HEADERS}")
|
||||||
|
|
|
@ -236,18 +236,24 @@ FGHelipadRef FGAirport::getHelipadByIdent(const std::string& aIdent) const
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
FGRunwayRef FGAirport::findBestRunwayForHeading(double aHeading) const
|
FGRunwayRef FGAirport::findBestRunwayForHeading(double aHeading, struct FindBestRunwayForHeadingParams * parms ) const
|
||||||
{
|
{
|
||||||
loadRunways();
|
loadRunways();
|
||||||
|
|
||||||
FGRunway* result = NULL;
|
FGRunway* result = NULL;
|
||||||
double currentBestQuality = 0.0;
|
double currentBestQuality = 0.0;
|
||||||
|
|
||||||
SGPropertyNode *param = fgGetNode("/sim/airport/runways/search", true);
|
struct FindBestRunwayForHeadingParams fbrfhp;
|
||||||
double lengthWeight = param->getDoubleValue("length-weight", 0.01);
|
if( NULL != parms ) fbrfhp = *parms;
|
||||||
double widthWeight = param->getDoubleValue("width-weight", 0.01);
|
|
||||||
double surfaceWeight = param->getDoubleValue("surface-weight", 10);
|
SGPropertyNode_ptr searchNode = fgGetNode("/sim/airport/runways/search");
|
||||||
double deviationWeight = param->getDoubleValue("deviation-weight", 1);
|
if( searchNode.valid() ) {
|
||||||
|
fbrfhp.lengthWeight = searchNode->getDoubleValue("length-weight", fbrfhp.lengthWeight );
|
||||||
|
fbrfhp.widthWeight = searchNode->getDoubleValue("width-weight", fbrfhp.widthWeight );
|
||||||
|
fbrfhp.surfaceWeight = searchNode->getDoubleValue("surface-weight", fbrfhp.surfaceWeight );
|
||||||
|
fbrfhp.deviationWeight = searchNode->getDoubleValue("deviation-weight", fbrfhp.deviationWeight );
|
||||||
|
fbrfhp.ilsWeight = searchNode->getDoubleValue("ils-weight", fbrfhp.ilsWeight );
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_FOREACH(PositionedID id, mRunways) {
|
BOOST_FOREACH(PositionedID id, mRunways) {
|
||||||
FGRunway* rwy = loadById<FGRunway>(id);
|
FGRunway* rwy = loadById<FGRunway>(id);
|
||||||
|
@ -257,10 +263,10 @@ FGRunwayRef FGAirport::findBestRunwayForHeading(double aHeading) const
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
double good = rwy->score(lengthWeight, widthWeight, surfaceWeight);
|
double good = rwy->score( fbrfhp.lengthWeight, fbrfhp.widthWeight, fbrfhp.surfaceWeight, fbrfhp.ilsWeight );
|
||||||
double dev = aHeading - rwy->headingDeg();
|
double dev = aHeading - rwy->headingDeg();
|
||||||
SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
|
SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
|
||||||
double bad = fabs(deviationWeight * dev) + 1e-20;
|
double bad = fabs( fbrfhp.deviationWeight * dev) + 1e-20;
|
||||||
double quality = good / bad;
|
double quality = good / bad;
|
||||||
|
|
||||||
if (quality > currentBestQuality) {
|
if (quality > currentBestQuality) {
|
||||||
|
|
|
@ -94,7 +94,22 @@ class FGAirport : public FGPositioned
|
||||||
bool hasHelipadWithIdent(const std::string& aIdent) const;
|
bool hasHelipadWithIdent(const std::string& aIdent) const;
|
||||||
FGRunwayRef getRunwayByIdent(const std::string& aIdent) const;
|
FGRunwayRef getRunwayByIdent(const std::string& aIdent) const;
|
||||||
FGHelipadRef getHelipadByIdent(const std::string& aIdent) const;
|
FGHelipadRef getHelipadByIdent(const std::string& aIdent) const;
|
||||||
FGRunwayRef findBestRunwayForHeading(double aHeading) const;
|
|
||||||
|
struct FindBestRunwayForHeadingParams {
|
||||||
|
FindBestRunwayForHeadingParams() {
|
||||||
|
lengthWeight = 0.01;
|
||||||
|
widthWeight = 0.01;
|
||||||
|
surfaceWeight = 10;
|
||||||
|
deviationWeight = 1;
|
||||||
|
ilsWeight = 0;
|
||||||
|
}
|
||||||
|
double lengthWeight;
|
||||||
|
double widthWeight;
|
||||||
|
double surfaceWeight;
|
||||||
|
double deviationWeight;
|
||||||
|
double ilsWeight;
|
||||||
|
};
|
||||||
|
FGRunwayRef findBestRunwayForHeading(double aHeading, struct FindBestRunwayForHeadingParams * parms = NULL ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return the most likely target runway based on a position.
|
* return the most likely target runway based on a position.
|
||||||
|
|
|
@ -38,8 +38,8 @@
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
#include <Main/globals.hxx>
|
#include <Main/globals.hxx>
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
|
#include <Main/locale.hxx>
|
||||||
#include <Airports/runways.hxx>
|
#include <Airports/runways.hxx>
|
||||||
#include <ATCDCL/ATCutils.hxx>
|
|
||||||
#include <Navaids/NavDataCache.hxx>
|
#include <Navaids/NavDataCache.hxx>
|
||||||
|
|
||||||
#include "airport.hxx"
|
#include "airport.hxx"
|
||||||
|
@ -530,8 +530,12 @@ const std::string FGAirportDynamics::getAtisSequence()
|
||||||
if (atisSequenceIndex == -1) {
|
if (atisSequenceIndex == -1) {
|
||||||
updateAtisSequence(1, false);
|
updateAtisSequence(1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char atisSequenceString[2];
|
||||||
|
atisSequenceString[0] = 'a' + atisSequenceIndex;
|
||||||
|
atisSequenceString[1] = 0;
|
||||||
|
|
||||||
return GetPhoneticLetter(atisSequenceIndex);
|
return globals->get_locale()->getLocalizedString(atisSequenceString, "atc", "unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate)
|
int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate)
|
||||||
|
@ -540,7 +544,7 @@ int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate)
|
||||||
if (atisSequenceIndex == -1) {
|
if (atisSequenceIndex == -1) {
|
||||||
// first computation
|
// first computation
|
||||||
atisSequenceTimeStamp = now;
|
atisSequenceTimeStamp = now;
|
||||||
atisSequenceIndex = rand() % LTRS; // random initial sequence letters
|
atisSequenceIndex = rand() % 26; // random initial sequence letters
|
||||||
return atisSequenceIndex;
|
return atisSequenceIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,7 +554,7 @@ int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate)
|
||||||
++steps; // a "special" ATIS update is required
|
++steps; // a "special" ATIS update is required
|
||||||
}
|
}
|
||||||
|
|
||||||
atisSequenceIndex = (atisSequenceIndex + steps) % LTRS;
|
atisSequenceIndex = (atisSequenceIndex + steps) % 26;
|
||||||
// return a huge value if no update occurred
|
// return a huge value if no update occurred
|
||||||
return (atisSequenceIndex + (steps ? 0 : LTRS*1000));
|
return (atisSequenceIndex + (steps ? 0 : 26*1000));
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,15 +96,17 @@ string FGRunway::reverseIdent(const string& aRunwayIdent)
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
double FGRunway::score(double aLengthWt, double aWidthWt, double aSurfaceWt) const
|
double FGRunway::score(double aLengthWt, double aWidthWt, double aSurfaceWt, double aIlsWt) const
|
||||||
{
|
{
|
||||||
int surface = 1;
|
int surface = 1;
|
||||||
if (_surface_code == 12 || _surface_code == 5) // dry lakebed & gravel
|
if (_surface_code == 12 || _surface_code == 5) // dry lakebed & gravel
|
||||||
surface = 2;
|
surface = 2;
|
||||||
else if (_surface_code == 1 || _surface_code == 2) // asphalt & concrete
|
else if (_surface_code == 1 || _surface_code == 2) // asphalt & concrete
|
||||||
surface = 3;
|
surface = 3;
|
||||||
|
|
||||||
|
int ils = (_ils != 0);
|
||||||
|
|
||||||
return _length * aLengthWt + _width * aWidthWt + surface * aSurfaceWt + 1e-20;
|
return _length * aLengthWt + _width * aWidthWt + surface * aSurfaceWt + ils * aIlsWt + 1e-20;
|
||||||
}
|
}
|
||||||
|
|
||||||
SGGeod FGRunway::begin() const
|
SGGeod FGRunway::begin() const
|
||||||
|
|
|
@ -58,7 +58,7 @@ public:
|
||||||
* score this runway according to the specified weights. Used by
|
* score this runway according to the specified weights. Used by
|
||||||
* FGAirport::findBestRunwayForHeading
|
* FGAirport::findBestRunwayForHeading
|
||||||
*/
|
*/
|
||||||
double score(double aLengthWt, double aWidthWt, double aSurfaceWt) const;
|
double score(double aLengthWt, double aWidthWt, double aSurfaceWt, double aIlsWt) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the runway beginning point - this is syntatic sugar, equivalent to
|
* Get the runway beginning point - this is syntatic sugar, equivalent to
|
||||||
|
|
|
@ -17,7 +17,7 @@ set(SOURCES
|
||||||
terrainsampler.cxx
|
terrainsampler.cxx
|
||||||
presets.cxx
|
presets.cxx
|
||||||
gravity.cxx
|
gravity.cxx
|
||||||
magvarmanager.cxx
|
magvarmanager.cxx
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
|
@ -37,7 +37,7 @@ set(HEADERS
|
||||||
terrainsampler.hxx
|
terrainsampler.hxx
|
||||||
presets.hxx
|
presets.hxx
|
||||||
gravity.hxx
|
gravity.hxx
|
||||||
magvarmanager.hxx
|
magvarmanager.hxx
|
||||||
)
|
)
|
||||||
|
|
||||||
flightgear_component(Environment "${SOURCES}" "${HEADERS}")
|
flightgear_component(Environment "${SOURCES}" "${HEADERS}")
|
||||||
|
|
|
@ -23,6 +23,7 @@ set(SOURCES
|
||||||
mrg.cxx
|
mrg.cxx
|
||||||
navradio.cxx
|
navradio.cxx
|
||||||
newnavradio.cxx
|
newnavradio.cxx
|
||||||
|
commradio.cxx
|
||||||
rad_alt.cxx
|
rad_alt.cxx
|
||||||
rnav_waypt_controller.cxx
|
rnav_waypt_controller.cxx
|
||||||
slip_skid_ball.cxx
|
slip_skid_ball.cxx
|
||||||
|
@ -83,6 +84,7 @@ set(HEADERS
|
||||||
mrg.hxx
|
mrg.hxx
|
||||||
navradio.hxx
|
navradio.hxx
|
||||||
newnavradio.hxx
|
newnavradio.hxx
|
||||||
|
commradio.hxx
|
||||||
rad_alt.hxx
|
rad_alt.hxx
|
||||||
rnav_waypt_controller.hxx
|
rnav_waypt_controller.hxx
|
||||||
slip_skid_ball.hxx
|
slip_skid_ball.hxx
|
||||||
|
|
384
src/Instrumentation/commradio.cxx
Normal file
384
src/Instrumentation/commradio.cxx
Normal file
|
@ -0,0 +1,384 @@
|
||||||
|
// commradio.cxx -- class to manage a nav radio instance
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, February 2014
|
||||||
|
//
|
||||||
|
// Copyright (C) 2000 - 2011 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "commradio.hxx"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
|
#include <simgear/sg_inlines.h>
|
||||||
|
#include <simgear/props/propertyObject.hxx>
|
||||||
|
#include <simgear/misc/strutils.hxx>
|
||||||
|
|
||||||
|
#include <ATC/CommStation.hxx>
|
||||||
|
#include <ATC/MetarPropertiesATISInformationProvider.hxx>
|
||||||
|
#include <Airports/airport.hxx>
|
||||||
|
#include <Main/fg_props.hxx>
|
||||||
|
#include <Navaids/navlist.hxx>
|
||||||
|
|
||||||
|
#include "frequencyformatter.hxx"
|
||||||
|
|
||||||
|
namespace Instrumentation {
|
||||||
|
|
||||||
|
using simgear::PropertyObject;
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
|
||||||
|
SignalQualityComputer::~SignalQualityComputer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimpleDistanceSquareSignalQualityComputer : public SignalQualityComputer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SimpleDistanceSquareSignalQualityComputer( double range ) : _rangeM(range), _rangeM2(range*range) {}
|
||||||
|
virtual double computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const;
|
||||||
|
virtual double computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const;
|
||||||
|
virtual double computeSignalQuality( double slantDistanceM ) const;
|
||||||
|
private:
|
||||||
|
double _rangeM;
|
||||||
|
double _rangeM2;
|
||||||
|
};
|
||||||
|
|
||||||
|
double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const
|
||||||
|
{
|
||||||
|
return computeSignalQuality( dist( sender, receiver ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const
|
||||||
|
{
|
||||||
|
return computeSignalQuality( SGGeodesy::distanceM( sender, receiver ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( double distanceM ) const
|
||||||
|
{
|
||||||
|
return distanceM < _rangeM ? 1.0 : ( _rangeM2 / (distanceM*distanceM) );
|
||||||
|
}
|
||||||
|
|
||||||
|
class OnExitHandler {
|
||||||
|
public:
|
||||||
|
virtual void onExit() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OnExit {
|
||||||
|
public:
|
||||||
|
OnExit( OnExitHandler * onExitHandler ) : _onExitHandler( onExitHandler ) {}
|
||||||
|
~OnExit() { _onExitHandler->onExit(); }
|
||||||
|
private:
|
||||||
|
OnExitHandler * _onExitHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class OutputProperties : public OnExitHandler {
|
||||||
|
public:
|
||||||
|
OutputProperties( SGPropertyNode_ptr rootNode ) :
|
||||||
|
_rootNode(rootNode),
|
||||||
|
_signalQuality_norm(0.0),
|
||||||
|
_slantDistance_m(0.0),
|
||||||
|
_trueBearingTo_deg(0.0),
|
||||||
|
_trueBearingFrom_deg(0.0),
|
||||||
|
_trackDistance_m(0.0),
|
||||||
|
_heightAboveStation_ft(0.0),
|
||||||
|
|
||||||
|
_PO_stationType( rootNode->getNode("station-type", true ) ),
|
||||||
|
_PO_stationName( rootNode->getNode("station-name", true ) ),
|
||||||
|
_PO_airportId( rootNode->getNode("airport-id", true ) ),
|
||||||
|
_PO_signalQuality_norm( rootNode->getNode("signal-quality-norm",true) ),
|
||||||
|
_PO_slantDistance_m( rootNode->getNode("slant-distance-m",true) ),
|
||||||
|
_PO_trueBearingTo_deg( rootNode->getNode("true-bearing-to-deg",true) ),
|
||||||
|
_PO_trueBearingFrom_deg( rootNode->getNode("true-bearing-from-deg",true) ),
|
||||||
|
_PO_trackDistance_m( rootNode->getNode("track-distance-m",true) ),
|
||||||
|
_PO_heightAboveStation_ft( rootNode->getNode("height-above-station-ft",true) )
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SGPropertyNode_ptr _rootNode;
|
||||||
|
|
||||||
|
std::string _stationType;
|
||||||
|
std::string _stationName;
|
||||||
|
std::string _airportId;
|
||||||
|
double _signalQuality_norm;
|
||||||
|
double _slantDistance_m;
|
||||||
|
double _trueBearingTo_deg;
|
||||||
|
double _trueBearingFrom_deg;
|
||||||
|
double _trackDistance_m;
|
||||||
|
double _heightAboveStation_ft;
|
||||||
|
|
||||||
|
private:
|
||||||
|
PropertyObject<string> _PO_stationType;
|
||||||
|
PropertyObject<string> _PO_stationName;
|
||||||
|
PropertyObject<string> _PO_airportId;
|
||||||
|
PropertyObject<double> _PO_signalQuality_norm;
|
||||||
|
PropertyObject<double> _PO_slantDistance_m;
|
||||||
|
PropertyObject<double> _PO_trueBearingTo_deg;
|
||||||
|
PropertyObject<double> _PO_trueBearingFrom_deg;
|
||||||
|
PropertyObject<double> _PO_trackDistance_m;
|
||||||
|
PropertyObject<double> _PO_heightAboveStation_ft;
|
||||||
|
|
||||||
|
virtual void onExit() {
|
||||||
|
_PO_stationType = _stationType;
|
||||||
|
_PO_stationName = _stationName;
|
||||||
|
_PO_airportId = _airportId;
|
||||||
|
_PO_signalQuality_norm = _signalQuality_norm;
|
||||||
|
_PO_slantDistance_m = _slantDistance_m;
|
||||||
|
_PO_trueBearingTo_deg = _trueBearingTo_deg;
|
||||||
|
_PO_trueBearingFrom_deg = _trueBearingFrom_deg;
|
||||||
|
_PO_trackDistance_m = _trackDistance_m;
|
||||||
|
_PO_heightAboveStation_ft = _heightAboveStation_ft;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ------------- The CommRadio implementation ---------------------- */
|
||||||
|
|
||||||
|
class MetarBridge : public SGReferenced, public SGPropertyChangeListener {
|
||||||
|
public:
|
||||||
|
MetarBridge();
|
||||||
|
~MetarBridge();
|
||||||
|
|
||||||
|
void bind();
|
||||||
|
void unbind();
|
||||||
|
void requestMetarForId( std::string & id );
|
||||||
|
void clearMetar();
|
||||||
|
void setMetarPropertiesRoot( SGPropertyNode_ptr n ) { _metarPropertiesNode = n; }
|
||||||
|
void setAtisNode( SGPropertyNode * n ) { _atisNode = n; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void valueChanged(SGPropertyNode * );
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string _requestedId;
|
||||||
|
SGPropertyNode_ptr _metarPropertiesNode;
|
||||||
|
SGPropertyNode * _atisNode;
|
||||||
|
ATISEncoder _atisEncoder;
|
||||||
|
};
|
||||||
|
typedef SGSharedPtr<MetarBridge> MetarBridgeRef;
|
||||||
|
|
||||||
|
MetarBridge::MetarBridge() :
|
||||||
|
_atisNode(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MetarBridge::~MetarBridge()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetarBridge::bind()
|
||||||
|
{
|
||||||
|
_metarPropertiesNode->getNode( "valid", true )->addChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetarBridge::unbind()
|
||||||
|
{
|
||||||
|
_metarPropertiesNode->getNode( "valid", true )->removeChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetarBridge::requestMetarForId( std::string & id )
|
||||||
|
{
|
||||||
|
std::string uppercaseId = simgear::strutils::uppercase( id );
|
||||||
|
if( _requestedId == uppercaseId ) return;
|
||||||
|
_requestedId = uppercaseId;
|
||||||
|
_metarPropertiesNode->getNode( "station-id", true )->setStringValue( uppercaseId );
|
||||||
|
_metarPropertiesNode->getNode( "valid", true )->setBoolValue( false );
|
||||||
|
_metarPropertiesNode->getNode( "time-to-live", true )->setDoubleValue( 0.0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetarBridge::clearMetar()
|
||||||
|
{
|
||||||
|
string empty;
|
||||||
|
requestMetarForId( empty );
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetarBridge::valueChanged(SGPropertyNode * node )
|
||||||
|
{
|
||||||
|
// check for raising edge of valid flag
|
||||||
|
if( NULL == node || false == node->getBoolValue() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string responseId = simgear::strutils::uppercase(
|
||||||
|
_metarPropertiesNode->getNode( "station-id", true )->getStringValue() );
|
||||||
|
|
||||||
|
// unrequested metar!?
|
||||||
|
if( responseId != _requestedId )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( NULL != _atisNode ) {
|
||||||
|
MetarPropertiesATISInformationProvider provider( _metarPropertiesNode );
|
||||||
|
_atisNode->setStringValue( _atisEncoder.encodeATIS( &provider ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------- The CommRadio implementation ---------------------- */
|
||||||
|
|
||||||
|
class CommRadioImpl : public CommRadio, OutputProperties {
|
||||||
|
|
||||||
|
public:
|
||||||
|
CommRadioImpl( SGPropertyNode_ptr node );
|
||||||
|
virtual ~CommRadioImpl();
|
||||||
|
|
||||||
|
virtual void update( double dt );
|
||||||
|
virtual void init();
|
||||||
|
void bind();
|
||||||
|
void unbind();
|
||||||
|
|
||||||
|
private:
|
||||||
|
MetarBridgeRef _metarBridge;
|
||||||
|
FrequencyFormatter _useFrequencyFormatter;
|
||||||
|
FrequencyFormatter _stbyFrequencyFormatter;
|
||||||
|
const SignalQualityComputerRef _signalQualityComputer;
|
||||||
|
|
||||||
|
double _stationTTL;
|
||||||
|
double _frequency;
|
||||||
|
flightgear::CommStationRef _commStationForFrequency;
|
||||||
|
|
||||||
|
PropertyObject<bool> _serviceable;
|
||||||
|
PropertyObject<bool> _power_btn;
|
||||||
|
PropertyObject<bool> _power_good;
|
||||||
|
PropertyObject<double> _volume_norm;
|
||||||
|
PropertyObject<string> _atis;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
CommRadioImpl::CommRadioImpl( SGPropertyNode_ptr node ) :
|
||||||
|
OutputProperties( fgGetNode("/instrumentation",true)->getNode(
|
||||||
|
node->getStringValue("name", "nav"),
|
||||||
|
node->getIntValue("number", 0), true)),
|
||||||
|
_metarBridge( new MetarBridge() ),
|
||||||
|
_useFrequencyFormatter( _rootNode->getNode("frequencies/selected-mhz",true),
|
||||||
|
_rootNode->getNode("frequencies/selected-mhz-fmt",true), 0.025 ),
|
||||||
|
_stbyFrequencyFormatter( _rootNode->getNode("frequencies/standby-mhz",true),
|
||||||
|
_rootNode->getNode("frequencies/standby-mhz-fmt",true), 0.025 ),
|
||||||
|
_signalQualityComputer( new SimpleDistanceSquareSignalQualityComputer(10*SG_NM_TO_METER) ),
|
||||||
|
|
||||||
|
_stationTTL(0.0),
|
||||||
|
_frequency(-1.0),
|
||||||
|
_commStationForFrequency(NULL),
|
||||||
|
|
||||||
|
_serviceable( _rootNode->getNode("serviceable",true) ),
|
||||||
|
_power_btn( _rootNode->getNode("power-btn",true) ),
|
||||||
|
_power_good( _rootNode->getNode("power-good",true) ),
|
||||||
|
_volume_norm( _rootNode->getNode("volume",true) ),
|
||||||
|
_atis( _rootNode->getNode("atis",true) )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CommRadioImpl::~CommRadioImpl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommRadioImpl::bind()
|
||||||
|
{
|
||||||
|
_metarBridge->setAtisNode( _atis.node() );
|
||||||
|
_metarBridge->setMetarPropertiesRoot( fgGetNode( "/environment/metar[3]", true ) );
|
||||||
|
_metarBridge->bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommRadioImpl::unbind()
|
||||||
|
{
|
||||||
|
_metarBridge->unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommRadioImpl::init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommRadioImpl::update( double dt )
|
||||||
|
{
|
||||||
|
if( dt < SGLimitsd::min() ) return;
|
||||||
|
_stationTTL -= dt;
|
||||||
|
|
||||||
|
// Ensure all output properties get written on exit of this method
|
||||||
|
OnExit onExit(this);
|
||||||
|
|
||||||
|
SGGeod position;
|
||||||
|
try { position = globals->get_aircraft_position(); }
|
||||||
|
catch( std::exception & ) { return; }
|
||||||
|
|
||||||
|
if( false == (_power_btn )) {
|
||||||
|
_stationTTL = 0.0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( _frequency != _useFrequencyFormatter.getFrequency() ) {
|
||||||
|
_frequency = _useFrequencyFormatter.getFrequency();
|
||||||
|
_stationTTL = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( _stationTTL <= 0.0 ) {
|
||||||
|
_stationTTL = 30.0;
|
||||||
|
|
||||||
|
// Note: 122.375 must be rounded DOWN to 122370
|
||||||
|
// in order to be consistent with apt.dat et cetera.
|
||||||
|
int freqKhz = 10 * static_cast<int>(_frequency * 100 + 0.25);
|
||||||
|
_commStationForFrequency = flightgear::CommStation::findByFreq( freqKhz, position, NULL );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( false == _commStationForFrequency.valid() ) return;
|
||||||
|
|
||||||
|
_slantDistance_m = dist(_commStationForFrequency->cart(), SGVec3d::fromGeod(position));
|
||||||
|
|
||||||
|
SGGeodesy::inverse(position, _commStationForFrequency->geod(),
|
||||||
|
_trueBearingTo_deg,
|
||||||
|
_trueBearingFrom_deg,
|
||||||
|
_trackDistance_m );
|
||||||
|
|
||||||
|
_heightAboveStation_ft =
|
||||||
|
SGMiscd::max(0.0, position.getElevationFt()
|
||||||
|
- _commStationForFrequency->airport()->elevation());
|
||||||
|
|
||||||
|
_signalQuality_norm = _signalQualityComputer->computeSignalQuality( _slantDistance_m );
|
||||||
|
_stationType = _commStationForFrequency->nameForType( _commStationForFrequency->type() );
|
||||||
|
_stationName = _commStationForFrequency->ident();
|
||||||
|
_airportId = _commStationForFrequency->airport()->getId();
|
||||||
|
|
||||||
|
switch( _commStationForFrequency->type() ) {
|
||||||
|
case FGPositioned::FREQ_ATIS:
|
||||||
|
case FGPositioned::FREQ_AWOS: {
|
||||||
|
if( _signalQuality_norm > 0.01 ) {
|
||||||
|
_metarBridge->requestMetarForId( _airportId );
|
||||||
|
} else {
|
||||||
|
_metarBridge->clearMetar();
|
||||||
|
_atis = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_metarBridge->clearMetar();
|
||||||
|
_atis = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SGSubsystem * CommRadio::createInstance( SGPropertyNode_ptr rootNode )
|
||||||
|
{
|
||||||
|
return new CommRadioImpl( rootNode );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Instrumentation
|
||||||
|
|
49
src/Instrumentation/commradio.hxx
Normal file
49
src/Instrumentation/commradio.hxx
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// commradio.hxx -- class to manage a nav radio instance
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started February 2014
|
||||||
|
//
|
||||||
|
// Copyright (C) Curtis L. Olson - http://www.flightgear.org/~curt
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _FG_INSTRUMENTATION_COMMRADIO_HXX
|
||||||
|
#define _FG_INSTRUMENTATION_COMMRADIO_HXX
|
||||||
|
|
||||||
|
#include <simgear/props/props.hxx>
|
||||||
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
|
|
||||||
|
namespace Instrumentation {
|
||||||
|
|
||||||
|
class SignalQualityComputer : public SGReferenced {
|
||||||
|
public:
|
||||||
|
virtual ~SignalQualityComputer();
|
||||||
|
virtual double computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const = 0;
|
||||||
|
virtual double computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const = 0;
|
||||||
|
virtual double computeSignalQuality( double slantDistanceM ) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef SGSharedPtr<SignalQualityComputer> SignalQualityComputerRef;
|
||||||
|
|
||||||
|
class CommRadio : public SGSubsystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static SGSubsystem * createInstance( SGPropertyNode_ptr rootNode );
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _FG_INSTRUMENTATION_COMMRADIO_HXX
|
47
src/Instrumentation/frequencyformatter.hxx
Normal file
47
src/Instrumentation/frequencyformatter.hxx
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef __FREQUENCY_FORMATTER_HXX
|
||||||
|
#define __FREQUENCY_FORMATTER_HXX
|
||||||
|
|
||||||
|
/* ------------- A NAV/COMM Frequency formatter ---------------------- */
|
||||||
|
|
||||||
|
class FrequencyFormatter : public SGPropertyChangeListener {
|
||||||
|
public:
|
||||||
|
FrequencyFormatter( SGPropertyNode_ptr freqNode, SGPropertyNode_ptr fmtFreqNode, double channelSpacing ) :
|
||||||
|
_freqNode( freqNode ),
|
||||||
|
_fmtFreqNode( fmtFreqNode ),
|
||||||
|
_channelSpacing(channelSpacing)
|
||||||
|
{
|
||||||
|
_freqNode->addChangeListener( this );
|
||||||
|
valueChanged(_freqNode);
|
||||||
|
}
|
||||||
|
~FrequencyFormatter()
|
||||||
|
{
|
||||||
|
_freqNode->removeChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueChanged (SGPropertyNode * prop)
|
||||||
|
{
|
||||||
|
// format as fixed decimal "nnn.nn"
|
||||||
|
std::ostringstream buf;
|
||||||
|
buf << std::fixed
|
||||||
|
<< std::setw(5)
|
||||||
|
<< std::setfill('0')
|
||||||
|
<< std::setprecision(2)
|
||||||
|
<< getFrequency();
|
||||||
|
_fmtFreqNode->setStringValue( buf.str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
double getFrequency() const
|
||||||
|
{
|
||||||
|
double d = SGMiscd::roundToInt(_freqNode->getDoubleValue() / _channelSpacing) * _channelSpacing;
|
||||||
|
// strip last digit, do not round
|
||||||
|
return ((int)(d*100))/100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SGPropertyNode_ptr _freqNode;
|
||||||
|
SGPropertyNode_ptr _fmtFreqNode;
|
||||||
|
double _channelSpacing;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //__FREQUENCY_FORMATTER_HXX
|
|
@ -36,6 +36,7 @@
|
||||||
#include "mag_compass.hxx"
|
#include "mag_compass.hxx"
|
||||||
#include "marker_beacon.hxx"
|
#include "marker_beacon.hxx"
|
||||||
#include "newnavradio.hxx"
|
#include "newnavradio.hxx"
|
||||||
|
#include "commradio.hxx"
|
||||||
#include "slip_skid_ball.hxx"
|
#include "slip_skid_ball.hxx"
|
||||||
#include "transponder.hxx"
|
#include "transponder.hxx"
|
||||||
#include "turn_indicator.hxx"
|
#include "turn_indicator.hxx"
|
||||||
|
@ -169,6 +170,9 @@ bool FGInstrumentMgr::build (SGPropertyNode* config_props)
|
||||||
} else if ( name == "marker-beacon" ) {
|
} else if ( name == "marker-beacon" ) {
|
||||||
set_subsystem( id, new FGMarkerBeacon( node ), 0.2 );
|
set_subsystem( id, new FGMarkerBeacon( node ), 0.2 );
|
||||||
|
|
||||||
|
} else if ( name == "comm-radio" ) {
|
||||||
|
set_subsystem( id, Instrumentation::CommRadio::createInstance( node ) );
|
||||||
|
|
||||||
} else if ( name == "nav-radio" ) {
|
} else if ( name == "nav-radio" ) {
|
||||||
set_subsystem( id, Instrumentation::NavRadio::createInstance( node ) );
|
set_subsystem( id, Instrumentation::NavRadio::createInstance( node ) );
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
#include <Sound/audioident.hxx>
|
#include <Sound/audioident.hxx>
|
||||||
|
|
||||||
#include "navradio.hxx"
|
#include "navradio.hxx"
|
||||||
|
#include "frequencyformatter.hxx"
|
||||||
|
|
||||||
namespace Instrumentation {
|
namespace Instrumentation {
|
||||||
|
|
||||||
|
@ -793,49 +793,6 @@ void GS::display( NavIndicator & navIndicator )
|
||||||
navIndicator.setGS( _glideslopeOffset_norm );
|
navIndicator.setGS( _glideslopeOffset_norm );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------- A NAV/COMM Frequency formatter ---------------------- */
|
|
||||||
|
|
||||||
class FrequencyFormatter : public SGPropertyChangeListener {
|
|
||||||
public:
|
|
||||||
FrequencyFormatter( SGPropertyNode_ptr freqNode, SGPropertyNode_ptr fmtFreqNode, double channelSpacing ) :
|
|
||||||
_freqNode( freqNode ),
|
|
||||||
_fmtFreqNode( fmtFreqNode ),
|
|
||||||
_channelSpacing(channelSpacing)
|
|
||||||
{
|
|
||||||
_freqNode->addChangeListener( this );
|
|
||||||
valueChanged(_freqNode);
|
|
||||||
}
|
|
||||||
~FrequencyFormatter()
|
|
||||||
{
|
|
||||||
_freqNode->removeChangeListener( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
void valueChanged (SGPropertyNode * prop)
|
|
||||||
{
|
|
||||||
// format as fixed decimal "nnn.nn"
|
|
||||||
std::ostringstream buf;
|
|
||||||
buf << std::fixed
|
|
||||||
<< std::setw(5)
|
|
||||||
<< std::setfill('0')
|
|
||||||
<< std::setprecision(2)
|
|
||||||
<< getFrequency();
|
|
||||||
_fmtFreqNode->setStringValue( buf.str() );
|
|
||||||
}
|
|
||||||
|
|
||||||
double getFrequency() const
|
|
||||||
{
|
|
||||||
double d = SGMiscd::roundToInt(_freqNode->getDoubleValue() / _channelSpacing) * _channelSpacing;
|
|
||||||
// strip last digit, do not round
|
|
||||||
return ((int)(d*100))/100.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
SGPropertyNode_ptr _freqNode;
|
|
||||||
SGPropertyNode_ptr _fmtFreqNode;
|
|
||||||
double _channelSpacing;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------- The NavRadio implementation ---------------------- */
|
/* ------------- The NavRadio implementation ---------------------- */
|
||||||
|
|
||||||
class NavRadioImpl : public NavRadio {
|
class NavRadioImpl : public NavRadio {
|
||||||
|
|
|
@ -78,7 +78,6 @@
|
||||||
|
|
||||||
#include <AIModel/AIManager.hxx>
|
#include <AIModel/AIManager.hxx>
|
||||||
|
|
||||||
#include <ATCDCL/ATISmgr.hxx>
|
|
||||||
#include <ATC/atc_mgr.hxx>
|
#include <ATC/atc_mgr.hxx>
|
||||||
|
|
||||||
#include <Autopilot/route_mgr.hxx>
|
#include <Autopilot/route_mgr.hxx>
|
||||||
|
@ -702,16 +701,6 @@ void fgCreateSubsystems(bool duringReset) {
|
||||||
globals->add_subsystem("Canvas", new CanvasMgr, SGSubsystemMgr::DISPLAY);
|
globals->add_subsystem("Canvas", new CanvasMgr, SGSubsystemMgr::DISPLAY);
|
||||||
globals->add_subsystem("CanvasGUI", new GUIMgr, SGSubsystemMgr::DISPLAY);
|
globals->add_subsystem("CanvasGUI", new GUIMgr, SGSubsystemMgr::DISPLAY);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Initialise the ATIS Manager
|
|
||||||
// Note that this is old stuff, but is necessary for the
|
|
||||||
// current ATIS implementation. Therefore, leave it in here
|
|
||||||
// until the ATIS system is ported over to make use of the ATIS
|
|
||||||
// sub system infrastructure.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
globals->add_subsystem("ATIS", new FGATISMgr, SGSubsystemMgr::INIT, 0.4);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Initialize the ATC subsystem
|
// Initialize the ATC subsystem
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
@ -927,8 +916,6 @@ 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 ) {
|
||||||
|
|
|
@ -46,7 +46,6 @@
|
||||||
|
|
||||||
#include <Aircraft/controls.hxx>
|
#include <Aircraft/controls.hxx>
|
||||||
#include <Airports/runways.hxx>
|
#include <Airports/runways.hxx>
|
||||||
#include <ATCDCL/ATISmgr.hxx>
|
|
||||||
#include <Autopilot/route_mgr.hxx>
|
#include <Autopilot/route_mgr.hxx>
|
||||||
#include <GUI/FGFontCache.hxx>
|
#include <GUI/FGFontCache.hxx>
|
||||||
#include <GUI/gui.h>
|
#include <GUI/gui.h>
|
||||||
|
@ -146,7 +145,6 @@ FGGlobals::FGGlobals() :
|
||||||
time_params( NULL ),
|
time_params( NULL ),
|
||||||
ephem( NULL ),
|
ephem( NULL ),
|
||||||
route_mgr( NULL ),
|
route_mgr( NULL ),
|
||||||
ATIS_mgr( NULL ),
|
|
||||||
controls( NULL ),
|
controls( NULL ),
|
||||||
viewmgr( NULL ),
|
viewmgr( NULL ),
|
||||||
commands( SGCommandMgr::instance() ),
|
commands( SGCommandMgr::instance() ),
|
||||||
|
@ -241,7 +239,6 @@ FGGlobals::~FGGlobals()
|
||||||
delete time_params;
|
delete time_params;
|
||||||
set_matlib(NULL);
|
set_matlib(NULL);
|
||||||
delete route_mgr;
|
delete route_mgr;
|
||||||
delete ATIS_mgr;
|
|
||||||
delete channel_options_list;
|
delete channel_options_list;
|
||||||
delete initial_waypoints;
|
delete initial_waypoints;
|
||||||
delete fontcache;
|
delete fontcache;
|
||||||
|
|
|
@ -55,7 +55,6 @@ class SGSubsystemMgr;
|
||||||
class SGSubsystem;
|
class SGSubsystem;
|
||||||
class SGSoundMgr;
|
class SGSoundMgr;
|
||||||
|
|
||||||
class FGATISMgr;
|
|
||||||
class FGControls;
|
class FGControls;
|
||||||
class FGTACANList;
|
class FGTACANList;
|
||||||
class FGLocale;
|
class FGLocale;
|
||||||
|
@ -119,9 +118,6 @@ private:
|
||||||
// Global autopilot "route"
|
// Global autopilot "route"
|
||||||
FGRouteMgr *route_mgr;
|
FGRouteMgr *route_mgr;
|
||||||
|
|
||||||
// ATC manager
|
|
||||||
FGATISMgr *ATIS_mgr;
|
|
||||||
|
|
||||||
// control input state
|
// control input state
|
||||||
FGControls *controls;
|
FGControls *controls;
|
||||||
|
|
||||||
|
@ -271,9 +267,6 @@ public:
|
||||||
inline SGMaterialLib *get_matlib() const { return matlib; }
|
inline SGMaterialLib *get_matlib() const { return matlib; }
|
||||||
void set_matlib( SGMaterialLib *m );
|
void set_matlib( SGMaterialLib *m );
|
||||||
|
|
||||||
inline FGATISMgr *get_ATIS_mgr() const { return ATIS_mgr; }
|
|
||||||
inline void set_ATIS_mgr( FGATISMgr *a ) {ATIS_mgr = a; }
|
|
||||||
|
|
||||||
inline FGControls *get_controls() const { return controls; }
|
inline FGControls *get_controls() const { return controls; }
|
||||||
inline void set_controls( FGControls *c ) { controls = c; }
|
inline void set_controls( FGControls *c ) { controls = c; }
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,9 @@ FGLocale::selectLanguage(const char *language)
|
||||||
// load resource for system messages (translations for fgfs internal messages)
|
// load resource for system messages (translations for fgfs internal messages)
|
||||||
loadResource("sys");
|
loadResource("sys");
|
||||||
|
|
||||||
|
// load resource for atc messages
|
||||||
|
loadResource("atc");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
#include <Cockpit/cockpitDisplayManager.hxx>
|
#include <Cockpit/cockpitDisplayManager.hxx>
|
||||||
#include <GUI/new_gui.hxx>
|
#include <GUI/new_gui.hxx>
|
||||||
#include <Main/logger.hxx>
|
#include <Main/logger.hxx>
|
||||||
#include <ATCDCL/ATISmgr.hxx>
|
|
||||||
#include <ATC/atc_mgr.hxx>
|
#include <ATC/atc_mgr.hxx>
|
||||||
#include <AIModel/AIManager.hxx>
|
#include <AIModel/AIManager.hxx>
|
||||||
#include <MultiPlayer/multiplaymgr.hxx>
|
#include <MultiPlayer/multiplaymgr.hxx>
|
||||||
|
@ -90,7 +89,6 @@ SGSubsystem* createSubsystemByName(const std::string& name)
|
||||||
MAKE_SUB(FGRouteMgr, "route-manager");
|
MAKE_SUB(FGRouteMgr, "route-manager");
|
||||||
MAKE_SUB(FGLogger, "logger");
|
MAKE_SUB(FGLogger, "logger");
|
||||||
MAKE_SUB(NewGUI, "gui");
|
MAKE_SUB(NewGUI, "gui");
|
||||||
MAKE_SUB(FGATISMgr, "atis");
|
|
||||||
MAKE_SUB(FGATCManager, "atc");
|
MAKE_SUB(FGATCManager, "atc");
|
||||||
MAKE_SUB(FGMultiplayMgr, "mp");
|
MAKE_SUB(FGMultiplayMgr, "mp");
|
||||||
MAKE_SUB(FGTrafficManager, "traffic-manager");
|
MAKE_SUB(FGTrafficManager, "traffic-manager");
|
||||||
|
|
Loading…
Add table
Reference in a new issue