--- /dev/null
+/*
+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();
+}
+
--- /dev/null
+/*
+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
atcdialog.cxx
trafficcontrol.cxx
CommStation.cxx
+ ATISEncoder.cxx
+ MetarPropertiesATISInformationProvider.cxx
)
set(HEADERS
atcdialog.hxx
trafficcontrol.hxx
CommStation.hxx
+ ATISEncoder.hxx
+ MetarPropertiesATISInformationProvider.hxx
)
flightgear_component(ATC "${SOURCES}" "${HEADERS}")
--- /dev/null
+/*
+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";
+}
+
--- /dev/null
+/*
+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
include(FlightGearComponent)
set(SOURCES
- ATC.cxx
- atis.cxx
- ATCVoice.cxx
- ATISmgr.cxx
- ATCutils.cxx
ATCProjection.cxx
)
set(HEADERS
- ATC.hxx
- atis.hxx
- ATCVoice.hxx
- ATISmgr.hxx
- ATCutils.hxx
ATCProjection.hxx
- atis_lexicon.hxx
- atis_remap.hxx
)
flightgear_component(ATCDCL "${SOURCES}" "${HEADERS}")
}
//------------------------------------------------------------------------------
-FGRunwayRef FGAirport::findBestRunwayForHeading(double aHeading) const
+FGRunwayRef FGAirport::findBestRunwayForHeading(double aHeading, struct FindBestRunwayForHeadingParams * parms ) const
{
loadRunways();
FGRunway* result = NULL;
double currentBestQuality = 0.0;
- SGPropertyNode *param = fgGetNode("/sim/airport/runways/search", true);
- double lengthWeight = param->getDoubleValue("length-weight", 0.01);
- double widthWeight = param->getDoubleValue("width-weight", 0.01);
- double surfaceWeight = param->getDoubleValue("surface-weight", 10);
- double deviationWeight = param->getDoubleValue("deviation-weight", 1);
+ struct FindBestRunwayForHeadingParams fbrfhp;
+ if( NULL != parms ) fbrfhp = *parms;
+
+ SGPropertyNode_ptr searchNode = fgGetNode("/sim/airport/runways/search");
+ 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) {
FGRunway* rwy = loadById<FGRunway>(id);
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();
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;
if (quality > currentBestQuality) {
bool hasHelipadWithIdent(const std::string& aIdent) const;
FGRunwayRef getRunwayByIdent(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.
#include <simgear/debug/logstream.hxx>
#include <Main/globals.hxx>
#include <Main/fg_props.hxx>
+#include <Main/locale.hxx>
#include <Airports/runways.hxx>
-#include <ATCDCL/ATCutils.hxx>
#include <Navaids/NavDataCache.hxx>
#include "airport.hxx"
if (atisSequenceIndex == -1) {
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)
if (atisSequenceIndex == -1) {
// first computation
atisSequenceTimeStamp = now;
- atisSequenceIndex = rand() % LTRS; // random initial sequence letters
+ atisSequenceIndex = rand() % 26; // random initial sequence letters
return atisSequenceIndex;
}
++steps; // a "special" ATIS update is required
}
- atisSequenceIndex = (atisSequenceIndex + steps) % LTRS;
+ atisSequenceIndex = (atisSequenceIndex + steps) % 26;
// return a huge value if no update occurred
- return (atisSequenceIndex + (steps ? 0 : LTRS*1000));
+ return (atisSequenceIndex + (steps ? 0 : 26*1000));
}
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;
if (_surface_code == 12 || _surface_code == 5) // dry lakebed & gravel
surface = 2;
else if (_surface_code == 1 || _surface_code == 2) // asphalt & concrete
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
* score this runway according to the specified weights. Used by
* 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
terrainsampler.cxx
presets.cxx
gravity.cxx
- magvarmanager.cxx
+ magvarmanager.cxx
)
set(HEADERS
terrainsampler.hxx
presets.hxx
gravity.hxx
- magvarmanager.hxx
+ magvarmanager.hxx
)
flightgear_component(Environment "${SOURCES}" "${HEADERS}")
mrg.cxx
navradio.cxx
newnavradio.cxx
+ commradio.cxx
rad_alt.cxx
rnav_waypt_controller.cxx
slip_skid_ball.cxx
mrg.hxx
navradio.hxx
newnavradio.hxx
+ commradio.hxx
rad_alt.hxx
rnav_waypt_controller.hxx
slip_skid_ball.hxx
--- /dev/null
+// 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
+
--- /dev/null
+// 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
--- /dev/null
+#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
#include "mag_compass.hxx"
#include "marker_beacon.hxx"
#include "newnavradio.hxx"
+#include "commradio.hxx"
#include "slip_skid_ball.hxx"
#include "transponder.hxx"
#include "turn_indicator.hxx"
} else if ( name == "marker-beacon" ) {
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" ) {
set_subsystem( id, Instrumentation::NavRadio::createInstance( node ) );
#include <Sound/audioident.hxx>
#include "navradio.hxx"
-
+#include "frequencyformatter.hxx"
namespace Instrumentation {
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 ---------------------- */
class NavRadioImpl : public NavRadio {
#include <AIModel/AIManager.hxx>
-#include <ATCDCL/ATISmgr.hxx>
#include <ATC/atc_mgr.hxx>
#include <Autopilot/route_mgr.hxx>
globals->add_subsystem("Canvas", new CanvasMgr, 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
////////////////////////////////////////////////////////////////////
globals->get_subsystem("systems")->reinit();
globals->get_subsystem("instrumentation")->reinit();
- globals->get_subsystem("ATIS")->reinit();
-
// setup state to end re-init
fgSetBool("/sim/signals/reinit", false);
if ( !freeze ) {
#include <Aircraft/controls.hxx>
#include <Airports/runways.hxx>
-#include <ATCDCL/ATISmgr.hxx>
#include <Autopilot/route_mgr.hxx>
#include <GUI/FGFontCache.hxx>
#include <GUI/gui.h>
time_params( NULL ),
ephem( NULL ),
route_mgr( NULL ),
- ATIS_mgr( NULL ),
controls( NULL ),
viewmgr( NULL ),
commands( SGCommandMgr::instance() ),
delete time_params;
set_matlib(NULL);
delete route_mgr;
- delete ATIS_mgr;
delete channel_options_list;
delete initial_waypoints;
delete fontcache;
class SGSubsystem;
class SGSoundMgr;
-class FGATISMgr;
class FGControls;
class FGTACANList;
class FGLocale;
// Global autopilot "route"
FGRouteMgr *route_mgr;
- // ATC manager
- FGATISMgr *ATIS_mgr;
-
// control input state
FGControls *controls;
inline SGMaterialLib *get_matlib() const { return matlib; }
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 void set_controls( FGControls *c ) { controls = c; }
// load resource for system messages (translations for fgfs internal messages)
loadResource("sys");
+ // load resource for atc messages
+ loadResource("atc");
+
return true;
}
#include <Cockpit/cockpitDisplayManager.hxx>
#include <GUI/new_gui.hxx>
#include <Main/logger.hxx>
-#include <ATCDCL/ATISmgr.hxx>
#include <ATC/atc_mgr.hxx>
#include <AIModel/AIManager.hxx>
#include <MultiPlayer/multiplaymgr.hxx>
MAKE_SUB(FGRouteMgr, "route-manager");
MAKE_SUB(FGLogger, "logger");
MAKE_SUB(NewGUI, "gui");
- MAKE_SUB(FGATISMgr, "atis");
MAKE_SUB(FGATCManager, "atc");
MAKE_SUB(FGMultiplayMgr, "mp");
MAKE_SUB(FGTrafficManager, "traffic-manager");